From 7d1db4e88387bd87037888cb2f8d6d063bcfdd19 Mon Sep 17 00:00:00 2001 From: serkixenos <70147861+serkixenos@users.noreply.github.com> Date: Tue, 6 Oct 2020 14:49:42 +0200 Subject: [PATCH 01/15] Merge develop into beatrice (#386) * 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 commit 499e3181990d7b732459a38263a6039703c94720. * 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 commit 8676940a281604688771e96ceb1e65a35d98e8e5. * 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 commit 8676940a281604688771e96ceb1e65a35d98e8e5. * 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 * 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 * 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 * 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 * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * 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: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac 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 * 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 commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru 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 * 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 * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * 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: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac 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 * 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 commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru 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 * [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 * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * 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: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac 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 * 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 commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru 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 * 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 * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow * Fix #include * 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: commit a688bb93ed4e16232a907aa8c76e240c83c771bf Author: obucinac 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 * 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 commit 6e61d6b055eb276757e426245a3a7c23a61b3854 Author: satyakoneru 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 Co-authored-by: gladcow * [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 Co-authored-by: satyakoneru * 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 * [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 * [SON-311] Add try_create_son call without explicit deposit params (#324) Co-authored-by: gladcow * 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 * [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 * 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 Co-authored-by: Srdjan Obucina * [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 commit d3385b28cb75bbf43ff6fbcccee404d2183ff8a7. * 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 * Hotfix - fix threshold_weight calculation in redeem scripts * fix broken peerplays_sidechain tests (#357) Co-authored-by: gladcow * 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 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 Co-authored-by: Roshan Syed Co-authored-by: Satyanarayana Koneru 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 * 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 Co-authored-by: Bobinson K B Co-authored-by: obucinac Co-authored-by: satyakoneru Co-authored-by: gladcow Co-authored-by: gladcow Co-authored-by: Alfredo Garcia Co-authored-by: Sandip Patel Co-authored-by: Roshan Syed Co-authored-by: pbattu123 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 Co-authored-by: Satyanarayana Koneru Co-authored-by: blockc p Co-authored-by: Nathan Hourt --- .clang-format | 127 ++ .gitignore | 1 + .gitlab-ci.yml | 22 +- CMakeDoxyfile.in | 279 +++ CMakeLists.txt | 2 +- Dockerfile | 4 +- README.md | 7 +- libraries/app/CMakeLists.txt | 2 +- libraries/app/api.cpp | 12 +- libraries/app/application.cpp | 2 +- libraries/app/database_api.cpp | 280 ++- .../app/include/graphene/app/application.hpp | 6 + .../app/include/graphene/app/database_api.hpp | 126 ++ libraries/chain/CMakeLists.txt | 12 + libraries/chain/account_evaluator.cpp | 2 + libraries/chain/account_role_evaluator.cpp | 162 ++ libraries/chain/asset_evaluator.cpp | 3 + .../custom_account_authority_evaluator.cpp | 28 +- libraries/chain/db_block.cpp | 29 +- libraries/chain/db_getter.cpp | 125 ++ libraries/chain/db_init.cpp | 75 + libraries/chain/db_maint.cpp | 594 ++++- libraries/chain/db_notify.cpp | 134 +- libraries/chain/db_update.cpp | 46 + libraries/chain/db_witness_schedule.cpp | 143 ++ libraries/chain/get_config.cpp | 5 + libraries/chain/hardfork.d/5050-1.hf | 2 +- libraries/chain/hardfork.d/CORE_210.hf | 6 + libraries/chain/hardfork.d/SON.hf | 7 + .../graphene/chain/account_role_evaluator.hpp | 35 + .../graphene/chain/account_role_object.hpp | 49 + .../graphene/chain/budget_record_object.hpp | 2 + .../chain/include/graphene/chain/config.hpp | 15 +- .../chain/include/graphene/chain/database.hpp | 38 + .../graphene/chain/global_property_object.hpp | 8 + .../chain/immutable_chain_parameters.hpp | 2 + .../chain/include/graphene/chain/impacted.hpp | 15 +- .../include/graphene/chain/nft_object.hpp | 4 +- .../graphene/chain/proposal_evaluator.hpp | 16 + .../graphene/chain/protocol/account.hpp | 3 + .../graphene/chain/protocol/account_role.hpp | 82 + .../chain/protocol/chain_parameters.hpp | 68 +- .../graphene/chain/protocol/custom.hpp | 3 + .../protocol/custom_account_authority.hpp | 9 +- .../chain/protocol/custom_permission.hpp | 9 +- .../graphene/chain/protocol/nft_ops.hpp | 29 +- .../graphene/chain/protocol/operations.hpp | 36 +- .../chain/protocol/sidechain_address.hpp | 80 + .../chain/protocol/sidechain_transaction.hpp | 88 + .../include/graphene/chain/protocol/son.hpp | 119 + .../graphene/chain/protocol/son_wallet.hpp | 40 + .../chain/protocol/son_wallet_deposit.hpp | 57 + .../chain/protocol/son_wallet_withdraw.hpp | 56 + .../graphene/chain/protocol/transaction.hpp | 14 +- .../include/graphene/chain/protocol/types.hpp | 51 +- .../graphene/chain/protocol/vesting.hpp | 14 +- .../include/graphene/chain/protocol/vote.hpp | 3 +- .../graphene/chain/rbac_hardfork_visitor.hpp | 48 + .../chain/sidechain_address_evaluator.hpp | 34 + .../chain/sidechain_address_object.hpp | 92 + .../include/graphene/chain/sidechain_defs.hpp | 22 + .../chain/sidechain_transaction_evaluator.hpp | 43 + .../chain/sidechain_transaction_object.hpp | 82 + .../include/graphene/chain/son_evaluator.hpp | 61 + .../chain/include/graphene/chain/son_info.hpp | 47 + .../include/graphene/chain/son_object.hpp | 133 ++ .../graphene/chain/son_proposal_object.hpp | 42 + .../chain/son_wallet_deposit_evaluator.hpp | 24 + .../chain/son_wallet_deposit_object.hpp | 69 + .../graphene/chain/son_wallet_evaluator.hpp | 25 + .../graphene/chain/son_wallet_object.hpp | 47 + .../chain/son_wallet_withdraw_evaluator.hpp | 24 + .../chain/son_wallet_withdraw_object.hpp | 68 + .../graphene/chain/vesting_balance_object.hpp | 30 +- .../include/graphene/chain/vote_count.hpp | 11 + .../chain/witness_schedule_object.hpp | 59 + libraries/chain/nft_evaluator.cpp | 24 + libraries/chain/offer_evaluator.cpp | 25 +- libraries/chain/proposal_evaluator.cpp | 88 +- libraries/chain/proposal_object.cpp | 6 +- libraries/chain/protocol/account.cpp | 13 +- libraries/chain/protocol/account_role.cpp | 61 + libraries/chain/protocol/nft.cpp | 6 +- libraries/chain/protocol/operations.cpp | 34 +- libraries/chain/protocol/transaction.cpp | 30 +- .../chain/sidechain_address_evaluator.cpp | 114 + .../chain/sidechain_transaction_evaluator.cpp | 159 ++ libraries/chain/son_evaluator.cpp | 243 +++ libraries/chain/son_object.cpp | 15 + .../chain/son_wallet_deposit_evaluator.cpp | 157 ++ libraries/chain/son_wallet_evaluator.cpp | 81 + .../chain/son_wallet_withdraw_evaluator.cpp | 155 ++ libraries/chain/vesting_balance_evaluator.cpp | 14 +- libraries/chain/vesting_balance_object.cpp | 27 + libraries/fc | 2 +- libraries/plugins/CMakeLists.txt | 1 + .../account_history_plugin.cpp | 8 +- .../delayed_node/delayed_node_plugin.cpp | 2 +- .../elasticsearch/elasticsearch_plugin.cpp | 9 +- .../peerplays_sidechain/CMakeLists.txt | 51 + .../peerplays_sidechain/bitcoin/bech32.cpp | 193 ++ .../bitcoin/bitcoin_address.cpp | 455 ++++ .../bitcoin/bitcoin_script.cpp | 65 + .../bitcoin/bitcoin_transaction.cpp | 257 +++ .../bitcoin/segwit_addr.cpp | 82 + .../bitcoin/sign_bitcoin_transaction.cpp | 153 ++ .../peerplays_sidechain/bitcoin/utils.cpp | 99 + .../peerplays_sidechain/bitcoin/bech32.hpp | 24 + .../bitcoin/bitcoin_address.hpp | 249 +++ .../bitcoin/bitcoin_script.hpp | 80 + .../bitcoin/bitcoin_transaction.hpp | 106 + .../bitcoin/segwit_addr.hpp | 34 + .../peerplays_sidechain/bitcoin/serialize.hpp | 354 +++ .../bitcoin/sign_bitcoin_transaction.hpp | 35 + .../peerplays_sidechain/bitcoin/types.hpp | 54 + .../peerplays_sidechain/bitcoin/utils.hpp | 26 + .../graphene/peerplays_sidechain/defs.hpp | 75 + .../peerplays_sidechain_plugin.hpp | 39 + .../sidechain_net_handler.hpp | 61 + .../sidechain_net_handler_bitcoin.hpp | 132 ++ .../sidechain_net_handler_peerplays.hpp | 29 + .../sidechain_net_manager.hpp | 34 + .../peerplays_sidechain_plugin.cpp | 698 ++++++ .../sidechain_net_handler.cpp | 569 +++++ .../sidechain_net_handler_bitcoin.cpp | 1911 +++++++++++++++++ .../sidechain_net_handler_peerplays.cpp | 306 +++ .../sidechain_net_manager.cpp | 90 + .../wallet/include/graphene/wallet/wallet.hpp | 310 ++- libraries/wallet/wallet.cpp | 632 +++++- programs/cli_wallet/main.cpp | 12 +- programs/delayed_node/main.cpp | 13 + programs/js_operation_serializer/main.cpp | 7 + programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/genesis.json | 12 +- programs/witness_node/main.cpp | 55 +- tests/CMakeLists.txt | 6 +- tests/app/main.cpp | 1 + tests/cli/cli_fixture.cpp | 277 +++ tests/cli/cli_fixture.hpp | 81 + tests/cli/main.cpp | 374 +--- tests/cli/son.cpp | 720 +++++++ tests/common/database_fixture.cpp | 46 +- tests/common/database_fixture.hpp | 18 +- tests/elasticsearch/main.cpp | 2 +- .../bitcoin_address_tests.cpp | 316 +++ .../bitcoin_sign_tests.cpp | 449 ++++ .../bitcoin_transaction_tests.cpp | 166 ++ .../peerplays_sidechain_tests.cpp | 17 + tests/tests/account_role_tests.cpp | 379 ++++ tests/tests/affiliate_tests.cpp | 6 +- tests/tests/authority_tests.cpp | 41 +- tests/tests/block_tests.cpp | 3 +- tests/tests/history_api_tests.cpp | 6 +- tests/tests/sidechain_addresses_test.cpp | 170 ++ tests/tests/son_operations_tests.cpp | 788 +++++++ tests/tests/son_wallet_tests.cpp | 225 ++ 156 files changed, 16137 insertions(+), 681 deletions(-) create mode 100644 .clang-format create mode 100644 CMakeDoxyfile.in mode change 100644 => 100755 libraries/chain/CMakeLists.txt create mode 100644 libraries/chain/account_role_evaluator.cpp create mode 100644 libraries/chain/hardfork.d/CORE_210.hf create mode 100644 libraries/chain/hardfork.d/SON.hf create mode 100644 libraries/chain/include/graphene/chain/account_role_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/account_role_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/account_role.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/son.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp create mode 100644 libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_address_object.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_defs.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp create mode 100644 libraries/chain/include/graphene/chain/son_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_info.hpp create mode 100644 libraries/chain/include/graphene/chain/son_object.hpp create mode 100644 libraries/chain/include/graphene/chain/son_proposal_object.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_object.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp create mode 100644 libraries/chain/protocol/account_role.cpp create mode 100644 libraries/chain/sidechain_address_evaluator.cpp create mode 100644 libraries/chain/sidechain_transaction_evaluator.cpp create mode 100644 libraries/chain/son_evaluator.cpp create mode 100644 libraries/chain/son_object.cpp create mode 100644 libraries/chain/son_wallet_deposit_evaluator.cpp create mode 100644 libraries/chain/son_wallet_evaluator.cpp create mode 100644 libraries/chain/son_wallet_withdraw_evaluator.cpp create mode 100755 libraries/plugins/peerplays_sidechain/CMakeLists.txt create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_script.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_transaction.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bech32.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/serialize.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/types.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp create mode 100644 libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp create mode 100644 tests/cli/cli_fixture.cpp create mode 100644 tests/cli/cli_fixture.hpp create mode 100644 tests/cli/son.cpp create mode 100644 tests/peerplays_sidechain/bitcoin_address_tests.cpp create mode 100644 tests/peerplays_sidechain/bitcoin_sign_tests.cpp create mode 100644 tests/peerplays_sidechain/bitcoin_transaction_tests.cpp create mode 100644 tests/peerplays_sidechain/peerplays_sidechain_tests.cpp create mode 100644 tests/tests/account_role_tests.cpp create mode 100644 tests/tests/sidechain_addresses_test.cpp create mode 100644 tests/tests/son_operations_tests.cpp create mode 100644 tests/tests/son_wallet_tests.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..2fffe7bae --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -3 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 6 +ContinuationIndentWidth: 6 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 3 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 3 +UseTab: Never +... + diff --git a/.gitignore b/.gitignore index 90311de0c..39b231630 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ moc_* hardfork.hpp build_xc data +CMakeDoxyfile.in build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b53c7541d..1403ba24e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,26 +12,30 @@ stages: build: stage: build script: + - rm -rf .git/modules/* ./docs ./libraries/fc - git submodule sync - git submodule update --init --recursive - - cmake . + - rm -rf build + - mkdir build + - cd build + - cmake .. - make -j$(nproc) artifacts: untracked: true paths: - - libraries/ - - programs/ - - tests/ + - build/libraries/ + - build/programs/ + - build/tests/ tags: - builder - + test: stage: test - dependencies: + dependencies: - build script: - - ./tests/betting_test - - ./tests/chain_test - - ./tests/cli_test + - ./build/tests/betting_test --log_level=message + - ./build/tests/chain_test --log_level=message + - ./build/tests/cli_test --log_level=message tags: - builder diff --git a/CMakeDoxyfile.in b/CMakeDoxyfile.in new file mode 100644 index 000000000..b0ed02fb1 --- /dev/null +++ b/CMakeDoxyfile.in @@ -0,0 +1,279 @@ +# +# DO NOT EDIT! THIS FILE WAS GENERATED BY CMAKE! +# + +DOXYFILE_ENCODING = @DOXYGEN_DOXYFILE_ENCODING@ +PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ +PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ +PROJECT_BRIEF = @DOXYGEN_PROJECT_BRIEF@ +PROJECT_LOGO = @DOXYGEN_PROJECT_LOGO@ +OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ +CREATE_SUBDIRS = @DOXYGEN_CREATE_SUBDIRS@ +ALLOW_UNICODE_NAMES = @DOXYGEN_ALLOW_UNICODE_NAMES@ +OUTPUT_LANGUAGE = @DOXYGEN_OUTPUT_LANGUAGE@ +OUTPUT_TEXT_DIRECTION = @DOXYGEN_OUTPUT_TEXT_DIRECTION@ +BRIEF_MEMBER_DESC = @DOXYGEN_BRIEF_MEMBER_DESC@ +REPEAT_BRIEF = @DOXYGEN_REPEAT_BRIEF@ +ABBREVIATE_BRIEF = @DOXYGEN_ABBREVIATE_BRIEF@ +ALWAYS_DETAILED_SEC = @DOXYGEN_ALWAYS_DETAILED_SEC@ +INLINE_INHERITED_MEMB = @DOXYGEN_INLINE_INHERITED_MEMB@ +FULL_PATH_NAMES = @DOXYGEN_FULL_PATH_NAMES@ +STRIP_FROM_PATH = @DOXYGEN_STRIP_FROM_PATH@ +STRIP_FROM_INC_PATH = @DOXYGEN_STRIP_FROM_INC_PATH@ +SHORT_NAMES = @DOXYGEN_SHORT_NAMES@ +JAVADOC_AUTOBRIEF = @DOXYGEN_JAVADOC_AUTOBRIEF@ +JAVADOC_BANNER = @DOXYGEN_JAVADOC_BANNER@ +QT_AUTOBRIEF = @DOXYGEN_QT_AUTOBRIEF@ +MULTILINE_CPP_IS_BRIEF = @DOXYGEN_MULTILINE_CPP_IS_BRIEF@ +INHERIT_DOCS = @DOXYGEN_INHERIT_DOCS@ +SEPARATE_MEMBER_PAGES = @DOXYGEN_SEPARATE_MEMBER_PAGES@ +TAB_SIZE = @DOXYGEN_TAB_SIZE@ +ALIASES = @DOXYGEN_ALIASES@ +TCL_SUBST = @DOXYGEN_TCL_SUBST@ +OPTIMIZE_OUTPUT_FOR_C = @DOXYGEN_OPTIMIZE_OUTPUT_FOR_C@ +OPTIMIZE_OUTPUT_JAVA = @DOXYGEN_OPTIMIZE_OUTPUT_JAVA@ +OPTIMIZE_FOR_FORTRAN = @DOXYGEN_OPTIMIZE_FOR_FORTRAN@ +OPTIMIZE_OUTPUT_VHDL = @DOXYGEN_OPTIMIZE_OUTPUT_VHDL@ +OPTIMIZE_OUTPUT_SLICE = @DOXYGEN_OPTIMIZE_OUTPUT_SLICE@ +EXTENSION_MAPPING = @DOXYGEN_EXTENSION_MAPPING@ +MARKDOWN_SUPPORT = @DOXYGEN_MARKDOWN_SUPPORT@ +TOC_INCLUDE_HEADINGS = @DOXYGEN_TOC_INCLUDE_HEADINGS@ +AUTOLINK_SUPPORT = @DOXYGEN_AUTOLINK_SUPPORT@ +BUILTIN_STL_SUPPORT = @DOXYGEN_BUILTIN_STL_SUPPORT@ +CPP_CLI_SUPPORT = @DOXYGEN_CPP_CLI_SUPPORT@ +SIP_SUPPORT = @DOXYGEN_SIP_SUPPORT@ +IDL_PROPERTY_SUPPORT = @DOXYGEN_IDL_PROPERTY_SUPPORT@ +DISTRIBUTE_GROUP_DOC = @DOXYGEN_DISTRIBUTE_GROUP_DOC@ +GROUP_NESTED_COMPOUNDS = @DOXYGEN_GROUP_NESTED_COMPOUNDS@ +SUBGROUPING = @DOXYGEN_SUBGROUPING@ +INLINE_GROUPED_CLASSES = @DOXYGEN_INLINE_GROUPED_CLASSES@ +INLINE_SIMPLE_STRUCTS = @DOXYGEN_INLINE_SIMPLE_STRUCTS@ +TYPEDEF_HIDES_STRUCT = @DOXYGEN_TYPEDEF_HIDES_STRUCT@ +LOOKUP_CACHE_SIZE = @DOXYGEN_LOOKUP_CACHE_SIZE@ +EXTRACT_ALL = @DOXYGEN_EXTRACT_ALL@ +EXTRACT_PRIVATE = @DOXYGEN_EXTRACT_PRIVATE@ +EXTRACT_PRIV_VIRTUAL = @DOXYGEN_EXTRACT_PRIV_VIRTUAL@ +EXTRACT_PACKAGE = @DOXYGEN_EXTRACT_PACKAGE@ +EXTRACT_STATIC = @DOXYGEN_EXTRACT_STATIC@ +EXTRACT_LOCAL_CLASSES = @DOXYGEN_EXTRACT_LOCAL_CLASSES@ +EXTRACT_LOCAL_METHODS = @DOXYGEN_EXTRACT_LOCAL_METHODS@ +EXTRACT_ANON_NSPACES = @DOXYGEN_EXTRACT_ANON_NSPACES@ +HIDE_UNDOC_MEMBERS = @DOXYGEN_HIDE_UNDOC_MEMBERS@ +HIDE_UNDOC_CLASSES = @DOXYGEN_HIDE_UNDOC_CLASSES@ +HIDE_FRIEND_COMPOUNDS = @DOXYGEN_HIDE_FRIEND_COMPOUNDS@ +HIDE_IN_BODY_DOCS = @DOXYGEN_HIDE_IN_BODY_DOCS@ +INTERNAL_DOCS = @DOXYGEN_INTERNAL_DOCS@ +CASE_SENSE_NAMES = @DOXYGEN_CASE_SENSE_NAMES@ +HIDE_SCOPE_NAMES = @DOXYGEN_HIDE_SCOPE_NAMES@ +HIDE_COMPOUND_REFERENCE= @DOXYGEN_HIDE_COMPOUND_REFERENCE@ +SHOW_INCLUDE_FILES = @DOXYGEN_SHOW_INCLUDE_FILES@ +SHOW_GROUPED_MEMB_INC = @DOXYGEN_SHOW_GROUPED_MEMB_INC@ +FORCE_LOCAL_INCLUDES = @DOXYGEN_FORCE_LOCAL_INCLUDES@ +INLINE_INFO = @DOXYGEN_INLINE_INFO@ +SORT_MEMBER_DOCS = @DOXYGEN_SORT_MEMBER_DOCS@ +SORT_BRIEF_DOCS = @DOXYGEN_SORT_BRIEF_DOCS@ +SORT_MEMBERS_CTORS_1ST = @DOXYGEN_SORT_MEMBERS_CTORS_1ST@ +SORT_GROUP_NAMES = @DOXYGEN_SORT_GROUP_NAMES@ +SORT_BY_SCOPE_NAME = @DOXYGEN_SORT_BY_SCOPE_NAME@ +STRICT_PROTO_MATCHING = @DOXYGEN_STRICT_PROTO_MATCHING@ +GENERATE_TODOLIST = @DOXYGEN_GENERATE_TODOLIST@ +GENERATE_TESTLIST = @DOXYGEN_GENERATE_TESTLIST@ +GENERATE_BUGLIST = @DOXYGEN_GENERATE_BUGLIST@ +GENERATE_DEPRECATEDLIST= @DOXYGEN_GENERATE_DEPRECATEDLIST@ +ENABLED_SECTIONS = @DOXYGEN_ENABLED_SECTIONS@ +MAX_INITIALIZER_LINES = @DOXYGEN_MAX_INITIALIZER_LINES@ +SHOW_USED_FILES = @DOXYGEN_SHOW_USED_FILES@ +SHOW_FILES = @DOXYGEN_SHOW_FILES@ +SHOW_NAMESPACES = @DOXYGEN_SHOW_NAMESPACES@ +FILE_VERSION_FILTER = @DOXYGEN_FILE_VERSION_FILTER@ +LAYOUT_FILE = @DOXYGEN_LAYOUT_FILE@ +CITE_BIB_FILES = @DOXYGEN_CITE_BIB_FILES@ +QUIET = @DOXYGEN_QUIET@ +WARNINGS = @DOXYGEN_WARNINGS@ +WARN_IF_UNDOCUMENTED = @DOXYGEN_WARN_IF_UNDOCUMENTED@ +WARN_IF_DOC_ERROR = @DOXYGEN_WARN_IF_DOC_ERROR@ +WARN_NO_PARAMDOC = @DOXYGEN_WARN_NO_PARAMDOC@ +WARN_AS_ERROR = @DOXYGEN_WARN_AS_ERROR@ +WARN_FORMAT = @DOXYGEN_WARN_FORMAT@ +WARN_LOGFILE = @DOXYGEN_WARN_LOGFILE@ +INPUT = @DOXYGEN_INPUT@ +INPUT_ENCODING = @DOXYGEN_INPUT_ENCODING@ +FILE_PATTERNS = @DOXYGEN_FILE_PATTERNS@ +RECURSIVE = @DOXYGEN_RECURSIVE@ +EXCLUDE = @DOXYGEN_EXCLUDE@ +EXCLUDE_SYMLINKS = @DOXYGEN_EXCLUDE_SYMLINKS@ +EXCLUDE_PATTERNS = @DOXYGEN_EXCLUDE_PATTERNS@ +EXCLUDE_SYMBOLS = @DOXYGEN_EXCLUDE_SYMBOLS@ +EXAMPLE_PATH = @DOXYGEN_EXAMPLE_PATH@ +EXAMPLE_PATTERNS = @DOXYGEN_EXAMPLE_PATTERNS@ +EXAMPLE_RECURSIVE = @DOXYGEN_EXAMPLE_RECURSIVE@ +IMAGE_PATH = @DOXYGEN_IMAGE_PATH@ +INPUT_FILTER = @DOXYGEN_INPUT_FILTER@ +FILTER_PATTERNS = @DOXYGEN_FILTER_PATTERNS@ +FILTER_SOURCE_FILES = @DOXYGEN_FILTER_SOURCE_FILES@ +FILTER_SOURCE_PATTERNS = @DOXYGEN_FILTER_SOURCE_PATTERNS@ +USE_MDFILE_AS_MAINPAGE = @DOXYGEN_USE_MDFILE_AS_MAINPAGE@ +SOURCE_BROWSER = @DOXYGEN_SOURCE_BROWSER@ +INLINE_SOURCES = @DOXYGEN_INLINE_SOURCES@ +STRIP_CODE_COMMENTS = @DOXYGEN_STRIP_CODE_COMMENTS@ +REFERENCED_BY_RELATION = @DOXYGEN_REFERENCED_BY_RELATION@ +REFERENCES_RELATION = @DOXYGEN_REFERENCES_RELATION@ +REFERENCES_LINK_SOURCE = @DOXYGEN_REFERENCES_LINK_SOURCE@ +SOURCE_TOOLTIPS = @DOXYGEN_SOURCE_TOOLTIPS@ +USE_HTAGS = @DOXYGEN_USE_HTAGS@ +VERBATIM_HEADERS = @DOXYGEN_VERBATIM_HEADERS@ +CLANG_ASSISTED_PARSING = @DOXYGEN_CLANG_ASSISTED_PARSING@ +CLANG_OPTIONS = @DOXYGEN_CLANG_OPTIONS@ +CLANG_DATABASE_PATH = @DOXYGEN_CLANG_DATABASE_PATH@ +ALPHABETICAL_INDEX = @DOXYGEN_ALPHABETICAL_INDEX@ +COLS_IN_ALPHA_INDEX = @DOXYGEN_COLS_IN_ALPHA_INDEX@ +IGNORE_PREFIX = @DOXYGEN_IGNORE_PREFIX@ +GENERATE_HTML = @DOXYGEN_GENERATE_HTML@ +HTML_OUTPUT = @DOXYGEN_HTML_OUTPUT@ +HTML_FILE_EXTENSION = @DOXYGEN_HTML_FILE_EXTENSION@ +HTML_HEADER = @DOXYGEN_HTML_HEADER@ +HTML_FOOTER = @DOXYGEN_HTML_FOOTER@ +HTML_STYLESHEET = @DOXYGEN_HTML_STYLESHEET@ +HTML_EXTRA_STYLESHEET = @DOXYGEN_HTML_EXTRA_STYLESHEET@ +HTML_EXTRA_FILES = @DOXYGEN_HTML_EXTRA_FILES@ +HTML_COLORSTYLE_HUE = @DOXYGEN_HTML_COLORSTYLE_HUE@ +HTML_COLORSTYLE_SAT = @DOXYGEN_HTML_COLORSTYLE_SAT@ +HTML_COLORSTYLE_GAMMA = @DOXYGEN_HTML_COLORSTYLE_GAMMA@ +HTML_TIMESTAMP = @DOXYGEN_HTML_TIMESTAMP@ +HTML_DYNAMIC_MENUS = @DOXYGEN_HTML_DYNAMIC_MENUS@ +HTML_DYNAMIC_SECTIONS = @DOXYGEN_HTML_DYNAMIC_SECTIONS@ +HTML_INDEX_NUM_ENTRIES = @DOXYGEN_HTML_INDEX_NUM_ENTRIES@ +GENERATE_DOCSET = @DOXYGEN_GENERATE_DOCSET@ +DOCSET_FEEDNAME = @DOXYGEN_DOCSET_FEEDNAME@ +DOCSET_BUNDLE_ID = @DOXYGEN_DOCSET_BUNDLE_ID@ +DOCSET_PUBLISHER_ID = @DOXYGEN_DOCSET_PUBLISHER_ID@ +DOCSET_PUBLISHER_NAME = @DOXYGEN_DOCSET_PUBLISHER_NAME@ +GENERATE_HTMLHELP = @DOXYGEN_GENERATE_HTMLHELP@ +CHM_FILE = @DOXYGEN_CHM_FILE@ +HHC_LOCATION = @DOXYGEN_HHC_LOCATION@ +GENERATE_CHI = @DOXYGEN_GENERATE_CHI@ +CHM_INDEX_ENCODING = @DOXYGEN_CHM_INDEX_ENCODING@ +BINARY_TOC = @DOXYGEN_BINARY_TOC@ +TOC_EXPAND = @DOXYGEN_TOC_EXPAND@ +GENERATE_QHP = @DOXYGEN_GENERATE_QHP@ +QCH_FILE = @DOXYGEN_QCH_FILE@ +QHP_NAMESPACE = @DOXYGEN_QHP_NAMESPACE@ +QHP_VIRTUAL_FOLDER = @DOXYGEN_QHP_VIRTUAL_FOLDER@ +QHP_CUST_FILTER_NAME = @DOXYGEN_QHP_CUST_FILTER_NAME@ +QHP_CUST_FILTER_ATTRS = @DOXYGEN_QHP_CUST_FILTER_ATTRS@ +QHP_SECT_FILTER_ATTRS = @DOXYGEN_QHP_SECT_FILTER_ATTRS@ +QHG_LOCATION = @DOXYGEN_QHG_LOCATION@ +GENERATE_ECLIPSEHELP = @DOXYGEN_GENERATE_ECLIPSEHELP@ +ECLIPSE_DOC_ID = @DOXYGEN_ECLIPSE_DOC_ID@ +DISABLE_INDEX = @DOXYGEN_DISABLE_INDEX@ +GENERATE_TREEVIEW = @DOXYGEN_GENERATE_TREEVIEW@ +ENUM_VALUES_PER_LINE = @DOXYGEN_ENUM_VALUES_PER_LINE@ +TREEVIEW_WIDTH = @DOXYGEN_TREEVIEW_WIDTH@ +EXT_LINKS_IN_WINDOW = @DOXYGEN_EXT_LINKS_IN_WINDOW@ +FORMULA_FONTSIZE = @DOXYGEN_FORMULA_FONTSIZE@ +FORMULA_TRANSPARENT = @DOXYGEN_FORMULA_TRANSPARENT@ +USE_MATHJAX = @DOXYGEN_USE_MATHJAX@ +MATHJAX_FORMAT = @DOXYGEN_MATHJAX_FORMAT@ +MATHJAX_RELPATH = @DOXYGEN_MATHJAX_RELPATH@ +MATHJAX_EXTENSIONS = @DOXYGEN_MATHJAX_EXTENSIONS@ +MATHJAX_CODEFILE = @DOXYGEN_MATHJAX_CODEFILE@ +SEARCHENGINE = @DOXYGEN_SEARCHENGINE@ +SERVER_BASED_SEARCH = @DOXYGEN_SERVER_BASED_SEARCH@ +EXTERNAL_SEARCH = @DOXYGEN_EXTERNAL_SEARCH@ +SEARCHENGINE_URL = @DOXYGEN_SEARCHENGINE_URL@ +SEARCHDATA_FILE = @DOXYGEN_SEARCHDATA_FILE@ +EXTERNAL_SEARCH_ID = @DOXYGEN_EXTERNAL_SEARCH_ID@ +EXTRA_SEARCH_MAPPINGS = @DOXYGEN_EXTRA_SEARCH_MAPPINGS@ +GENERATE_LATEX = @DOXYGEN_GENERATE_LATEX@ +LATEX_OUTPUT = @DOXYGEN_LATEX_OUTPUT@ +LATEX_CMD_NAME = @DOXYGEN_LATEX_CMD_NAME@ +MAKEINDEX_CMD_NAME = @DOXYGEN_MAKEINDEX_CMD_NAME@ +LATEX_MAKEINDEX_CMD = @DOXYGEN_LATEX_MAKEINDEX_CMD@ +COMPACT_LATEX = @DOXYGEN_COMPACT_LATEX@ +PAPER_TYPE = @DOXYGEN_PAPER_TYPE@ +EXTRA_PACKAGES = @DOXYGEN_EXTRA_PACKAGES@ +LATEX_HEADER = @DOXYGEN_LATEX_HEADER@ +LATEX_FOOTER = @DOXYGEN_LATEX_FOOTER@ +LATEX_EXTRA_STYLESHEET = @DOXYGEN_LATEX_EXTRA_STYLESHEET@ +LATEX_EXTRA_FILES = @DOXYGEN_LATEX_EXTRA_FILES@ +PDF_HYPERLINKS = @DOXYGEN_PDF_HYPERLINKS@ +USE_PDFLATEX = @DOXYGEN_USE_PDFLATEX@ +LATEX_BATCHMODE = @DOXYGEN_LATEX_BATCHMODE@ +LATEX_HIDE_INDICES = @DOXYGEN_LATEX_HIDE_INDICES@ +LATEX_SOURCE_CODE = @DOXYGEN_LATEX_SOURCE_CODE@ +LATEX_BIB_STYLE = @DOXYGEN_LATEX_BIB_STYLE@ +LATEX_TIMESTAMP = @DOXYGEN_LATEX_TIMESTAMP@ +LATEX_EMOJI_DIRECTORY = @DOXYGEN_LATEX_EMOJI_DIRECTORY@ +GENERATE_RTF = @DOXYGEN_GENERATE_RTF@ +RTF_OUTPUT = @DOXYGEN_RTF_OUTPUT@ +COMPACT_RTF = @DOXYGEN_COMPACT_RTF@ +RTF_HYPERLINKS = @DOXYGEN_RTF_HYPERLINKS@ +RTF_STYLESHEET_FILE = @DOXYGEN_RTF_STYLESHEET_FILE@ +RTF_EXTENSIONS_FILE = @DOXYGEN_RTF_EXTENSIONS_FILE@ +RTF_SOURCE_CODE = @DOXYGEN_RTF_SOURCE_CODE@ +GENERATE_MAN = @DOXYGEN_GENERATE_MAN@ +MAN_OUTPUT = @DOXYGEN_MAN_OUTPUT@ +MAN_EXTENSION = @DOXYGEN_MAN_EXTENSION@ +MAN_SUBDIR = @DOXYGEN_MAN_SUBDIR@ +MAN_LINKS = @DOXYGEN_MAN_LINKS@ +GENERATE_XML = @DOXYGEN_GENERATE_XML@ +XML_OUTPUT = @DOXYGEN_XML_OUTPUT@ +XML_PROGRAMLISTING = @DOXYGEN_XML_PROGRAMLISTING@ +XML_NS_MEMB_FILE_SCOPE = @DOXYGEN_XML_NS_MEMB_FILE_SCOPE@ +GENERATE_DOCBOOK = @DOXYGEN_GENERATE_DOCBOOK@ +DOCBOOK_OUTPUT = @DOXYGEN_DOCBOOK_OUTPUT@ +DOCBOOK_PROGRAMLISTING = @DOXYGEN_DOCBOOK_PROGRAMLISTING@ +GENERATE_AUTOGEN_DEF = @DOXYGEN_GENERATE_AUTOGEN_DEF@ +GENERATE_PERLMOD = @DOXYGEN_GENERATE_PERLMOD@ +PERLMOD_LATEX = @DOXYGEN_PERLMOD_LATEX@ +PERLMOD_PRETTY = @DOXYGEN_PERLMOD_PRETTY@ +PERLMOD_MAKEVAR_PREFIX = @DOXYGEN_PERLMOD_MAKEVAR_PREFIX@ +ENABLE_PREPROCESSING = @DOXYGEN_ENABLE_PREPROCESSING@ +MACRO_EXPANSION = @DOXYGEN_MACRO_EXPANSION@ +EXPAND_ONLY_PREDEF = @DOXYGEN_EXPAND_ONLY_PREDEF@ +SEARCH_INCLUDES = @DOXYGEN_SEARCH_INCLUDES@ +INCLUDE_PATH = @DOXYGEN_INCLUDE_PATH@ +INCLUDE_FILE_PATTERNS = @DOXYGEN_INCLUDE_FILE_PATTERNS@ +PREDEFINED = @DOXYGEN_PREDEFINED@ +EXPAND_AS_DEFINED = @DOXYGEN_EXPAND_AS_DEFINED@ +SKIP_FUNCTION_MACROS = @DOXYGEN_SKIP_FUNCTION_MACROS@ +TAGFILES = @DOXYGEN_TAGFILES@ +GENERATE_TAGFILE = @DOXYGEN_GENERATE_TAGFILE@ +ALLEXTERNALS = @DOXYGEN_ALLEXTERNALS@ +EXTERNAL_GROUPS = @DOXYGEN_EXTERNAL_GROUPS@ +EXTERNAL_PAGES = @DOXYGEN_EXTERNAL_PAGES@ +CLASS_DIAGRAMS = @DOXYGEN_CLASS_DIAGRAMS@ +DIA_PATH = @DOXYGEN_DIA_PATH@ +HIDE_UNDOC_RELATIONS = @DOXYGEN_HIDE_UNDOC_RELATIONS@ +HAVE_DOT = @DOXYGEN_HAVE_DOT@ +DOT_NUM_THREADS = @DOXYGEN_DOT_NUM_THREADS@ +DOT_FONTNAME = @DOXYGEN_DOT_FONTNAME@ +DOT_FONTSIZE = @DOXYGEN_DOT_FONTSIZE@ +DOT_FONTPATH = @DOXYGEN_DOT_FONTPATH@ +CLASS_GRAPH = @DOXYGEN_CLASS_GRAPH@ +COLLABORATION_GRAPH = @DOXYGEN_COLLABORATION_GRAPH@ +GROUP_GRAPHS = @DOXYGEN_GROUP_GRAPHS@ +UML_LOOK = @DOXYGEN_UML_LOOK@ +UML_LIMIT_NUM_FIELDS = @DOXYGEN_UML_LIMIT_NUM_FIELDS@ +TEMPLATE_RELATIONS = @DOXYGEN_TEMPLATE_RELATIONS@ +INCLUDE_GRAPH = @DOXYGEN_INCLUDE_GRAPH@ +INCLUDED_BY_GRAPH = @DOXYGEN_INCLUDED_BY_GRAPH@ +CALL_GRAPH = @DOXYGEN_CALL_GRAPH@ +CALLER_GRAPH = @DOXYGEN_CALLER_GRAPH@ +GRAPHICAL_HIERARCHY = @DOXYGEN_GRAPHICAL_HIERARCHY@ +DIRECTORY_GRAPH = @DOXYGEN_DIRECTORY_GRAPH@ +DOT_IMAGE_FORMAT = @DOXYGEN_DOT_IMAGE_FORMAT@ +INTERACTIVE_SVG = @DOXYGEN_INTERACTIVE_SVG@ +DOT_PATH = @DOXYGEN_DOT_PATH@ +DOTFILE_DIRS = @DOXYGEN_DOTFILE_DIRS@ +MSCFILE_DIRS = @DOXYGEN_MSCFILE_DIRS@ +DIAFILE_DIRS = @DOXYGEN_DIAFILE_DIRS@ +PLANTUML_JAR_PATH = @DOXYGEN_PLANTUML_JAR_PATH@ +PLANTUML_CFG_FILE = @DOXYGEN_PLANTUML_CFG_FILE@ +PLANTUML_INCLUDE_PATH = @DOXYGEN_PLANTUML_INCLUDE_PATH@ +DOT_GRAPH_MAX_NODES = @DOXYGEN_DOT_GRAPH_MAX_NODES@ +MAX_DOT_GRAPH_DEPTH = @DOXYGEN_MAX_DOT_GRAPH_DEPTH@ +DOT_TRANSPARENT = @DOXYGEN_DOT_TRANSPARENT@ +DOT_MULTI_TARGETS = @DOXYGEN_DOT_MULTI_TARGETS@ +GENERATE_LEGEND = @DOXYGEN_GENERATE_LEGEND@ +DOT_CLEANUP = @DOXYGEN_DOT_CLEANUP@ diff --git a/CMakeLists.txt b/CMakeLists.txt index cd539a9c6..9c6be0bc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux endif( APPLE ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof -Wno-sign-compare" ) elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" ) diff --git a/Dockerfile b/Dockerfile index c20b4beaf..5f63bd77b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN \ build-essential \ ca-certificates \ cmake \ + dnsutils \ doxygen \ git \ graphviz \ @@ -22,6 +23,7 @@ RUN \ libreadline-dev \ libssl-dev \ libtool \ + libzmq3-dev \ locales \ ntp \ pkg-config \ @@ -57,7 +59,7 @@ RUN \ cd build/release && \ cmake \ -DBOOST_ROOT="$BOOST_ROOT" \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=Debug \ ../.. && \ make witness_node cli_wallet && \ install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \ diff --git a/README.md b/README.md index 8207bb299..941afa680 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ This is a quick introduction to get new developers and witnesses up to speed on The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS: ``` - sudo apt-get install gcc-5 g++-5 cmake make libbz2-dev\ - libdb++-dev libdb-dev libssl-dev openssl libreadline-dev\ - autoconf libtool git + sudo apt-get install autoconf bash build-essential ca-certificates cmake \ + doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ + libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \ + wget ``` ## Build Boost 1.67.0 diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index d66b4052b..e0e5e6c29 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -13,7 +13,7 @@ add_library( graphene_app # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie graphene_elasticsearch ) +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie graphene_elasticsearch peerplays_sidechain ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index c808a27c7..11d39f693 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -448,7 +448,17 @@ namespace graphene { namespace app { } case balance_object_type:{ /** these are free from any accounts */ break; - } + } case son_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->son_account ); + break; + } case sidechain_address_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->sidechain_address_account ); + break; + } case sport_object_type: case event_group_object_type: case event_object_type: diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 2c7553f5d..db73124f2 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -226,7 +226,7 @@ namespace detail { void new_connection( const fc::http::websocket_connection_ptr& c ) { - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); auto login = std::make_shared( std::ref(*_self) ); login->enable_api("database_api"); diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6b14f2bc3..a2c56c594 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -112,7 +112,7 @@ class database_api_impl : public std::enable_shared_from_this uint64_t get_asset_count()const; // Peerplays - vector list_sports() const; + vector list_sports() const; vector list_event_groups(sport_id_type sport_id) const; vector list_events_in_group(event_group_id_type event_group_id) const; vector list_betting_market_groups(event_id_type) const; @@ -124,14 +124,14 @@ class database_api_impl : public std::enable_shared_from_this vector get_lotteries( asset_id_type stop = asset_id_type(), unsigned limit = 100, asset_id_type start = asset_id_type() )const; - vector get_account_lotteries( account_id_type issuer, + vector 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 get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const; vector get_limit_orders( const std::string& a, const std::string& b, const uint32_t limit)const; @@ -156,6 +156,24 @@ class database_api_impl : public std::enable_shared_from_this fc::optional get_committee_member_by_account(const std::string account_id_or_name)const; map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + // SON members + vector> get_sons(const vector& son_ids)const; + fc::optional get_son_by_account(account_id_type account)const; + map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; + uint64_t get_son_count()const; + + // SON wallets + optional get_active_son_wallet(); + optional get_son_wallet_by_time_point(time_point_sec time_point); + vector> get_son_wallets(uint32_t limit); + + // Sidechain addresses + vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; + vector> get_sidechain_addresses_by_account(account_id_type account)const; + vector> get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const; + fc::optional get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const; + uint64_t get_sidechain_addresses_count()const; + // Votes vector lookup_vote_ids( const vector& votes )const; @@ -217,6 +235,9 @@ class database_api_impl : public std::enable_shared_from_this vector get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const; vector 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 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; @@ -571,11 +592,11 @@ vector> database_api::get_key_references( vector> database_api_impl::get_key_references( vector keys )const { wdump( (keys) ); - + const auto& idx = _db.get_index_type(); const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); - + vector< vector > final_result; final_result.reserve(keys.size()); @@ -696,7 +717,7 @@ std::map database_api_impl::get_full_accounts( const const auto& proposal_idx = _db.get_index_type(); const auto& pidx = dynamic_cast(proposal_idx); const auto& proposals_by_account = pidx.get_secondary_index(); - + std::map results; for (const std::string& account_name_or_id : names_or_ids) @@ -786,7 +807,7 @@ std::map database_api_impl::get_full_accounts( const acnt.withdraws.emplace_back(withdraw); }); - auto pending_payouts_range = + auto pending_payouts_range = _db.get_index_type().indices().get().equal_range(boost::make_tuple(account->id)); std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments)); @@ -1143,7 +1164,7 @@ vector database_api_impl::get_lotteries( asset_id_type stop, return result; } -vector database_api::get_account_lotteries( account_id_type issuer, +vector database_api::get_account_lotteries( account_id_type issuer, asset_id_type stop, unsigned limit, asset_id_type start )const @@ -1151,7 +1172,7 @@ vector database_api::get_account_lotteries( account_id_type issuer return my->get_account_lotteries( issuer, stop, limit, start ); } -vector database_api_impl::get_account_lotteries( account_id_type issuer, +vector database_api_impl::get_account_lotteries( account_id_type issuer, asset_id_type stop, unsigned limit, asset_id_type start )const @@ -1793,6 +1814,213 @@ map database_api_impl::lookup_committee_member return committee_members_by_account_name; } +////////////////////////////////////////////////////////////////////// +// // +// SON members // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_sons(const vector& son_ids)const +{ + return my->get_sons( son_ids ); +} + +vector> database_api_impl::get_sons(const vector& son_ids)const +{ + vector> result; result.reserve(son_ids.size()); + std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result), + [this](son_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +fc::optional database_api::get_son_by_account(account_id_type account)const +{ + return my->get_son_by_account( account ); +} + +fc::optional database_api_impl::get_son_by_account(account_id_type account) const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; +} + +map database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const +{ + return my->lookup_son_accounts( lower_bound_name, limit ); +} + +map 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().indices().get(); + + // 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 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().indices().size(); +} + +////////////////////////////////////////////////////////////////////// +// // +// SON Wallets // +// // +////////////////////////////////////////////////////////////////////// + +optional database_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional database_api_impl::get_active_son_wallet() +{ + const auto& idx = _db.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj != idx.rend()) { + return *obj; + } + return {}; +} + +optional database_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +optional database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point) +{ + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) { + if ((time_point >= swo.valid_from) && (time_point < swo.expires)) + return swo; + } + return {}; +} + +vector> database_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + +vector> database_api_impl::get_son_wallets(uint32_t limit) +{ + FC_ASSERT( limit <= 1000 ); + vector> result; + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) + result.push_back(swo); + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Sidechain Accounts // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_sidechain_addresses(const vector& sidechain_address_ids)const +{ + return my->get_sidechain_addresses( sidechain_address_ids ); +} + +vector> database_api_impl::get_sidechain_addresses(const vector& sidechain_address_ids)const +{ + vector> 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 { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +vector> database_api::get_sidechain_addresses_by_account(account_id_type account)const +{ + return my->get_sidechain_addresses_by_account( account ); +} + +vector> database_api_impl::get_sidechain_addresses_by_account(account_id_type account)const +{ + vector> result; + const auto& sidechain_addresses_range = _db.get_index_type().indices().get().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> database_api::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const +{ + return my->get_sidechain_addresses_by_sidechain( sidechain ); +} + +vector> database_api_impl::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const +{ + vector> result; + const auto& sidechain_addresses_range = _db.get_index_type().indices().get().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 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 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().indices().get(); + 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().indices().size(); +} + ////////////////////////////////////////////////////////////////////// // // // Votes // @@ -1812,6 +2040,7 @@ vector database_api_impl::lookup_vote_ids( const vector& const auto& committee_idx = _db.get_index_type().indices().get(); const auto& for_worker_idx = _db.get_index_type().indices().get(); const auto& against_worker_idx = _db.get_index_type().indices().get(); + const auto& son_idx = _db.get_index_type().indices().get(); vector result; result.reserve( votes.size() ); @@ -1854,6 +2083,16 @@ vector database_api_impl::lookup_vote_ids( const vector& } 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) ); @@ -2203,7 +2442,7 @@ vector database_api::get_tournaments(tournament_id_type stop, vector database_api_impl::get_tournaments(tournament_id_type stop, unsigned limit, - tournament_id_type start) + tournament_id_type start) { vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); @@ -2230,7 +2469,7 @@ vector database_api_impl::get_tournaments_by_state(tournament unsigned limit, tournament_id_type start, tournament_state state) -{ +{ vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); for (auto elem: tournament_idx) { @@ -2888,6 +3127,23 @@ vector database_api_impl::get_offer_history_by_bidder(cons return result; } + +vector database_api::get_account_roles_by_owner(account_id_type owner) const +{ + return my->get_account_roles_by_owner(owner); +} + +vector database_api_impl::get_account_roles_by_owner(account_id_type owner) const +{ + const auto &idx_aro = _db.get_index_type().indices().get(); + auto idx_aro_range = idx_aro.equal_range(owner); + vector result; + for (auto itr = idx_aro_range.first; itr != idx_aro_range.second; ++itr) + { + result.push_back(*itr); + } + return result; +} ////////////////////////////////////////////////////////////////////// // // // Private methods // @@ -2986,7 +3242,7 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec /// 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 ) + //if( _subscribe_callback ) // _subscribe_callback( updates ); for(auto id : ids) diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index a313e2f8c..a436aacdc 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -61,8 +61,14 @@ namespace graphene { namespace app { plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options); if( !plugin_cli_options.options().empty() ) _cli_options.add(plugin_cli_options); + if( !plugin_cfg_options.options().empty() ) + { + std::string header_name = "plugin-cfg-header-" + plug->plugin_name(); + std::string header_desc = plug->plugin_name() + " plugin options"; + _cfg_options.add_options()(header_name.c_str(), header_desc.c_str()); _cfg_options.add(plugin_cfg_options); + } add_available_plugin( plug ); return plug; diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 0b1411252..ec5f8c1a4 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include #include #include @@ -52,6 +55,7 @@ #include #include #include +#include #include @@ -599,6 +603,102 @@ class database_api */ map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + ///////////////// + // SON members // + ///////////////// + + /** + * @brief Get a list of SONs by ID + * @param son_ids IDs of the SONs to retrieve + * @return The SONs corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_sons(const vector& son_ids)const; + + /** + * @brief Get the SON owned by a given account + * @param account The ID of the account whose SON should be retrieved + * @return The SON object, or null if the account does not have a SON + */ + fc::optional get_son_by_account(account_id_type account)const; + + /** + * @brief Get names and IDs for registered SONs + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of SON names to corresponding IDs + */ + map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; + + /** + * @brief Get the total number of SONs registered with the blockchain + */ + uint64_t get_son_count()const; + + ///////////////////////// + // SON Wallets // + ///////////////////////// + + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + + ///////////////////////// + // Sidechain Addresses // + ///////////////////////// + + /** + * @brief Get a list of sidechain addresses + * @param sidechain_address_ids IDs of the sidechain addresses to retrieve + * @return The sidechain accounts corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; + + /** + * @brief Get the sidechain addresses for a given account + * @param account The ID of the account whose sidechain addresses should be retrieved + * @return The sidechain addresses objects, or null if the account does not have a sidechain addresses + */ + vector> get_sidechain_addresses_by_account(account_id_type account)const; + + /** + * @brief Get the sidechain addresses for a given sidechain + * @param sidechain Sidechain for which addresses should be retrieved + * @return The sidechain addresses objects, or null if the sidechain does not have any addresses + */ + vector> get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const; + + /** + * @brief Get the sidechain addresses for a given account and sidechain + * @param account The ID of the account whose sidechain addresses should be retrieved + * @param sidechain Sidechain for which address should be retrieved + * @return The sidechain addresses objects, or null if the account does not have a sidechain addresses for a given sidechain + */ + fc::optional get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const; + + /** + * @brief Get the total number of sidechain addresses registered with the blockchain + */ + uint64_t get_sidechain_addresses_count()const; /// WORKERS @@ -829,6 +929,11 @@ class database_api vector get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const; vector get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const; vector 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 get_account_roles_by_owner(account_id_type owner) const; private: std::shared_ptr< database_api_impl > my; }; @@ -937,6 +1042,24 @@ FC_API(graphene::app::database_api, (get_committee_member_by_account) (lookup_committee_member_accounts) + // SON members + (get_sons) + (get_son_by_account) + (lookup_son_accounts) + (get_son_count) + + // SON wallets + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) + + // Sidechain addresses + (get_sidechain_addresses) + (get_sidechain_addresses_by_account) + (get_sidechain_addresses_by_sidechain) + (get_sidechain_address_by_account_and_sidechain) + (get_sidechain_addresses_count) + // workers (get_workers_by_account) // Votes @@ -999,4 +1122,7 @@ FC_API(graphene::app::database_api, (get_offer_history_by_issuer) (get_offer_history_by_item) (get_offer_history_by_bidder) + + // Account Roles + (get_account_roles_by_owner) ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt old mode 100644 new mode 100755 index 88d868e5e..73024f692 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -124,6 +124,18 @@ add_library( graphene_chain offer_evaluator.cpp nft_evaluator.cpp protocol/nft.cpp + protocol/account_role.cpp + account_role_evaluator.cpp + + son_evaluator.cpp + son_object.cpp + + son_wallet_evaluator.cpp + son_wallet_deposit_evaluator.cpp + son_wallet_withdraw_evaluator.cpp + + sidechain_address_evaluator.cpp + sidechain_transaction_evaluator.cpp ${HEADERS} ${PROTOCOL_HEADERS} diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index ad6ac5dce..a166e7e5c 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -110,6 +110,8 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio } if( d.head_block_time() < HARDFORK_999_TIME ) FC_ASSERT( !op.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" ); + if (d.head_block_time() < HARDFORK_SON_TIME) + FC_ASSERT(op.name != "son-account", "Son account creation before SON hardfork"); FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." ); FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." ); diff --git a/libraries/chain/account_role_evaluator.cpp b/libraries/chain/account_role_evaluator.cpp new file mode 100644 index 000000000..9c0274e4f --- /dev/null +++ b/libraries/chain/account_role_evaluator.cpp @@ -0,0 +1,162 @@ +#include + +#include +#include +#include +#include + +namespace graphene +{ + namespace chain + { + + void_result account_role_create_evaluator::do_evaluate(const account_role_create_operation &op) + { + try + { + const database &d = db(); + auto now = d.head_block_time(); + FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF"); + op.owner(d); + + rbac_operation_hardfork_visitor arvtor(now); + for (const auto &op_type : op.allowed_operations) + { + arvtor(op_type); + } + + for (const auto &acc : op.whitelisted_accounts) + { + acc(d); + } + + FC_ASSERT(op.valid_to > now, "valid_to expiry should be in future"); + FC_ASSERT((op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry"); + + const auto &ar_idx = d.get_index_type().indices().get(); + auto aro_range = ar_idx.equal_range(op.owner); + FC_ASSERT(std::distance(aro_range.first, aro_range.second) < d.get_global_properties().parameters.account_roles_max_per_account(), "Max account roles that can be created by one owner is reached"); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + object_id_type account_role_create_evaluator::do_apply(const account_role_create_operation &op) + { + try + { + database &d = db(); + return d.create([&op](account_role_object &obj) mutable { + obj.owner = op.owner; + obj.name = op.name; + obj.metadata = op.metadata; + obj.allowed_operations = op.allowed_operations; + obj.whitelisted_accounts = op.whitelisted_accounts; + obj.valid_to = op.valid_to; + }) + .id; + } + FC_CAPTURE_AND_RETHROW((op)) + } + + void_result account_role_update_evaluator::do_evaluate(const account_role_update_operation &op) + { + try + { + const database &d = db(); + auto now = d.head_block_time(); + FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF"); + op.owner(d); + const account_role_object &aobj = op.account_role_id(d); + FC_ASSERT(aobj.owner == op.owner, "Only owner account can update account role object"); + + for (const auto &op_type : op.allowed_operations_to_remove) + { + FC_ASSERT(aobj.allowed_operations.find(op_type) != aobj.allowed_operations.end(), + "Cannot remove non existent operation"); + } + + for (const auto &acc : op.accounts_to_remove) + { + FC_ASSERT(aobj.whitelisted_accounts.find(acc) != aobj.whitelisted_accounts.end(), + "Cannot remove non existent account"); + } + + rbac_operation_hardfork_visitor arvtor(now); + for (const auto &op_type : op.allowed_operations_to_add) + { + arvtor(op_type); + } + FC_ASSERT((aobj.allowed_operations.size() + op.allowed_operations_to_add.size() - op.allowed_operations_to_remove.size()) > 0, "Allowed operations should be positive"); + + for (const auto &acc : op.accounts_to_add) + { + acc(d); + } + FC_ASSERT((aobj.whitelisted_accounts.size() + op.accounts_to_add.size() - op.accounts_to_remove.size()) > 0, "Accounts should be positive"); + + if (op.valid_to) + { + FC_ASSERT(*op.valid_to > now, "valid_to expiry should be in future"); + FC_ASSERT((*op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry"); + } + + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + void_result account_role_update_evaluator::do_apply(const account_role_update_operation &op) + { + try + { + database &d = db(); + const account_role_object &aobj = op.account_role_id(d); + d.modify(aobj, [&op](account_role_object &obj) { + if (op.name) + obj.name = *op.name; + if (op.metadata) + obj.metadata = *op.metadata; + obj.allowed_operations.insert(op.allowed_operations_to_add.begin(), op.allowed_operations_to_add.end()); + obj.whitelisted_accounts.insert(op.accounts_to_add.begin(), op.accounts_to_add.end()); + for (const auto &op_type : op.allowed_operations_to_remove) + obj.allowed_operations.erase(op_type); + for (const auto &acc : op.accounts_to_remove) + obj.whitelisted_accounts.erase(acc); + if (op.valid_to) + obj.valid_to = *op.valid_to; + }); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + void_result account_role_delete_evaluator::do_evaluate(const account_role_delete_operation &op) + { + try + { + const database &d = db(); + auto now = d.head_block_time(); + FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF"); + op.owner(d); + const account_role_object &aobj = op.account_role_id(d); + FC_ASSERT(aobj.owner == op.owner, "Only owner account can delete account role object"); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + void_result account_role_delete_evaluator::do_apply(const account_role_delete_operation &op) + { + try + { + database &d = db(); + const account_role_object &aobj = op.account_role_id(d); + d.remove(aobj); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + } // namespace chain +} // namespace graphene diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 7a26a2cbe..e61684389 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -42,6 +42,9 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o database& d = db(); + if (d.head_block_time() < HARDFORK_SON_TIME) + FC_ASSERT(op.symbol != "BTC", "BTC asset creation before SON hardfork"); + const auto& chain_parameters = d.get_global_properties().parameters; FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); diff --git a/libraries/chain/custom_account_authority_evaluator.cpp b/libraries/chain/custom_account_authority_evaluator.cpp index 200590f61..bbf29c2cf 100644 --- a/libraries/chain/custom_account_authority_evaluator.cpp +++ b/libraries/chain/custom_account_authority_evaluator.cpp @@ -4,37 +4,13 @@ #include #include #include +#include namespace graphene { namespace chain { -struct rbac_operation_hardfork_visitor -{ - typedef void result_type; - const fc::time_point_sec block_time; - - rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {} - void operator()(int op_type) const - { - int first_allowed_op = operation::tag::value; - switch (op_type) - { - case operation::tag::value: - case operation::tag::value: - case operation::tag::value: - case operation::tag::value: - case operation::tag::value: - case operation::tag::value: - FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permission not allowed on this operation yet!"); - break; - default: - FC_ASSERT(op_type < first_allowed_op, "Custom permission not allowed on this operation!"); - } - } -}; - void_result create_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_create_operation &op) { try @@ -149,4 +125,4 @@ void_result delete_custom_account_authority_evaluator::do_apply(const custom_acc } } // namespace chain -} // namespace graphene \ No newline at end of file +} // namespace graphene diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e2fc9aab6..e8a49199d 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -426,6 +426,7 @@ processed_transaction database::push_proposal(const proposal_object& proposal) auto session = _undo_db.start_undo_session(true); for( auto& op : proposal.proposed_transaction.operations ) eval_state.operation_results.emplace_back(apply_operation(eval_state, op)); + remove_son_proposal(proposal); remove(proposal); session.merge(); } catch ( const fc::exception& e ) { @@ -698,8 +699,13 @@ void database::_apply_block( const signed_block& next_block ) _current_virtual_op = 0; } - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) - update_witness_schedule(next_block); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { + update_witness_schedule(next_block); + if(global_props.active_sons.size() > 0) { + update_son_schedule(next_block); + } + } + const uint32_t missed = update_witness_missed_blocks( next_block ); update_global_dynamic_data( next_block, missed ); update_signing_witness(signing_witness, next_block); @@ -729,8 +735,13 @@ void database::_apply_block( const signed_block& next_block ) // update_global_dynamic_data() as perhaps these methods only need // to be called for header validation? update_maintenance_flag( maint_needed ); - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) - update_witness_schedule(); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { + update_witness_schedule(); + if(global_props.active_sons.size() > 0) { + update_son_schedule(); + } + } + if( !_node_property_object.debug_updates.empty() ) apply_debug_updates(); @@ -789,12 +800,14 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx if( !(skip & (skip_transaction_signatures | skip_authority_check) ) ) { - auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; - auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; - auto get_custom = [&]( account_id_type id, const operation& op ) { + auto get_active = [this]( account_id_type id ) { return &id(*this).active; }; + auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; }; + auto get_custom = [this]( account_id_type id, const operation& op ) { return get_account_custom_authorities(id, op); }; - trx.verify_authority( chain_id, get_active, get_owner, get_custom, get_global_properties().parameters.max_authority_depth ); + trx.verify_authority( chain_id, get_active, get_owner, get_custom, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()), + get_global_properties().parameters.max_authority_depth ); } //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 0f7af1a8b..07b96fea6 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include @@ -192,4 +195,126 @@ bool database::item_locked(const nft_id_type &item) const auto items_itr = market_items._locked_items.find(item); return (items_itr != market_items._locked_items.end()); } + +bool database::account_role_valid(const account_role_object &aro, account_id_type account, optional op_type) const +{ + return (aro.valid_to > head_block_time()) && + (aro.whitelisted_accounts.find(account) != aro.whitelisted_accounts.end()) && + (!op_type || (aro.allowed_operations.find(*op_type) != aro.allowed_operations.end())); +} +std::set database::get_sons_being_deregistered() +{ + std::set ret; + const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); + + for( auto& son_proposal : son_proposal_idx ) + { + if(son_proposal.proposal_type == son_proposal_type::son_deregister_proposal) + { + ret.insert(son_proposal.son_id); + } + } + return ret; +} + +std::set database::get_sons_to_be_deregistered() +{ + std::set ret; + const auto& son_idx = get_index_type().indices().get< by_id >(); + + for( auto& son : son_idx ) + { + if(son.status == son_status::in_maintenance) + { + auto stats = son.statistics(*this); + // TODO : We need to add a function that returns if we can deregister SON + // i.e. with introduction of PW code, we have to make a decision if the SON + // is needed for release of funds from the PW + if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())) + { + ret.insert(son.id); + } + } + } + return ret; +} + +std::set database::get_sons_being_reported_down() +{ + std::set ret; + const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); + + for( auto& son_proposal : son_proposal_idx ) + { + if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal) + { + ret.insert(son_proposal.son_id); + } + } + return ret; +} + +fc::optional database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ) +{ + son_deregister_operation son_dereg_op; + son_dereg_op.payer = get_global_properties().parameters.son_account(); + son_dereg_op.son_id = son_id; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = paying_son; + proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) ); + uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3; + proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime ); + return proposal_op; +} + +signed_transaction database::create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ) +{ + signed_transaction processed_trx; + auto dyn_props = get_dynamic_global_properties(); + processed_trx.set_reference_block( dyn_props.head_block_id ); + processed_trx.set_expiration( head_block_time() + get_global_properties().parameters.maximum_time_until_expiration ); + processed_trx.operations.push_back( op ); + current_fee_schedule().set_fee( processed_trx.operations.back() ); + + processed_trx.sign( signing_private_key, get_chain_id() ); + + return processed_trx; +} + +bool database::is_son_dereg_valid( son_id_type son_id ) +{ + const auto& son_idx = get_index_type().indices().get< by_id >(); + auto son = son_idx.find( son_id ); + if(son == son_idx.end()) + { + return false; + } + + return (son->status == son_status::in_maintenance && + (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); +} + +bool database::is_son_active( son_id_type son_id ) +{ + const auto& son_idx = get_index_type().indices().get< by_id >(); + auto son = son_idx.find( son_id ); + if(son == son_idx.end()) + { + return false; + } + + const global_property_object& gpo = get_global_properties(); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); + return (it_son != active_son_ids.end()); +} + } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 9ae1fb964..e857ed94a 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -60,6 +61,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -85,6 +93,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -186,6 +201,9 @@ const uint8_t offer_object::type_id; const uint8_t offer_history_object::space_id; const uint8_t offer_history_object::type_id; +const uint8_t account_role_object::space_id; +const uint8_t account_role_object::type_id; + void database::initialize_evaluators() { _operation_evaluators.resize(255); @@ -275,6 +293,28 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -291,6 +331,7 @@ void database::initialize_indexes() acnt_index->add_secondary_index(); add_index< primary_index >(); // 256 members per chunk + add_index< primary_index >(); add_index< primary_index >(); // 1024 witnesses per chunk add_index< primary_index >(); add_index< primary_index >(); @@ -323,6 +364,15 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); + + add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); + + add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); @@ -339,6 +389,7 @@ void database::initialize_indexes() add_index< primary_index> >(); add_index< primary_index > >(); add_index< primary_index > >(); + add_index< primary_index > >(); add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); @@ -353,6 +404,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); } @@ -997,6 +1049,29 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); + // Initialize witness schedule +#ifndef NDEBUG + const son_schedule_object& sso = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_witnesses = get_global_properties().active_witnesses; + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); + + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( sso.id == son_schedule_id_type() ); + // Enable fees modify(get_global_properties(), [&genesis_state](global_property_object& p) { p.parameters.current_fees = genesis_state.initial_parameters.current_fees; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 03d2a274d..d23b0fe28 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,32 @@ vector> database::sort return refs; } +template<> +vector> database::sort_votable_objects(size_t count) const +{ + const auto& all_sons = get_index_type().indices().get< by_id >(); + std::vector> refs; + for( auto& son : all_sons ) + { + if(son.has_valid_config() && son.status != son_status::deregistered) + { + refs.push_back(std::cref(son)); + } + } + count = std::min(count, refs.size()); + std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), + [this](const son_object& a, const son_object& b)->bool { + share_type oa_vote = _vote_tally_buffer[a.vote_id]; + share_type ob_vote = _vote_tally_buffer[b.vote_id]; + if( oa_vote != ob_vote ) + return oa_vote > ob_vote; + return a.vote_id < b.vote_id; + }); + + refs.resize(count, refs.front()); + return refs; +} + template void database::perform_account_maintenance(Type tally_helper) { @@ -150,6 +177,215 @@ void database::update_worker_votes() } } +void database::pay_sons() +{ + auto get_weight = []( uint64_t total_votes ) { + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + uint16_t weight = std::max((total_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + time_point_sec now = head_block_time(); + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + // Current requirement is that we have to pay every 24 hours, so the following check + if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { + uint64_t weighted_total_txs_signed = 0; + share_type son_budget = dpo.son_budget; + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight](const object& o) { + const son_statistics_object& s = static_cast(o); + const auto& idx = get_index_type().indices().get(); + auto son_obj = idx.find( s.owner ); + auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); + weighted_total_txs_signed += (s.txs_signed * son_weight); + }); + + + // Now pay off each SON proportional to the number of transactions signed. + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight](const object& o) { + const son_statistics_object& s = static_cast(o); + if(s.txs_signed > 0){ + auto son_params = get_global_properties().parameters; + const auto& idx = get_index_type().indices().get(); + auto son_obj = idx.find( s.owner ); + auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); + share_type pay = (s.txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; + + modify( *son_obj, [&]( son_object& _son_obj) + { + _son_obj.pay_son_fee(pay, *this); + }); + //Remove the amount paid out to SON from global SON Budget + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.son_budget -= pay; + } ); + //Reset the tx counter in each son statistics object + modify( s, [&]( son_statistics_object& _s) + { + _s.total_txs_signed += _s.txs_signed; + _s.txs_signed = 0; + }); + } + }); + //Note the last son pay out time + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_son_payout_time = now; + }); + } +} + +void database::update_son_metrics(const vector& curr_active_sons) +{ + vector current_sons; + + current_sons.reserve(curr_active_sons.size()); + std::transform(curr_active_sons.begin(), curr_active_sons.end(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + const auto& son_idx = get_index_type().indices().get< by_id >(); + for( auto& son : son_idx ) + { + auto& stats = son.statistics(*this); + bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); + modify( stats, [&]( son_statistics_object& _stats ) + { + _stats.total_downtime += _stats.current_interval_downtime; + _stats.current_interval_downtime = 0; + if(is_active_son) + { + _stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval; + } + }); + } +} + +void database::update_son_statuses(const vector& curr_active_sons, const vector& new_active_sons) +{ + vector current_sons, new_sons; + vector sons_to_remove, sons_to_add; + const auto& idx = get_index_type().indices().get(); + + current_sons.reserve(curr_active_sons.size()); + std::transform(curr_active_sons.begin(), curr_active_sons.end(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + new_sons.reserve(new_active_sons.size()); + std::transform(new_active_sons.begin(), new_active_sons.end(), + std::inserter(new_sons, new_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + // find all cur_active_sons members that is not in new_active_sons + for_each(current_sons.begin(), current_sons.end(), + [&sons_to_remove, &new_sons](const son_id_type& si) + { + if(std::find(new_sons.begin(), new_sons.end(), si) == + new_sons.end()) + { + sons_to_remove.push_back(si); + } + } + ); + + for( const auto& sid : sons_to_remove ) + { + auto son = idx.find( sid ); + if(son == idx.end()) // SON is deleted already + continue; + // keep maintenance status for nodes becoming inactive + if(son->status == son_status::active) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::inactive; + }); + } + } + + // find all new_active_sons members that is not in cur_active_sons + for_each(new_sons.begin(), new_sons.end(), + [&sons_to_add, ¤t_sons](const son_id_type& si) + { + if(std::find(current_sons.begin(), current_sons.end(), si) == + current_sons.end()) + { + sons_to_add.push_back(si); + } + } + ); + + for( const auto& sid : sons_to_add ) + { + auto son = idx.find( sid ); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); + // keep maintenance status for new nodes + if(son->status == son_status::inactive) + { + modify( *son, [&]( son_object& obj ){ + obj.status = son_status::active; + }); + } + } + + ilog("New SONS"); + for(size_t i = 0; i < new_sons.size(); i++) { + auto son = idx.find( new_sons[i] ); + if(son == idx.end()) // SON is deleted already + continue; + ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) ); + } + + if( sons_to_remove.size() > 0 ) + { + remove_inactive_son_proposals(sons_to_remove); + } +} + +void database::update_son_wallet(const vector& new_active_sons) +{ + bool should_recreate_pw = true; + + // Expire for current son_wallet_object wallet, if exists + const auto& idx_swi = get_index_type().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*obj).sons; + + bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); + if (wallet_son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + } + } + + should_recreate_pw = !wallet_son_sets_equal; + + if (should_recreate_pw) { + modify(*obj, [&, obj](son_wallet_object &swo) { + swo.expires = head_block_time(); + }); + } + } + + should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count); + + if (should_recreate_pw) { + // Create new son_wallet_object, to initiate wallet recreation + create( [&]( son_wallet_object& obj ) { + obj.valid_from = head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); + }); + } +} + void database::pay_workers( share_type& budget ) { const auto head_time = head_block_time(); @@ -207,7 +443,7 @@ void database::update_active_witnesses() /// accounts that vote for 0 or 1 witness do not get to express an opinion on /// the number of witnesses to have (they abstain and are non-voting accounts) - share_type stake_tally = 0; + share_type stake_tally = 0; size_t witness_count = 0; if( stake_target > 0 ) @@ -306,7 +542,7 @@ void database::update_active_witnesses() void database::update_active_committee_members() { try { assert( _committee_count_histogram_buffer.size() > 0 ); - share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2; + share_type stake_target = (_total_voting_stake-_committee_count_histogram_buffer[0]) / 2; /// accounts that vote for 0 or 1 witness do not get to express an opinion on /// the number of witnesses to have (they abstain and are non-voting accounts) @@ -395,6 +631,146 @@ void database::update_active_committee_members() }); } FC_CAPTURE_AND_RETHROW() } +void database::update_active_sons() +{ try { + assert( _son_count_histogram_buffer.size() > 0 ); + share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; + + /// accounts that vote for 0 or 1 son do not get to express an opinion on + /// the number of sons to have (they abstain and are non-voting accounts) + + share_type stake_tally = 0; + + size_t son_count = 0; + if( stake_target > 0 ) + { + while( (son_count < _son_count_histogram_buffer.size() - 1) + && (stake_tally <= stake_target) ) + { + stake_tally += _son_count_histogram_buffer[++son_count]; + } + } + + const global_property_object& gpo = get_global_properties(); + const chain_parameters& cp = gpo.parameters; + auto sons = sort_votable_objects(cp.maximum_son_count); + + const auto& all_sons = get_index_type().indices(); + + auto& local_vote_buffer_ref = _vote_tally_buffer; + for( const son_object& son : all_sons ) + { + if(son.status == son_status::request_maintenance) + { + auto& stats = son.statistics(*this); + modify( stats, [&]( son_statistics_object& _s){ + _s.last_down_timestamp = head_block_time(); + }); + } + modify( son, [local_vote_buffer_ref]( son_object& obj ){ + obj.total_votes = local_vote_buffer_ref[obj.vote_id]; + if(obj.status == son_status::request_maintenance) + obj.status = son_status::in_maintenance; + }); + } + + // Update SON authority + if( gpo.parameters.son_account() != GRAPHENE_NULL_ACCOUNT ) + { + modify( get(gpo.parameters.son_account()), [&]( account_object& a ) + { + if( head_block_time() < HARDFORK_533_TIME ) + { + map weights; + a.active.weight_threshold = 0; + a.active.account_auths.clear(); + + for( const son_object& son : sons ) + { + weights.emplace(son.son_account, uint64_t(1)); + } + + for( const auto& weight : weights ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + a.active.account_auths[weight.first] += 1; + a.active.weight_threshold += 1; + } + + a.active.weight_threshold *= 2; + a.active.weight_threshold /= 3; + a.active.weight_threshold += 1; + } + else + { + vote_counter vc; + for( const son_object& son : sons ) + vc.add( son.son_account, UINT64_C(1) ); + vc.finish_2_3( a.active ); + } + } ); + } + + + // Compare current and to-be lists of active sons + auto cur_active_sons = gpo.active_sons; + vector new_active_sons; + const auto &acc = get(gpo.parameters.son_account()); + for( const son_object& son : sons ) { + son_info swi; + swi.son_id = son.id; + swi.weight = acc.active.account_auths.at(son.son_account); + swi.signing_key = son.signing_key; + swi.sidechain_public_keys = son.sidechain_public_keys; + new_active_sons.push_back(swi); + } + + bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size()); + if (son_sets_equal) { + for( size_t i = 0; i < cur_active_sons.size(); i++ ) { + son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i); + } + } + + if (son_sets_equal) { + ilog( "Active SONs set NOT CHANGED" ); + } else { + ilog( "Active SONs set CHANGED" ); + + update_son_wallet(new_active_sons); + update_son_statuses(cur_active_sons, new_active_sons); + } + + // Update son performance metrics + update_son_metrics(cur_active_sons); + + modify(gpo, [&]( global_property_object& gp ){ + gp.active_sons.clear(); + gp.active_sons.reserve(new_active_sons.size()); + gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end()); + }); + + const son_schedule_object& sso = son_schedule_id_type()(*this); + modify(sso, [&](son_schedule_object& _sso) + { + flat_set active_sons; + active_sons.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_sons, active_sons.end()), + [](const son_info& swi) { + return swi.son_id; + }); + _sso.scheduler.update(active_sons); + // similar to witness, produce schedule for sons + if(cur_active_sons.size() == 0 && new_active_sons.size() > 0) + { + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + for( size_t i=0; i= HARDFORK_SON_TIME){ + rec.leftover_son_funds = dpo.son_budget; + available_funds += rec.leftover_son_funds; + son_budget = gpo.parameters.son_pay_max(); + son_budget = std::min(son_budget, available_funds); + rec.son_budget = son_budget; + available_funds -= son_budget; + } + fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value; worker_budget_u128 *= uint64_t(time_to_maint); worker_budget_u128 /= 60*60*24; @@ -502,9 +889,11 @@ void database::process_budget() rec.supply_delta = rec.witness_budget + rec.worker_budget + + rec.son_budget - rec.leftover_worker_funds - rec.from_accumulated_fees - - rec.from_unused_witness_budget; + - rec.from_unused_witness_budget + - rec.leftover_son_funds; modify(core, [&]( asset_dynamic_data_object& _core ) { @@ -513,9 +902,11 @@ void database::process_budget() assert( rec.supply_delta == witness_budget + worker_budget + + son_budget - leftover_worker_funds - _core.accumulated_fees - dpo.witness_budget + - dpo.son_budget ); _core.accumulated_fees = 0; }); @@ -526,6 +917,7 @@ void database::process_budget() // available_funds, we replace it with witness_budget // instead of adding it. _dpo.witness_budget = witness_budget; + _dpo.son_budget = son_budget; _dpo.last_budget_time = now; }); @@ -941,14 +1333,23 @@ void clear_expired_custom_account_authorities(database& db) } } +void clear_expired_account_roles(database& db) +{ + const auto& arindex = db.get_index_type().indices().get(); + while(!arindex.empty() && arindex.begin()->valid_to < db.head_block_time()) + { + db.remove(*arindex.begin()); + } +} + // Schedules payouts from a dividend distribution account to the current holders of the // dividend-paying asset. This takes any deposits made to the dividend distribution account // since the last time it was called, and distributes them to the current owners of the // dividend-paying asset according to the amount they own. -void schedule_pending_dividend_balances(database& db, +void schedule_pending_dividend_balances(database& db, const asset_object& dividend_holder_asset_obj, const asset_dividend_data_object& dividend_data, - const fc::time_point_sec& current_head_block_time, + const fc::time_point_sec& current_head_block_time, const account_balance_index& balance_index, const vesting_balance_index& vesting_index, const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index, @@ -957,7 +1358,7 @@ void schedule_pending_dividend_balances(database& db, dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}", ("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time())); auto balance_by_acc_index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >(); - auto current_distribution_account_balance_range = + auto current_distribution_account_balance_range = //balance_index.indices().get().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account)); balance_by_acc_index.get_account_balances(dividend_data.dividend_distribution_account); auto previous_distribution_account_balance_range = @@ -968,7 +1369,7 @@ void schedule_pending_dividend_balances(database& db, const auto& gpo = db.get_global_properties(); // get the list of accounts that hold nonzero balances of the dividend asset - auto holder_balances_begin = + auto holder_balances_begin = balance_index.indices().get().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id)); auto holder_balances_end = balance_index.indices().get().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type())); @@ -1028,7 +1429,7 @@ void schedule_pending_dividend_balances(database& db, ("previous", (int64_t)std::distance(previous_distribution_account_balance_range.first, previous_distribution_account_balance_range.second))); // when we pay out the dividends to the holders, we need to know the total balance of the dividend asset in all - // accounts other than the distribution account (it would be silly to distribute dividends back to + // accounts other than the distribution account (it would be silly to distribute dividends back to // the distribution account) share_type total_balance_of_dividend_asset; if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // only core @@ -1059,7 +1460,7 @@ void schedule_pending_dividend_balances(database& db, share_type previous_balance; asset_id_type payout_asset_type; - if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second || + if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second || current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type) { // there are no more previous balances or there is no previous balance for this particular asset type @@ -1067,7 +1468,7 @@ void schedule_pending_dividend_balances(database& db, current_balance = current_distribution_account_balance_iter->second->balance; idump((payout_asset_type)(current_balance)); } - else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() || + else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() || previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type) { // there are no more current balances or there is no current balance for this particular previous asset type @@ -1086,7 +1487,7 @@ void schedule_pending_dividend_balances(database& db, share_type delta_balance = current_balance - previous_balance; - // Next, figure out if we want to share this out -- if the amount added to the distribution + // Next, figure out if we want to share this out -- if the amount added to the distribution // account since last payout is too small, we won't bother. share_type total_fee_per_asset_in_payout_asset; @@ -1095,7 +1496,7 @@ void schedule_pending_dividend_balances(database& db, { payout_asset_object = &db.get_core_asset(); total_fee_per_asset_in_payout_asset = total_fee_per_asset_in_core; - dlog("Fee for distributing ${payout_asset_type}: ${fee}", + dlog("Fee for distributing ${payout_asset_type}: ${fee}", ("payout_asset_type", asset_id_type()(db).symbol) ("fee", asset(total_fee_per_asset_in_core, asset_id_type()))); } @@ -1111,7 +1512,7 @@ void schedule_pending_dividend_balances(database& db, FC_ASSERT(total_fee_per_asset.asset_id == payout_asset_type); total_fee_per_asset_in_payout_asset = total_fee_per_asset.amount; - dlog("Fee for distributing ${payout_asset_type}: ${fee}", + dlog("Fee for distributing ${payout_asset_type}: ${fee}", ("payout_asset_type", payout_asset_type(db).symbol)("fee", total_fee_per_asset_in_payout_asset)); } @@ -1124,7 +1525,7 @@ void schedule_pending_dividend_balances(database& db, wdump((total_fee_per_asset_in_payout_asset)(dividend_data.options)); minimum_shares_to_distribute = minimum_amount_to_distribute.to_uint64(); } - + dlog("Processing dividend payments of asset type ${payout_asset_type}, delta balance is ${delta_balance}", ("payout_asset_type", payout_asset_type(db).symbol)("delta_balance", delta_balance)); if (delta_balance > 0) { @@ -1137,7 +1538,7 @@ void schedule_pending_dividend_balances(database& db, db.modify(asset_dynamic_data_id_type()(db), [total_fee_per_asset_in_core](asset_dynamic_data_object& d) { d.accumulated_fees += total_fee_per_asset_in_core; }); - db.adjust_balance(dividend_data.dividend_distribution_account, + db.adjust_balance(dividend_data.dividend_distribution_account, asset(-total_fee_per_asset_in_core, asset_id_type())); delta_balance -= total_fee_per_asset_in_core; } @@ -1152,7 +1553,7 @@ void schedule_pending_dividend_balances(database& db, ("need", asset(total_fee_per_asset_in_core, asset_id_type())) ("have", asset(dynamic_data.fee_pool, payout_asset_type))); // deduct the fee from the dividend distribution account - db.adjust_balance(dividend_data.dividend_distribution_account, + db.adjust_balance(dividend_data.dividend_distribution_account, asset(-total_fee_per_asset_in_payout_asset, payout_asset_type)); // convert it to core db.modify(payout_asset_object->dynamic_data(db), [total_fee_per_asset_in_core, total_fee_per_asset_in_payout_asset](asset_dynamic_data_object& d) { @@ -1166,7 +1567,7 @@ void schedule_pending_dividend_balances(database& db, delta_balance -= total_fee_per_asset_in_payout_asset; } - dlog("There are ${count} holders of the dividend-paying asset, with a total balance of ${total}", + dlog("There are ${count} holders of the dividend-paying asset, with a total balance of ${total}", ("count", holder_account_count) ("total", total_balance_of_dividend_asset)); share_type remaining_amount_to_distribute = delta_balance; @@ -1238,7 +1639,7 @@ void schedule_pending_dividend_balances(database& db, dlog("Pending payout: ${account_name} -> ${amount}", ("account_name", pending_payout.owner(db).name) ("amount", asset(pending_payout.pending_balance, pending_payout.dividend_payout_asset_type))); - dlog("Remaining balance not paid out: ${amount}", + dlog("Remaining balance not paid out: ${amount}", ("amount", asset(remaining_amount_to_distribute, payout_asset_type))); share_type distributed_amount = delta_balance - remaining_amount_to_distribute; @@ -1268,7 +1669,7 @@ void schedule_pending_dividend_balances(database& db, // This should be extremely rare (caused by an override transfer by the asset owner). // Reduce all pending payouts proportionally share_type total_pending_balances; - auto pending_payouts_range = + auto pending_payouts_range = pending_payout_balance_index.indices().get().equal_range(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type)); for (const pending_dividend_payout_balance_for_holder_object& pending_balance_object : boost::make_iterator_range(pending_payouts_range.first, pending_payouts_range.second)) @@ -1305,10 +1706,10 @@ void schedule_pending_dividend_balances(database& db, } // iterate - if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second || + if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second || current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type) ++current_distribution_account_balance_iter; - else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() || + else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() || previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type) ++previous_distribution_account_balance_iter; else @@ -1350,7 +1751,7 @@ void process_dividend_assets(database& db) { try { - dlog("Dividend payout time has arrived for asset ${holder_asset}", + dlog("Dividend payout time has arrived for asset ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol)); #ifndef NDEBUG // dump balances before the payouts for debugging @@ -1362,14 +1763,14 @@ void process_dividend_assets(database& db) // when we do the payouts, we first increase the balances in all of the receiving accounts // and use this map to keep track of the total amount of each asset paid out. - // Afterwards, we decrease the distribution account's balance by the total amount paid out, + // Afterwards, we decrease the distribution account's balance by the total amount paid out, // and modify the distributed_balances accordingly std::map amounts_paid_out_by_asset; - auto pending_payouts_range = + auto pending_payouts_range = pending_payout_balance_index.indices().get().equal_range(boost::make_tuple(dividend_holder_asset_obj.id)); // the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account - // we iterate in this order so we can build up a list of payouts for each account to put in the + // we iterate in this order so we can build up a list of payouts for each account to put in the // virtual op vector payouts_for_this_holder; fc::optional last_holder_account_id; @@ -1381,7 +1782,7 @@ void process_dividend_assets(database& db) auto approved_assets_iter = approved_assets.find(asset_id); if (approved_assets_iter != approved_assets.end()) return approved_assets_iter->second; - bool is_approved = is_authorized_asset(db, dividend_distribution_account_object, + bool is_approved = is_authorized_asset(db, dividend_distribution_account_object, asset_id(db)); approved_assets[asset_id] = is_approved; return is_approved; @@ -1394,8 +1795,8 @@ void process_dividend_assets(database& db) if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size()) { // we've moved on to a new account, generate the dividend payment virtual op for the previous one - db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, - *last_holder_account_id, + db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, + *last_holder_account_id, payouts_for_this_holder)); dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name)); payouts_for_this_holder.clear(); @@ -1407,14 +1808,14 @@ void process_dividend_assets(database& db) is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) && is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type)) { - dlog("Processing payout of ${asset} to account ${account}", + dlog("Processing payout of ${asset} to account ${account}", ("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type)) ("account", pending_balance_object.owner(db).name)); db.adjust_balance(pending_balance_object.owner, - asset(pending_balance_object.pending_balance, + asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type)); - payouts_for_this_holder.push_back(asset(pending_balance_object.pending_balance, + payouts_for_this_holder.push_back(asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type)); last_holder_account_id = pending_balance_object.owner; amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance; @@ -1430,8 +1831,8 @@ void process_dividend_assets(database& db) if (last_holder_account_id && payouts_for_this_holder.size()) { // we've moved on to a new account, generate the dividend payment virtual op for the previous one - db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, - *last_holder_account_id, + db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id, + *last_holder_account_id, payouts_for_this_holder)); dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name)); } @@ -1444,11 +1845,11 @@ void process_dividend_assets(database& db) const asset_id_type& asset_paid_out = value.first; const share_type& amount_paid_out = value.second; - db.adjust_balance(dividend_data.dividend_distribution_account, + db.adjust_balance(dividend_data.dividend_distribution_account, asset(-amount_paid_out, asset_paid_out)); - auto distributed_balance_iter = - distributed_dividend_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_obj.id, + auto distributed_balance_iter = + distributed_dividend_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_obj.id, asset_paid_out)); assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get().end()); if (distributed_balance_iter != distributed_dividend_balance_index.indices().get().end()) @@ -1465,7 +1866,7 @@ void process_dividend_assets(database& db) fc::optional next_payout_time; if (dividend_data_obj.options.payout_interval) { - // if there was a previous payout, make our next payment one interval + // if there was a previous payout, make our next payment one interval uint32_t current_time_sec = current_head_block_time.sec_since_epoch(); fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time; uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch(); @@ -1480,12 +1881,84 @@ void process_dividend_assets(database& db) (dividend_data_obj.last_payout_time) (dividend_data_obj.options.next_payout_time)); }); - } + } FC_RETHROW_EXCEPTIONS(error, "Error while paying out dividends for holder asset ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol)) } } } FC_CAPTURE_AND_RETHROW() } +void database::perform_son_tasks() +{ + const global_property_object& gpo = get_global_properties(); + if(gpo.parameters.son_account() == GRAPHENE_NULL_ACCOUNT && head_block_time() >= HARDFORK_SON_TIME) + { + const auto& son_account = create([&](account_object& a) { + a.name = "son-account"; + a.statistics = create([&a](account_statistics_object& s){ + s.owner = a.id; + s.name = a.name; + }).id; + a.owner.weight_threshold = 1; + a.active.weight_threshold = 0; + a.registrar = a.lifetime_referrer = a.referrer = a.id; + a.membership_expiration_date = time_point_sec::maximum(); + a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; + }); + + modify( gpo, [&son_account]( global_property_object& gpo ) { + gpo.parameters.extensions.value.son_account = son_account.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.son_account = son_account.get_id(); + }); + } + // create BTC asset here because son_account is the issuer of the BTC + if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME) + { + const asset_dynamic_data_object& dyn_asset = + create([](asset_dynamic_data_object& a) { + a.current_supply = 0; + }); + + const asset_object& btc_asset = + create( [&gpo, &dyn_asset]( asset_object& a ) { + a.symbol = "BTC"; + a.precision = 8; + a.issuer = gpo.parameters.son_account(); + a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + a.options.market_fee_percent = 500; // 5% + a.options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; + a.options.flags = asset_issuer_permission_flags::charge_market_fee | + //asset_issuer_permission_flags::white_list | + asset_issuer_permission_flags::override_authority | + asset_issuer_permission_flags::transfer_restricted | + asset_issuer_permission_flags::disable_confidential; + a.options.core_exchange_rate.base.amount = 100000; + a.options.core_exchange_rate.base.asset_id = asset_id_type(0); + a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.asset_id = a.id; + a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty + a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set + a.options.whitelist_markets.clear(); // might be traded with + a.options.blacklist_markets.clear(); // might not be traded with + a.dynamic_asset_data_id = dyn_asset.id; + }); + modify( gpo, [&btc_asset]( global_property_object& gpo ) { + gpo.parameters.extensions.value.btc_asset = btc_asset.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id(); + }); + } + // Pay the SONs + if (head_block_time() >= HARDFORK_SON_TIME) + { + // Before making a budget we should pay out SONs + // This function should check if its time to pay sons + // and modify the global son funds accordingly, whatever is left is passed on to next budget + pay_sons(); + } +} + void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) { try { const auto& gpo = get_global_properties(); @@ -1508,6 +1981,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._vote_tally_buffer.resize(props.next_available_vote_id); d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1); d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1); + d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1); d._total_voting_stake = 0; auto balance_type = vesting_balance_type::normal; @@ -1619,6 +2093,18 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // same rationale as for witnesses d._committee_count_histogram_buffer[offset] += voting_stake; } + if( opinion_account.options.num_son <= props.parameters.maximum_son_count ) + { + uint16_t offset = std::min(size_t(opinion_account.options.num_son/2), + d._son_count_histogram_buffer.size() - 1); + // votes for a number greater than maximum_son_count + // are turned into votes for maximum_son_count. + // + // in particular, this takes care of the case where a + // member was voting for a high number, then the + // parameter was lowered. + d._son_count_histogram_buffer[offset] += voting_stake; + } d._total_voting_stake += voting_stake; } @@ -1634,11 +2120,14 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g }; clear_canary a(_witness_count_histogram_buffer), b(_committee_count_histogram_buffer), + d(_son_count_histogram_buffer), c(_vote_tally_buffer); + perform_son_tasks(); update_top_n_authorities(*this); update_active_witnesses(); update_active_committee_members(); + update_active_sons(); update_worker_votes(); const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); @@ -1668,6 +2157,32 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod; if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() ) p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period; + if( !p.pending_parameters->extensions.value.rbac_max_permissions_per_account.valid() ) + p.pending_parameters->extensions.value.rbac_max_permissions_per_account = p.parameters.extensions.value.rbac_max_permissions_per_account; + if( !p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime.valid() ) + p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime = p.parameters.extensions.value.rbac_max_account_authority_lifetime; + if( !p.pending_parameters->extensions.value.rbac_max_authorities_per_permission.valid() ) + p.pending_parameters->extensions.value.rbac_max_authorities_per_permission = p.parameters.extensions.value.rbac_max_authorities_per_permission; + if( !p.pending_parameters->extensions.value.account_roles_max_per_account.valid() ) + p.pending_parameters->extensions.value.account_roles_max_per_account = p.parameters.extensions.value.account_roles_max_per_account; + if( !p.pending_parameters->extensions.value.account_roles_max_lifetime.valid() ) + p.pending_parameters->extensions.value.account_roles_max_lifetime = p.parameters.extensions.value.account_roles_max_lifetime; + if( !p.pending_parameters->extensions.value.son_vesting_amount.valid() ) + p.pending_parameters->extensions.value.son_vesting_amount = p.parameters.extensions.value.son_vesting_amount; + if( !p.pending_parameters->extensions.value.son_vesting_period.valid() ) + p.pending_parameters->extensions.value.son_vesting_period = p.parameters.extensions.value.son_vesting_period; + if( !p.pending_parameters->extensions.value.son_pay_max.valid() ) + p.pending_parameters->extensions.value.son_pay_max = p.parameters.extensions.value.son_pay_max; + if( !p.pending_parameters->extensions.value.son_pay_time.valid() ) + p.pending_parameters->extensions.value.son_pay_time = p.parameters.extensions.value.son_pay_time; + if( !p.pending_parameters->extensions.value.son_deregister_time.valid() ) + p.pending_parameters->extensions.value.son_deregister_time = p.parameters.extensions.value.son_deregister_time; + if( !p.pending_parameters->extensions.value.son_heartbeat_frequency.valid() ) + p.pending_parameters->extensions.value.son_heartbeat_frequency = p.parameters.extensions.value.son_heartbeat_frequency; + if( !p.pending_parameters->extensions.value.son_down_time.valid() ) + p.pending_parameters->extensions.value.son_down_time = p.parameters.extensions.value.son_down_time; + if( !p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations.valid() ) + p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations = p.parameters.extensions.value.son_bitcoin_min_tx_confirmations; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); } @@ -1717,8 +2232,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; }); // Ideally we have to do this after every block but that leads to longer block applicaiton/replay times. // So keep it here as it is not critical. valid_to check ensures - // these custom account auths are not usable. + // these custom account auths and account roles are not usable. clear_expired_custom_account_authorities(*this); + clear_expired_account_roles(*this); // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index f56e3d8b3..06ffdee51 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -40,6 +40,7 @@ #include #include #include +#include using namespace fc; @@ -49,8 +50,13 @@ using namespace graphene::chain; struct get_impacted_account_visitor { flat_set& _impacted; - get_impacted_account_visitor( flat_set& impact ):_impacted(impact) {} - typedef void result_type; + bool _ignore_custom_op_reqd_auths; + + get_impacted_account_visitor( flat_set& impact, bool ignore_custom_operation_required_auths ) + : _impacted( impact ), _ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths ) + {} + + using result_type = void; void operator()( const transfer_operation& op ) { @@ -136,7 +142,7 @@ struct get_impacted_account_visitor { vector other; for( const auto& proposed_op : op.proposed_ops ) - operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other ); + operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other, _ignore_custom_op_reqd_auths ); for( auto& o : other ) add_authority_accounts( _impacted, o ); } @@ -344,22 +350,85 @@ struct get_impacted_account_visitor void operator()( const finalize_offer_operation& op ) { _impacted.insert( op.fee_paying_account ); } + void operator()( const account_role_create_operation& op ){ + _impacted.insert( op.owner ); + } + void operator()( const account_role_update_operation& op ){ + _impacted.insert( op.owner ); + } + void operator()( const account_role_delete_operation& op ){ + _impacted.insert( op.owner ); + } + void operator()( const son_create_operation& op ) { + _impacted.insert( op.owner_account ); + } + void operator()( const son_update_operation& op ) { + _impacted.insert( op.owner_account ); + } + void operator()( const son_deregister_operation& op ) { + _impacted.insert( op.payer); + } + void operator()( const son_heartbeat_operation& op ) { + _impacted.insert( op.owner_account ); + } + void operator()( const son_report_down_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_maintenance_operation& op ) { + _impacted.insert( op.owner_account ); + } + void operator()( const son_wallet_recreate_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_deposit_create_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_deposit_process_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_create_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_withdraw_process_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_address_add_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_address_update_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_address_delete_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_transaction_create_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_transaction_sign_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_transaction_send_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const sidechain_transaction_settle_operation& op ) { + _impacted.insert( op.payer ); + } }; -void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) -{ - get_impacted_account_visitor vtor = get_impacted_account_visitor( result ); +void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result, bool ignore_custom_operation_required_auths ) { + get_impacted_account_visitor vtor = get_impacted_account_visitor( result, ignore_custom_operation_required_auths ); op.visit( vtor ); } -void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set& result ) -{ +void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set& result, bool ignore_custom_operation_required_auths ) { for( const auto& op : tx.operations ) - operation_get_impacted_accounts( op, result ); + operation_get_impacted_accounts( op, result, ignore_custom_operation_required_auths ); } -void get_relevant_accounts( const object* obj, flat_set& accounts ) -{ +void get_relevant_accounts( const object* obj, flat_set& accounts, bool ignore_custom_operation_required_auths ) { if( obj->id.space() == protocol_ids ) { switch( (object_type)obj->id.type() ) @@ -406,12 +475,14 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case proposal_object_type:{ const auto& aobj = dynamic_cast(obj); assert( aobj != nullptr ); - transaction_get_impacted_accounts( aobj->proposed_transaction, accounts ); + transaction_get_impacted_accounts( aobj->proposed_transaction, accounts, + ignore_custom_operation_required_auths); break; } case operation_history_object_type:{ const auto& aobj = dynamic_cast(obj); assert( aobj != nullptr ); - operation_get_impacted_accounts( aobj->op, accounts ); + operation_get_impacted_accounts( aobj->op, accounts, + ignore_custom_operation_required_auths); break; } case withdraw_permission_object_type:{ const auto& aobj = dynamic_cast(obj); @@ -432,6 +503,30 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; + } case account_role_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->owner ); + accounts.insert( aobj->whitelisted_accounts.begin(), aobj->whitelisted_accounts.end() ); + break; + } case son_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->son_account ); + break; + } case son_wallet_object_type:{ + break; + } case son_wallet_deposit_object_type:{ + break; + } case son_wallet_withdraw_object_type:{ + break; + } case sidechain_address_object_type:{ + const auto& aobj = dynamic_cast(obj); + assert( aobj != nullptr ); + accounts.insert( aobj->sidechain_address_account ); + break; + } case sidechain_transaction_object_type:{ + break; } } } @@ -462,7 +557,8 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case impl_transaction_object_type:{ const auto& aobj = dynamic_cast(obj); assert( aobj != nullptr ); - transaction_get_impacted_accounts( aobj->trx, accounts ); + transaction_get_impacted_accounts( aobj->trx, accounts, + ignore_custom_operation_required_auths); break; } case impl_blinded_balance_object_type:{ const auto& aobj = dynamic_cast(obj); @@ -507,6 +603,7 @@ void database::notify_changed_objects() if( _undo_db.enabled() ) { const auto& head_undo = _undo_db.head(); + auto chain_time = head_block_time(); // New if( !new_objects.empty() ) @@ -518,7 +615,8 @@ void database::notify_changed_objects() new_ids.push_back(item); auto obj = find_object(item); if(obj != nullptr) - get_relevant_accounts(obj, new_accounts_impacted); + get_relevant_accounts(obj, new_accounts_impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); } GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) @@ -532,7 +630,8 @@ void database::notify_changed_objects() for( const auto& item : head_undo.old_values ) { changed_ids.push_back(item.first); - get_relevant_accounts(item.second.get(), changed_accounts_impacted); + get_relevant_accounts(item.second.get(), changed_accounts_impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); } GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) @@ -549,7 +648,8 @@ void database::notify_changed_objects() removed_ids.emplace_back( item.first ); auto obj = item.second.get(); removed.emplace_back( obj ); - get_relevant_accounts(obj, removed_accounts_impacted); + get_relevant_accounts(obj, removed_accounts_impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); } GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 96f9e3abb..2e896070d 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -225,6 +225,7 @@ void database::clear_expired_proposals() elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}", ("proposal", proposal)("error", e.to_detail_string())); } + remove_son_proposal(proposal); remove(proposal); } } @@ -730,5 +731,50 @@ void database::finalize_expired_offers(){ } }); } FC_CAPTURE_AND_RETHROW()} +void database::remove_son_proposal( const proposal_object& proposal ) +{ try { + if( proposal.proposed_transaction.operations.size() == 1 && + ( proposal.proposed_transaction.operations.back().which() == operation::tag::value || + proposal.proposed_transaction.operations.back().which() == operation::tag::value) ) + { + const auto& son_proposal_idx = get_index_type().indices().get(); + auto son_proposal_itr = son_proposal_idx.find( proposal.id ); + if( son_proposal_itr == son_proposal_idx.end() ) { + return; + } + remove( *son_proposal_itr ); + } +} FC_CAPTURE_AND_RETHROW( (proposal) ) } + +void database::remove_inactive_son_down_proposals( const vector& son_ids_to_remove ) +{ + const auto& son_proposal_idx = get_index_type().indices().get< by_id >(); + std::vector proposals_to_remove; + + for( auto& son_proposal : son_proposal_idx ) + { + if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal) + { + auto it = std::find(son_ids_to_remove.begin(), son_ids_to_remove.end(), son_proposal.son_id); + if (it != son_ids_to_remove.end()) + { + ilog( "Removing inactive proposal ${p} for son ${s}", ("p", son_proposal.proposal_id) ("s",son_proposal.son_id)); + proposals_to_remove.push_back(son_proposal.proposal_id); + } + } + } + + for( auto& proposal_id : proposals_to_remove ) + { + const auto& proposal_obj = proposal_id(*this); + remove_son_proposal(proposal_obj); + remove(proposal_obj); + } +} + +void database::remove_inactive_son_proposals( const vector& son_ids_to_remove ) +{ + remove_inactive_son_down_proposals( son_ids_to_remove ); +} } } diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 762157bce..084c8e1d3 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { @@ -72,6 +74,47 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const return wid; } +son_id_type database::get_scheduled_son( uint32_t slot_num )const +{ + son_id_type sid; + const global_property_object& gpo = get_global_properties(); + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) + { + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + const son_schedule_object& sso = son_schedule_id_type()(*this); + uint64_t current_aslot = dpo.current_aslot + slot_num; + return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; + } + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && + slot_num != 0 ) + { + const son_schedule_object& sso = son_schedule_id_type()(*this); + // ask the near scheduler who goes in the given slot + bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); + if(! slot_is_near) + { + // if the near scheduler doesn't know, we have to extend it to + // a far scheduler. + // n.b. instantiating it is slow, but block gaps long enough to + // need it are likely pretty rare. + + witness_scheduler_rng far_rng(sso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV); + + far_future_son_scheduler far_scheduler = + far_future_son_scheduler(sso.scheduler, far_rng); + if(!far_scheduler.get_slot(slot_num-1, sid)) + { + // no scheduled son -- somebody set up us the bomb + // n.b. this code path is impossible, the present + // implementation of far_future_son_scheduler + // returns true unconditionally + assert( false ); + } + } + } + return sid; +} + fc::time_point_sec database::get_slot_time(uint32_t slot_num)const { if( slot_num == 0 ) @@ -146,6 +189,41 @@ void database::update_witness_schedule() } } +void database::update_son_schedule() +{ + const son_schedule_object& sso = son_schedule_id_type()(*this); + const global_property_object& gpo = get_global_properties(); + + if( head_block_num() % gpo.active_sons.size() == 0 ) + { + modify( sso, [&]( son_schedule_object& _sso ) + { + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); + + for( const son_info& w : gpo.active_sons ) + _sso.current_shuffled_sons.push_back( w.son_id ); + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) + { + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; + + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k%jmax; + std::swap( _sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j] ); + } + }); + } +} + vector database::get_near_witness_schedule()const { const witness_schedule_object& wso = get_witness_schedule_object(); @@ -226,6 +304,71 @@ void database::update_witness_schedule(const signed_block& next_block) idump( ( double(total_time/1000000.0)/calls) ); } +void database::update_son_schedule(const signed_block& next_block) +{ + auto start = fc::time_point::now(); + const global_property_object& gpo = get_global_properties(); + const son_schedule_object& sso = get(son_schedule_id_type()); + uint32_t schedule_needs_filled = gpo.active_sons.size(); + uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); + + // We shouldn't be able to generate _pending_block with timestamp + // in the past, and incoming blocks from the network with timestamp + // in the past shouldn't be able to make it this far without + // triggering FC_ASSERT elsewhere + + assert( schedule_slot > 0 ); + + son_id_type first_son; + bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son ); + + son_id_type son; + + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + + assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); + assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); + + modify(sso, [&](son_schedule_object& _sso) + { + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); + + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) + { + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; + } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); + auto end = fc::time_point::now(); + static uint64_t total_time = 0; + static uint64_t calls = 0; + total_time += (end - start).count(); + if( ++calls % 1000 == 0 ) + idump( ( double(total_time/1000000.0)/calls) ); +} + uint32_t database::update_witness_missed_blocks( const signed_block& b ) { uint32_t missed_blocks = get_slot_at_time( b.timestamp ); diff --git a/libraries/chain/get_config.cpp b/libraries/chain/get_config.cpp index c961b950d..245d65984 100644 --- a/libraries/chain/get_config.cpp +++ b/libraries/chain/get_config.cpp @@ -108,6 +108,11 @@ fc::variant_object get_config() result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); + result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS); return result; } diff --git a/libraries/chain/hardfork.d/5050-1.hf b/libraries/chain/hardfork.d/5050-1.hf index eb9aa74dd..bd621f269 100644 --- a/libraries/chain/hardfork.d/5050-1.hf +++ b/libraries/chain/hardfork.d/5050-1.hf @@ -1,4 +1,4 @@ -// 5050_1 HARDFORK Thursday, 15 April 2020 20:00:00 GMT +// 5050_1 HARDFORK Wednesday, 15 April 2020 20:00:00 GMT #ifndef HARDFORK_5050_1_TIME #define HARDFORK_5050_1_TIME (fc::time_point_sec( 1586980800 )) #endif diff --git a/libraries/chain/hardfork.d/CORE_210.hf b/libraries/chain/hardfork.d/CORE_210.hf new file mode 100644 index 000000000..0f2852747 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_210.hf @@ -0,0 +1,6 @@ +// #210 Check authorities on custom_operation +#ifndef HARDFORK_CORE_210_TIME +#define HARDFORK_CORE_210_TIME (fc::time_point_sec(1893456000)) // Jan 1 00:00:00 2030 (Not yet scheduled) +// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored. +#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME) +#endif diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf new file mode 100644 index 000000000..b3947a750 --- /dev/null +++ b/libraries/chain/hardfork.d/SON.hf @@ -0,0 +1,7 @@ +// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800 +// SON HARDFORK Monday, March 30, 2020 3:00:00 PM - 1585573200 +// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791 +#ifndef HARDFORK_SON_TIME +#include +#define HARDFORK_SON_TIME (fc::time_point_sec( 1585573200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_role_evaluator.hpp b/libraries/chain/include/graphene/chain/account_role_evaluator.hpp new file mode 100644 index 000000000..29c4ada60 --- /dev/null +++ b/libraries/chain/include/graphene/chain/account_role_evaluator.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +namespace graphene { namespace chain { + + class account_role_create_evaluator : public evaluator + { + public: + typedef account_role_create_operation operation_type; + void_result do_evaluate( const account_role_create_operation& o ); + object_id_type do_apply( const account_role_create_operation& o ); + }; + + class account_role_update_evaluator : public evaluator + { + public: + typedef account_role_update_operation operation_type; + void_result do_evaluate( const account_role_update_operation& o ); + void_result do_apply( const account_role_update_operation& o ); + }; + + class account_role_delete_evaluator : public evaluator + { + public: + typedef account_role_delete_operation operation_type; + void_result do_evaluate( const account_role_delete_operation& o ); + void_result do_apply( const account_role_delete_operation& o ); + }; + +} } // graphene::chain + diff --git a/libraries/chain/include/graphene/chain/account_role_object.hpp b/libraries/chain/include/graphene/chain/account_role_object.hpp new file mode 100644 index 000000000..9455f4759 --- /dev/null +++ b/libraries/chain/include/graphene/chain/account_role_object.hpp @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include + +namespace graphene +{ + namespace chain + { + using namespace graphene::db; + + class account_role_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = account_role_type; + + account_id_type owner; + std::string name; + std::string metadata; + flat_set allowed_operations; + flat_set whitelisted_accounts; + time_point_sec valid_to; + }; + + struct by_owner; + struct by_expiration; + using account_role_multi_index_type = multi_index_container< + account_role_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_unique< tag, + composite_key, + member> + > + > + >; + using account_role_index = generic_index; + } // namespace chain +} // namespace graphene + +FC_REFLECT_DERIVED(graphene::chain::account_role_object, (graphene::db::object), + (owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/budget_record_object.hpp b/libraries/chain/include/graphene/chain/budget_record_object.hpp index e3f6d06e4..007d46a75 100644 --- a/libraries/chain/include/graphene/chain/budget_record_object.hpp +++ b/libraries/chain/include/graphene/chain/budget_record_object.hpp @@ -45,9 +45,11 @@ struct budget_record // sinks of budget, should sum up to total_budget share_type witness_budget = 0; share_type worker_budget = 0; + share_type son_budget = 0; // unused budget share_type leftover_worker_funds = 0; + share_type leftover_son_funds = 0; // change in supply due to budget operations share_type supply_delta = 0; diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 42c2fd137..89d3cce33 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -90,8 +90,10 @@ #define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT (11) #define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT (11) +#define GRAPHENE_DEFAULT_MIN_SON_COUNT (5) #define GRAPHENE_DEFAULT_MAX_WITNESSES (1001) // SHOULD BE ODD #define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD +#define GRAPHENE_DEFAULT_MAX_SONS (15) #define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks #define GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks #define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT) @@ -227,6 +229,14 @@ #define TOURNAMENT_MAX_WHITELIST_LENGTH 1000 #define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month #define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week +#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY +#define SON_VESTING_PERIOD (60*60*24*2) // 2 days +#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds +#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds +#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds +#define SON_BITCOIN_MIN_TX_CONFIRMATIONS (1) +#define SON_PAY_TIME (60*60*24) // 1 day +#define SON_PAY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200)) #define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT) #define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0)) #define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000 @@ -243,4 +253,7 @@ #define NFT_TOKEN_MIN_LENGTH 3 #define NFT_TOKEN_MAX_LENGTH 15 -#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH \ No newline at end of file +#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH + +#define ACCOUNT_ROLES_MAX_PER_ACCOUNT 20 // Max 20 roles can be created by a resource owner +#define ACCOUNT_ROLES_MAX_LIFETIME 365*24*60*60 // 1 Year diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 8ecb4b911..05927399b 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,6 +40,8 @@ #include +#include + #include #include @@ -240,6 +242,22 @@ namespace graphene { namespace chain { */ witness_id_type get_scheduled_witness(uint32_t slot_num)const; + /** + * @brief Get the son scheduled for block production in a slot. + * + * slot_num always corresponds to a time in the future. + * + * If slot_num == 1, returns the next scheduled son. + * If slot_num == 2, returns the next scheduled son after + * 1 block gap. + * + * Use the get_slot_time() and get_slot_at_time() functions + * to convert between slot_num and timestamp. + * + * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS + */ + son_id_type get_scheduled_son(uint32_t slot_num)const; + /** * Get the time at which the given slot occurs. * @@ -263,6 +281,8 @@ namespace graphene { namespace chain { vector get_near_witness_schedule()const; void update_witness_schedule(); void update_witness_schedule(const signed_block& next_block); + void update_son_schedule(); + void update_son_schedule(const signed_block& next_block); void check_lottery_end_by_participants( asset_id_type asset_id ); void check_ending_lotteries(); @@ -283,6 +303,14 @@ namespace graphene { namespace chain { uint64_t get_random_bits( uint64_t bound ); const witness_schedule_object& get_witness_schedule_object()const; bool item_locked(const nft_id_type& item)const; + bool account_role_valid(const account_role_object& aro, account_id_type account, optional op_type = optional()) const; + std::set get_sons_being_deregistered(); + std::set get_sons_being_reported_down(); + std::set get_sons_to_be_deregistered(); + fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); + signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); + bool is_son_dereg_valid( son_id_type son_id ); + bool is_son_active( son_id_type son_id ); time_point_sec head_block_time()const; uint32_t head_block_num()const; @@ -531,9 +559,18 @@ namespace graphene { namespace chain { void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const; void process_budget(); void pay_workers( share_type& budget ); + void pay_sons(); + void perform_son_tasks(); void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); + void update_son_metrics( const vector& curr_active_sons ); + void update_active_sons(); + void remove_son_proposal( const proposal_object& proposal ); + void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); + void remove_inactive_son_proposals( const vector& son_ids_to_remove ); + void update_son_statuses( const vector& cur_active_sons, const vector& new_active_sons ); + void update_son_wallet( const vector& new_active_sons ); void update_worker_votes(); public: @@ -575,6 +612,7 @@ namespace graphene { namespace chain { vector _vote_tally_buffer; vector _witness_count_histogram_buffer; vector _committee_count_histogram_buffer; + vector _son_count_histogram_buffer; uint64_t _total_voting_stake; flat_map _checkpoints; diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 0ed401a13..0f95a6e36 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace graphene { namespace chain { @@ -51,6 +52,7 @@ namespace graphene { namespace chain { uint32_t next_available_vote_id = 0; vector active_committee_members; // updated once per maintenance interval flat_set active_witnesses; // updated once per maintenance interval + vector active_sons; // updated once per maintenance interval // n.b. witness scheduling is done by witness_schedule object }; @@ -77,6 +79,9 @@ namespace graphene { namespace chain { time_point_sec next_maintenance_time; time_point_sec last_budget_time; share_type witness_budget; + //Last SON Payout time, it can be different to the maintenance interval time + time_point_sec last_son_payout_time = HARDFORK_SON_TIME; + share_type son_budget = 0; uint32_t accounts_registered_this_interval = 0; /** * Every time a block is missed this increases by @@ -133,6 +138,8 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene:: (next_maintenance_time) (last_budget_time) (witness_budget) + (last_son_payout_time) + (son_budget) (accounts_registered_this_interval) (recently_missed_count) (current_aslot) @@ -147,6 +154,7 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje (next_available_vote_id) (active_committee_members) (active_witnesses) + (active_sons) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::dynamic_global_property_object ) diff --git a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp index 6e18c079d..f71288895 100644 --- a/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/immutable_chain_parameters.hpp @@ -32,6 +32,7 @@ struct immutable_chain_parameters { uint16_t min_committee_member_count = GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT; uint16_t min_witness_count = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT; + uint16_t min_son_count = GRAPHENE_DEFAULT_MIN_SON_COUNT; uint32_t num_special_accounts = 0; uint32_t num_special_assets = 0; }; @@ -41,6 +42,7 @@ struct immutable_chain_parameters FC_REFLECT( graphene::chain::immutable_chain_parameters, (min_committee_member_count) (min_witness_count) + (min_son_count) (num_special_accounts) (num_special_assets) ) diff --git a/libraries/chain/include/graphene/chain/impacted.hpp b/libraries/chain/include/graphene/chain/impacted.hpp index 2a22cbd12..3e070ce6c 100644 --- a/libraries/chain/include/graphene/chain/impacted.hpp +++ b/libraries/chain/include/graphene/chain/impacted.hpp @@ -30,13 +30,12 @@ namespace graphene { namespace chain { -void operation_get_impacted_accounts( - const graphene::chain::operation& op, - fc::flat_set& result ); +void operation_get_impacted_accounts( const graphene::chain::operation& op, + fc::flat_set& result, + bool ignore_custom_operation_required_auths ); -void transaction_get_impacted_accounts( - const graphene::chain::transaction& tx, - fc::flat_set& result - ); +void transaction_get_impacted_accounts( const graphene::chain::transaction& tx, + fc::flat_set& result, + bool ignore_custom_operation_required_auths ); -} } // graphene::app \ No newline at end of file +} } // graphene::app diff --git a/libraries/chain/include/graphene/chain/nft_object.hpp b/libraries/chain/include/graphene/chain/nft_object.hpp index 1994a92e3..6a1508527 100644 --- a/libraries/chain/include/graphene/chain/nft_object.hpp +++ b/libraries/chain/include/graphene/chain/nft_object.hpp @@ -20,6 +20,7 @@ namespace graphene { namespace chain { optional revenue_split; bool is_transferable = false; bool is_sellable = true; + optional account_role; }; class nft_object : public abstract_object @@ -95,7 +96,8 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object) (revenue_partner) (revenue_split) (is_transferable) - (is_sellable) ) + (is_sellable) + (account_role) ) FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object), (nft_metadata_id) diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index bf6fc5472..3e8ee15e0 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -30,6 +30,22 @@ namespace graphene { namespace chain { + class son_hardfork_visitor + { + public: + typedef void result_type; + database& db; + proposal_id_type prop_id; + + son_hardfork_visitor( database& _db, const proposal_id_type& _prop_id ) : db( _db ), prop_id( _prop_id ) {} + + template + void operator()( const T &v ) const {} + + void operator()( const son_deregister_operation &v ); + void operator()( const son_report_down_operation &v ); + }; + class proposal_create_evaluator : public evaluator { public: diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 36926480d..50ccb8aef 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -52,6 +52,9 @@ namespace graphene { namespace chain { /// The number of active committee members this account votes the blockchain should appoint /// Must not exceed the actual number of committee members voted for in @ref votes uint16_t num_committee = 0; + /// The number of active son members this account votes the blockchain should appoint + /// Must not exceed the actual number of son members voted for in @ref votes + uint16_t num_son = 0; /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// account's balance of core asset. flat_set votes; diff --git a/libraries/chain/include/graphene/chain/protocol/account_role.hpp b/libraries/chain/include/graphene/chain/protocol/account_role.hpp new file mode 100644 index 000000000..6a532cb7d --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/account_role.hpp @@ -0,0 +1,82 @@ +#pragma once +#include +#include + +namespace graphene +{ + namespace chain + { + + struct account_role_create_operation : public base_operation + { + struct fee_parameters_type + { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; + + account_id_type owner; + std::string name; + std::string metadata; + flat_set allowed_operations; + flat_set whitelisted_accounts; + time_point_sec valid_to; + extensions_type extensions; + + account_id_type fee_payer() const { return owner; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const; + }; + + struct account_role_update_operation : public base_operation + { + struct fee_parameters_type + { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; + + account_id_type owner; + account_role_id_type account_role_id; + optional name; + optional metadata; + flat_set allowed_operations_to_add; + flat_set allowed_operations_to_remove; + flat_set accounts_to_add; + flat_set accounts_to_remove; + optional valid_to; + extensions_type extensions; + + account_id_type fee_payer() const { return owner; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const; + }; + + struct account_role_delete_operation : public base_operation + { + struct fee_parameters_type + { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + asset fee; + account_id_type owner; + account_role_id_type account_role_id; + extensions_type extensions; + + account_id_type fee_payer() const { return owner; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; } + }; + } // namespace chain +} // namespace graphene + +FC_REFLECT(graphene::chain::account_role_create_operation::fee_parameters_type, (fee)(price_per_kbyte)) +FC_REFLECT(graphene::chain::account_role_update_operation::fee_parameters_type, (fee)(price_per_kbyte)) +FC_REFLECT(graphene::chain::account_role_delete_operation::fee_parameters_type, (fee)) + +FC_REFLECT(graphene::chain::account_role_create_operation, (fee)(owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to)(extensions)) +FC_REFLECT(graphene::chain::account_role_update_operation, (fee)(owner)(account_role_id)(name)(metadata)(allowed_operations_to_add)(allowed_operations_to_remove)(accounts_to_add)(accounts_to_remove)(valid_to)(extensions)) +FC_REFLECT(graphene::chain::account_role_delete_operation, (fee)(owner)(account_role_id)(owner)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index ddac31154..88d5dccf9 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -27,6 +27,7 @@ #include #include +#include #include <../hardfork.d/GPOS.hf> #include @@ -52,6 +53,21 @@ namespace graphene { namespace chain { optional < uint16_t > rbac_max_permissions_per_account = RBAC_MAX_PERMISSIONS_PER_ACCOUNT; optional < uint32_t > rbac_max_account_authority_lifetime = RBAC_MAX_ACCOUNT_AUTHORITY_LIFETIME; optional < uint16_t > rbac_max_authorities_per_permission = RBAC_MAX_AUTHS_PER_PERMISSION; + /* Account Roles - Permissions Parameters */ + optional < uint16_t > account_roles_max_per_account = ACCOUNT_ROLES_MAX_PER_ACCOUNT; + optional < uint32_t > account_roles_max_lifetime = ACCOUNT_ROLES_MAX_LIFETIME; + + optional < uint32_t > son_vesting_amount = SON_VESTING_AMOUNT; + optional < uint32_t > son_vesting_period = SON_VESTING_PERIOD; + optional < uint32_t > son_pay_max = SON_PAY_MAX; + optional < uint32_t > son_pay_time = SON_PAY_TIME; + optional < uint32_t > son_deregister_time = SON_DEREGISTER_TIME; + optional < uint32_t > son_heartbeat_frequency = SON_HEARTBEAT_FREQUENCY; + optional < uint32_t > son_down_time = SON_DOWN_TIME; + optional < uint16_t > son_bitcoin_min_tx_confirmations = SON_BITCOIN_MIN_TX_CONFIRMATIONS; + + optional < account_id_type > son_account; + optional < asset_id_type > btc_asset; }; struct chain_parameters @@ -70,6 +86,7 @@ namespace graphene { namespace chain { uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members + uint16_t maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have uint16_t reserve_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network @@ -101,7 +118,7 @@ namespace graphene { namespace chain { uint32_t maximum_tournament_start_time_in_future = TOURNAMENT_MAX_START_TIME_IN_FUTURE; uint32_t maximum_tournament_start_delay = TOURNAMENT_MAX_START_DELAY; uint16_t maximum_tournament_number_of_wins = TOURNAMENT_MAX_NUMBER_OF_WINS; - + extension extensions; /** defined in fee_schedule.cpp */ @@ -152,6 +169,42 @@ namespace graphene { namespace chain { inline uint16_t rbac_max_authorities_per_permission()const { return extensions.value.rbac_max_authorities_per_permission.valid() ? *extensions.value.rbac_max_authorities_per_permission : RBAC_MAX_AUTHS_PER_PERMISSION; } + inline uint16_t account_roles_max_per_account()const { + return extensions.value.account_roles_max_per_account.valid() ? *extensions.value.account_roles_max_per_account : ACCOUNT_ROLES_MAX_PER_ACCOUNT; + } + inline uint32_t account_roles_max_lifetime()const { + return extensions.value.account_roles_max_lifetime.valid() ? *extensions.value.account_roles_max_lifetime : ACCOUNT_ROLES_MAX_LIFETIME; + } + inline uint32_t son_vesting_amount()const { + return extensions.value.son_vesting_amount.valid() ? *extensions.value.son_vesting_amount : SON_VESTING_AMOUNT; /// current period start date + } + inline uint32_t son_vesting_period()const { + return extensions.value.son_vesting_period.valid() ? *extensions.value.son_vesting_period : SON_VESTING_PERIOD; /// current period start date + } + inline uint16_t son_pay_max()const { + return extensions.value.son_pay_max.valid() ? *extensions.value.son_pay_max : SON_PAY_MAX; + } + inline uint16_t son_pay_time()const { + return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME; + } + inline uint16_t son_deregister_time()const { + return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME; + } + inline uint16_t son_heartbeat_frequency()const { + return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY; + } + inline uint16_t son_down_time()const { + return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; + } + inline uint16_t son_bitcoin_min_tx_confirmations()const { + return extensions.value.son_bitcoin_min_tx_confirmations.valid() ? *extensions.value.son_bitcoin_min_tx_confirmations : SON_BITCOIN_MIN_TX_CONFIRMATIONS; + } + inline account_id_type son_account() const { + return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT; + } + inline asset_id_type btc_asset() const { + return extensions.value.btc_asset.valid() ? *extensions.value.btc_asset : asset_id_type(); + } }; } } // graphene::chain @@ -172,6 +225,18 @@ FC_REFLECT( graphene::chain::parameter_extension, (rbac_max_permissions_per_account) (rbac_max_account_authority_lifetime) (rbac_max_authorities_per_permission) + (account_roles_max_per_account) + (account_roles_max_lifetime) + (son_vesting_amount) + (son_vesting_period) + (son_pay_max) + (son_pay_time) + (son_deregister_time) + (son_heartbeat_frequency) + (son_down_time) + (son_bitcoin_min_tx_confirmations) + (son_account) + (btc_asset) ) FC_REFLECT( graphene::chain::chain_parameters, @@ -188,6 +253,7 @@ FC_REFLECT( graphene::chain::chain_parameters, (maximum_asset_feed_publishers) (maximum_witness_count) (maximum_committee_count) + (maximum_son_count) (maximum_authority_membership) (reserve_percent_of_fee) (network_percent_of_fee) diff --git a/libraries/chain/include/graphene/chain/protocol/custom.hpp b/libraries/chain/include/graphene/chain/protocol/custom.hpp index 5596aaad1..6c3af9262 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom.hpp @@ -50,6 +50,9 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return payer; } void validate()const; share_type calculate_fee(const fee_parameters_type& k)const; + void get_required_active_authorities( flat_set& auths )const { + auths.insert( required_auths.begin(), required_auths.end() ); + } }; } } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp index f5f8c1cdf..b3fe29037 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom_account_authority.hpp @@ -20,6 +20,7 @@ struct custom_account_authority_create_operation : public base_operation time_point_sec valid_from; time_point_sec valid_to; account_id_type owner_account; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -38,6 +39,7 @@ struct custom_account_authority_update_operation : public base_operation optional new_valid_from; optional new_valid_to; account_id_type owner_account; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -54,6 +56,7 @@ struct custom_account_authority_delete_operation : public base_operation asset fee; custom_account_authority_id_type auth_id; account_id_type owner_account; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -64,10 +67,10 @@ struct custom_account_authority_delete_operation : public base_operation } // namespace graphene FC_REFLECT(graphene::chain::custom_account_authority_create_operation::fee_parameters_type, (fee)(price_per_kbyte)) -FC_REFLECT(graphene::chain::custom_account_authority_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account)) +FC_REFLECT(graphene::chain::custom_account_authority_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account)(extensions)) FC_REFLECT(graphene::chain::custom_account_authority_update_operation::fee_parameters_type, (fee)) -FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account)) +FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account)(extensions)) FC_REFLECT(graphene::chain::custom_account_authority_delete_operation::fee_parameters_type, (fee)) -FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account)) \ No newline at end of file +FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp index 8093ef07f..d61384c20 100644 --- a/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp +++ b/libraries/chain/include/graphene/chain/protocol/custom_permission.hpp @@ -18,6 +18,7 @@ struct custom_permission_create_operation : public base_operation account_id_type owner_account; string permission_name; authority auth; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -35,6 +36,7 @@ struct custom_permission_update_operation : public base_operation custom_permission_id_type permission_id; optional new_auth; account_id_type owner_account; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -51,6 +53,7 @@ struct custom_permission_delete_operation : public base_operation asset fee; custom_permission_id_type permission_id; account_id_type owner_account; + extensions_type extensions; account_id_type fee_payer() const { return owner_account; } void validate() const; @@ -61,10 +64,10 @@ struct custom_permission_delete_operation : public base_operation } // namespace graphene FC_REFLECT(graphene::chain::custom_permission_create_operation::fee_parameters_type, (fee)(price_per_kbyte)) -FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth)) +FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth)(extensions)) FC_REFLECT(graphene::chain::custom_permission_update_operation::fee_parameters_type, (fee)) -FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account)) +FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account)(extensions)) FC_REFLECT(graphene::chain::custom_permission_delete_operation::fee_parameters_type, (fee)) -FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account)) \ No newline at end of file +FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp b/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp index 41e77b064..4facf51aa 100644 --- a/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/nft_ops.hpp @@ -21,6 +21,9 @@ namespace graphene { namespace chain { optional revenue_split; bool is_transferable = false; bool is_sellable = true; + // Accounts Role + optional account_role; + extensions_type extensions; account_id_type fee_payer()const { return owner; } void validate() const; @@ -29,7 +32,11 @@ namespace graphene { namespace chain { struct nft_metadata_update_operation : public base_operation { - struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + struct fee_parameters_type + { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; + }; asset fee; account_id_type owner; @@ -41,6 +48,9 @@ namespace graphene { namespace chain { optional revenue_split; optional is_transferable; optional is_sellable; + // Accounts Role + optional account_role; + extensions_type extensions; account_id_type fee_payer()const { return owner; } void validate() const; @@ -63,6 +73,7 @@ namespace graphene { namespace chain { account_id_type approved; vector approved_operators; std::string token_uri; + extensions_type extensions; account_id_type fee_payer()const { return payer; } void validate() const; @@ -84,6 +95,7 @@ namespace graphene { namespace chain { account_id_type to; nft_id_type token_id; string data; + extensions_type extensions; account_id_type fee_payer()const { return operator_; } share_type calculate_fee(const fee_parameters_type &k) const; @@ -98,6 +110,7 @@ namespace graphene { namespace chain { account_id_type approved; nft_id_type token_id; + extensions_type extensions; account_id_type fee_payer()const { return operator_; } share_type calculate_fee(const fee_parameters_type &k) const; @@ -112,6 +125,7 @@ namespace graphene { namespace chain { account_id_type operator_; bool approved; + extensions_type extensions; account_id_type fee_payer()const { return owner; } share_type calculate_fee(const fee_parameters_type &k) const; @@ -126,10 +140,9 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) ) -FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) ) -FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) ) -FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) ) -FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) ) -FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) ) - +FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) ) +FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) ) +FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) ) +FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) ) +FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) (extensions) ) +FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) (extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 1285d3531..1a1222a54 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -49,6 +49,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include namespace graphene { namespace chain { @@ -155,7 +162,29 @@ namespace graphene { namespace chain { nft_mint_operation, nft_safe_transfer_from_operation, nft_approve_operation, - nft_set_approval_for_all_operation + nft_set_approval_for_all_operation, + account_role_create_operation, + account_role_update_operation, + account_role_delete_operation, + son_create_operation, + son_update_operation, + son_deregister_operation, + son_heartbeat_operation, + son_report_down_operation, + son_maintenance_operation, + son_wallet_recreate_operation, + son_wallet_update_operation, + son_wallet_deposit_create_operation, + son_wallet_deposit_process_operation, + son_wallet_withdraw_create_operation, + son_wallet_withdraw_process_operation, + sidechain_address_add_operation, + sidechain_address_update_operation, + sidechain_address_delete_operation, + sidechain_transaction_create_operation, + sidechain_transaction_sign_operation, + sidechain_transaction_send_operation, + sidechain_transaction_settle_operation > operation; /// @} // operations group @@ -166,10 +195,11 @@ namespace graphene { namespace chain { * * @return a set of required authorities for @ref op */ - void operation_get_required_authorities( const operation& op, + void operation_get_required_authorities( const operation& op, flat_set& active, flat_set& owner, - vector& other ); + vector& other, + bool ignore_custom_operation_required_auths ); void operation_validate( const operation& op ); diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp new file mode 100644 index 000000000..a5e12a03a --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp @@ -0,0 +1,80 @@ +#pragma once +#include + +#include + +namespace graphene { namespace chain { + + struct sidechain_address_add_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + account_id_type sidechain_address_account; + sidechain_type sidechain; + string deposit_public_key; + string deposit_address; + string deposit_address_data; + string withdraw_public_key; + string withdraw_address; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct sidechain_address_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_address_id_type sidechain_address_id; + account_id_type sidechain_address_account; + sidechain_type sidechain; + optional deposit_public_key; + optional deposit_address; + optional deposit_address_data; + optional withdraw_public_key; + optional withdraw_address; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct sidechain_address_delete_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_address_id_type sidechain_address_id; + account_id_type sidechain_address_account; + sidechain_type sidechain; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee)(payer) + (sidechain_address_account)(sidechain) + (deposit_public_key)(deposit_address)(deposit_address_data) + (withdraw_public_key)(withdraw_address) ) + +FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee)(payer) + (sidechain_address_id) + (sidechain_address_account)(sidechain) + (deposit_public_key)(deposit_address)(deposit_address_data) + (withdraw_public_key)(withdraw_address) ) + +FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee)(payer) + (sidechain_address_id) + (sidechain_address_account)(sidechain) ) diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp new file mode 100644 index 000000000..01a4ce741 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include + +namespace graphene { namespace chain { + + struct sidechain_transaction_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector signers; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct sidechain_transaction_sign_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type signer; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + std::string signature; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + + struct sidechain_transaction_send_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + std::string sidechain_transaction; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + + struct sidechain_transaction_settle_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer) + (sidechain) + (object_id) + (transaction) + (signers) ) + +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer) + (sidechain_transaction_id) + (signature) ) + +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer) + (sidechain_transaction_id) + (sidechain_transaction) ) + +FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation, (fee)(payer) + (sidechain_transaction_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp new file mode 100644 index 000000000..10a754123 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -0,0 +1,119 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + + struct son_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type owner_account; + std::string url; + vesting_balance_id_type deposit; + public_key_type signing_key; + flat_map sidechain_public_keys; + vesting_balance_id_type pay_vb; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type owner_account; + optional new_url; + optional new_deposit; + optional new_signing_key; + optional> new_sidechain_public_keys; + optional new_pay_vb; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_deregister_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type payer; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_heartbeat_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type owner_account; + time_point_sec ts; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + + struct son_report_down_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type payer; + time_point_sec down_ts; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + enum class son_maintenance_request_type + { + request_maintenance, + cancel_request_maintenance + }; + + struct son_maintenance_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + son_id_type son_id; + account_id_type owner_account; + son_maintenance_request_type request_type = son_maintenance_request_type::request_maintenance; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)(sidechain_public_keys) + (pay_vb) ) + +FC_REFLECT(graphene::chain::son_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(new_url)(new_deposit) + (new_signing_key)(new_sidechain_public_keys)(new_pay_vb) ) + +FC_REFLECT(graphene::chain::son_deregister_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_deregister_operation, (fee)(son_id)(payer) ) + +FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) ) + +FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) ) + +FC_REFLECT_ENUM( graphene::chain::son_maintenance_request_type, (request_maintenance)(cancel_request_maintenance) ) +FC_REFLECT(graphene::chain::son_maintenance_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account)(request_type) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp new file mode 100644 index 000000000..5194bed2c --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + + struct son_wallet_recreate_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + vector sons; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_id_type son_wallet_id; + sidechain_type sidechain; + string address; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp new file mode 100644 index 000000000..1b6a02c77 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp @@ -0,0 +1,57 @@ +#pragma once +#include +#include + +#include + +namespace graphene { namespace chain { + + struct son_wallet_deposit_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_id_type son_id; + fc::time_point_sec timestamp; + uint32_t block_num; + sidechain_type sidechain; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + std::string sidechain_currency; + fc::safe sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; + chain::asset peerplays_asset; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_deposit_process_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_deposit_id_type son_wallet_deposit_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer) + (son_id) (timestamp) (block_num) (sidechain) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) + (peerplays_from) (peerplays_to) (peerplays_asset) ) + +FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer) + (son_wallet_deposit_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp new file mode 100644 index 000000000..0461d4b23 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#include + +namespace graphene { namespace chain { + + struct son_wallet_withdraw_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_id_type son_id; + fc::time_point_sec timestamp; + uint32_t block_num; + sidechain_type sidechain; + std::string peerplays_uid; + std::string peerplays_transaction_id; + chain::account_id_type peerplays_from; + chain::asset peerplays_asset; + sidechain_type withdraw_sidechain; + std::string withdraw_address; + std::string withdraw_currency; + safe withdraw_amount; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_withdraw_process_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_withdraw_id_type son_wallet_withdraw_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer) + (son_id) (timestamp) (block_num) (sidechain) + (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) + (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + +FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer) + (son_wallet_withdraw_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index ec8f3f534..5dbb77525 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -112,7 +112,10 @@ namespace graphene { namespace chain { return results; } - void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; + void get_required_authorities( flat_set& active, + flat_set& owner, + vector& other, + bool ignore_custom_operation_required_auths )const; }; /** @@ -142,6 +145,7 @@ namespace graphene { namespace chain { const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_authorities, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; @@ -150,6 +154,7 @@ namespace graphene { namespace chain { const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; /** @@ -158,13 +163,13 @@ namespace graphene { namespace chain { * some cases where get_required_signatures() returns a * non-minimal set. */ - set minimize_required_signatures( const chain_id_type& chain_id, const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH ) const; @@ -198,10 +203,11 @@ namespace graphene { namespace chain { const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, - bool allow_committe = false, + bool allow_committee = false, const flat_set& active_aprovals = flat_set(), - const flat_set& owner_approvals = flat_set()); + const flat_set& owner_approvals = flat_set() ); /** * @brief captures the result of evaluating the operations contained in the transaction diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 6fa2ab4d1..436197a72 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -176,6 +176,14 @@ namespace graphene { namespace chain { offer_object_type, nft_metadata_type, nft_object_type, + account_role_type, + son_object_type, + son_proposal_object_type, + son_wallet_object_type, + son_wallet_deposit_object_type, + son_wallet_withdraw_object_type, + sidechain_address_object_type, + sidechain_transaction_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -205,7 +213,9 @@ namespace graphene { namespace chain { impl_global_betting_statistics_object_type, impl_lottery_balance_object_type, impl_sweeps_vesting_balance_object_type, - impl_offer_history_object_type + impl_offer_history_object_type, + impl_son_statistics_object_type, + impl_son_schedule_object_type }; //typedef fc::unsigned_int object_id_type; @@ -241,6 +251,14 @@ namespace graphene { namespace chain { class offer_object; class nft_metadata_object; class nft_object; + class account_role_object; + class son_object; + class son_proposal_object; + class son_wallet_object; + class son_wallet_deposit_object; + class son_wallet_withdraw_object; + class sidechain_address_object; + class sidechain_transaction_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -272,6 +290,14 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, offer_object_type, offer_object> offer_id_type; typedef object_id< protocol_ids, nft_metadata_type, nft_metadata_object> nft_metadata_id_type; typedef object_id< protocol_ids, nft_object_type, nft_object> nft_id_type; + typedef object_id< protocol_ids, account_role_type, account_role_object> account_role_id_type; + typedef object_id< protocol_ids, son_object_type, son_object> son_id_type; + typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type; + typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type; + typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type; + typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; + typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; + typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type; // implementation types class global_property_object; @@ -296,6 +322,8 @@ namespace graphene { namespace chain { class lottery_balance_object; class sweeps_vesting_balance_object; class offer_history_object; + class son_statistics_object; + class son_schedule_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -325,6 +353,8 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type; typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type; typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type; + typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type; + typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type; typedef fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -459,6 +489,14 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (offer_object_type) (nft_metadata_type) (nft_object_type) + (account_role_type) + (son_object_type) + (son_proposal_object_type) + (son_wallet_object_type) + (son_wallet_deposit_object_type) + (son_wallet_withdraw_object_type) + (sidechain_address_object_type) + (sidechain_transaction_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -487,6 +525,8 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_lottery_balance_object_type) (impl_sweeps_vesting_balance_object_type) (impl_offer_history_object_type) + (impl_son_statistics_object_type) + (impl_son_schedule_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) @@ -535,6 +575,15 @@ FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type ) FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type ) FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type ) FC_REFLECT_TYPENAME( graphene::chain::nft_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type ) + FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 4dffb253f..9bde10089 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -27,12 +27,14 @@ namespace graphene { namespace chain { - enum class vesting_balance_type { normal, gpos }; + enum class vesting_balance_type { normal, gpos, son }; inline std::string get_vesting_balance_type(vesting_balance_type type) { switch (type) { case vesting_balance_type::normal: return "NORMAL"; + case vesting_balance_type::son: + return "SON"; case vesting_balance_type::gpos: default: return "GPOS"; @@ -55,9 +57,10 @@ namespace graphene { namespace chain { cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} }; - typedef fc::static_variant vesting_policy_initializer; - + struct dormant_vesting_policy_initializer {}; + typedef fc::static_variant vesting_policy_initializer; /** * @brief Create a vesting balance. @@ -127,13 +130,14 @@ FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_ty FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy)(balance_type) ) -FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount)) +FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) +FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) -FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos) ) +FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation::fee_parameters_type ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type ) diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 7d733e45d..8a46954d3 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -59,6 +59,7 @@ struct vote_id_type committee, witness, worker, + son, VOTE_TYPE_COUNT }; @@ -143,7 +144,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp b/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp new file mode 100644 index 000000000..6851c4e76 --- /dev/null +++ b/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +namespace graphene +{ + namespace chain + { + struct rbac_operation_hardfork_visitor + { + typedef void result_type; + const fc::time_point_sec block_time; + + rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {} + void operator()(int op_type) const + { + int first_allowed_op = operation::tag::value; + switch (op_type) + { + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!"); + break; + default: + FC_ASSERT(op_type >= operation::tag::value && op_type < first_allowed_op, "Custom permissions and roles not allowed on this operation!"); + } + } + }; + + } // namespace chain +} // namespace graphene \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp new file mode 100644 index 000000000..82bfcf1aa --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_address_evaluator.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class add_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_add_operation operation_type; + + void_result do_evaluate(const sidechain_address_add_operation& o); + object_id_type do_apply(const sidechain_address_add_operation& o); +}; + +class update_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_update_operation operation_type; + + void_result do_evaluate(const sidechain_address_update_operation& o); + object_id_type do_apply(const sidechain_address_update_operation& o); +}; + +class delete_sidechain_address_evaluator : public evaluator +{ +public: + typedef sidechain_address_delete_operation operation_type; + + void_result do_evaluate(const sidechain_address_delete_operation& o); + void_result do_apply(const sidechain_address_delete_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp new file mode 100644 index 000000000..b8aa07c65 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -0,0 +1,92 @@ +#pragma once +#include +#include +#include + +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class sidechain_address_object + * @brief tracks information about a sidechain addresses for user accounts. + * @ingroup object + */ + class sidechain_address_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = sidechain_address_object_type; + + account_id_type sidechain_address_account; + sidechain_type sidechain; + string deposit_public_key; + string deposit_address; + string deposit_address_data; + string withdraw_public_key; + string withdraw_address; + time_point_sec valid_from; + time_point_sec expires; + + sidechain_address_object() : + sidechain(sidechain_type::bitcoin), + deposit_public_key(""), + deposit_address(""), + withdraw_public_key(""), + withdraw_address("") {} + }; + + struct by_account; + struct by_sidechain; + struct by_sidechain_and_deposit_public_key_and_expires; + struct by_withdraw_public_key; + struct by_account_and_sidechain_and_expires; + struct by_sidechain_and_deposit_address_and_expires; + using sidechain_address_multi_index_type = multi_index_container< + sidechain_address_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + member + >, + ordered_unique< tag, + composite_key, + member, + member + > + >, + ordered_non_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member, + member + > + >, + ordered_non_unique< tag, + composite_key, + member, + member + > + > + > + >; + using sidechain_address_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object), + (sidechain_address_account) (sidechain) + (deposit_public_key) (deposit_address) (deposit_address_data) + (withdraw_public_key) (withdraw_address) (valid_from) (expires) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp new file mode 100644 index 000000000..6bbc8b5c3 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace graphene { namespace chain { + +enum class sidechain_type { + unknown, + bitcoin, + ethereum, + eos, + peerplays +}; + +} } + +FC_REFLECT_ENUM(graphene::chain::sidechain_type, + (unknown) + (bitcoin) + (ethereum) + (eos) + (peerplays) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp new file mode 100644 index 000000000..702788ff4 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class sidechain_transaction_create_evaluator : public evaluator +{ +public: + typedef sidechain_transaction_create_operation operation_type; + + void_result do_evaluate(const sidechain_transaction_create_operation& o); + object_id_type do_apply(const sidechain_transaction_create_operation& o); +}; + +class sidechain_transaction_sign_evaluator : public evaluator +{ +public: + typedef sidechain_transaction_sign_operation operation_type; + + void_result do_evaluate(const sidechain_transaction_sign_operation& o); + object_id_type do_apply(const sidechain_transaction_sign_operation& o); +}; + +class sidechain_transaction_send_evaluator : public evaluator +{ +public: + typedef sidechain_transaction_send_operation operation_type; + + void_result do_evaluate(const sidechain_transaction_send_operation& o); + object_id_type do_apply(const sidechain_transaction_send_operation& o); +}; + +class sidechain_transaction_settle_evaluator : public evaluator +{ +public: + typedef sidechain_transaction_settle_operation operation_type; + + void_result do_evaluate(const sidechain_transaction_settle_operation& o); + object_id_type do_apply(const sidechain_transaction_settle_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp new file mode 100644 index 000000000..e9011ffb2 --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -0,0 +1,82 @@ +#pragma once +#include +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + enum class sidechain_transaction_status { + invalid, + valid, + complete, + sent, + settled + }; + + /** + * @class sidechain_transaction_object + * @brief tracks state of sidechain transaction during signing process. + * @ingroup object + */ + class sidechain_transaction_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = sidechain_transaction_object_type; + + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector signers; + std::vector> signatures; + std::string sidechain_transaction; + + uint32_t total_weight = 0; + uint32_t current_weight = 0; + uint32_t threshold = 0; + + sidechain_transaction_status status; + }; + + struct by_object_id; + struct by_sidechain_and_status; + using sidechain_transaction_multi_index_type = multi_index_container< + sidechain_transaction_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member + > + > + > + >; + using sidechain_transaction_index = generic_index; +} } // graphene::chain + +FC_REFLECT_ENUM( graphene::chain::sidechain_transaction_status, + (invalid) + (valid) + (complete) + (sent) + (settled) ) + +FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ), + (sidechain) + (object_id) + (transaction) + (signers) + (signatures) + (sidechain_transaction) + (total_weight) + (current_weight) + (threshold) + (status) ) diff --git a/libraries/chain/include/graphene/chain/son_evaluator.hpp b/libraries/chain/include/graphene/chain/son_evaluator.hpp new file mode 100644 index 000000000..c3f1ac495 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_evaluator.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class create_son_evaluator : public evaluator +{ +public: + typedef son_create_operation operation_type; + + void_result do_evaluate(const son_create_operation& o); + object_id_type do_apply(const son_create_operation& o); +}; + +class update_son_evaluator : public evaluator +{ +public: + typedef son_update_operation operation_type; + + void_result do_evaluate(const son_update_operation& o); + object_id_type do_apply(const son_update_operation& o); +}; + +class deregister_son_evaluator : public evaluator +{ +public: + typedef son_deregister_operation operation_type; + + void_result do_evaluate(const son_deregister_operation& o); + void_result do_apply(const son_deregister_operation& o); +}; + +class son_heartbeat_evaluator : public evaluator +{ +public: + typedef son_heartbeat_operation operation_type; + + void_result do_evaluate(const son_heartbeat_operation& o); + object_id_type do_apply(const son_heartbeat_operation& o); +}; + +class son_report_down_evaluator : public evaluator +{ +public: + typedef son_report_down_operation operation_type; + + void_result do_evaluate(const son_report_down_operation& o); + object_id_type do_apply(const son_report_down_operation& o); +}; + +class son_maintenance_evaluator : public evaluator +{ +public: + typedef son_maintenance_operation operation_type; + + void_result do_evaluate(const son_maintenance_operation& o); + object_id_type do_apply(const son_maintenance_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp new file mode 100644 index 000000000..2bfecac44 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_info + * @brief tracks information about a SON info required to re/create primary wallet + * @ingroup object + */ + struct son_info { + son_id_type son_id; + weight_type weight = 0; + public_key_type signing_key; + flat_map sidechain_public_keys; + + bool operator==(const son_info& rhs) { + bool son_sets_equal = + (son_id == rhs.son_id) && + (weight == rhs.weight) && + (signing_key == rhs.signing_key) && + (sidechain_public_keys.size() == rhs.sidechain_public_keys.size()); + + if (son_sets_equal) { + bool sidechain_public_keys_equal = true; + for (size_t i = 0; i < sidechain_public_keys.size(); i++) { + const auto lhs_scpk = sidechain_public_keys.nth(i); + const auto rhs_scpk = rhs.sidechain_public_keys.nth(i); + sidechain_public_keys_equal = sidechain_public_keys_equal && + (lhs_scpk->first == rhs_scpk->first) && + (lhs_scpk->second == rhs_scpk->second); + } + son_sets_equal = son_sets_equal && sidechain_public_keys_equal; + } + return son_sets_equal; + } + }; + +} } + +FC_REFLECT( graphene::chain::son_info, + (son_id) + (weight) + (signing_key) + (sidechain_public_keys) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp new file mode 100644 index 000000000..3335bd8a6 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -0,0 +1,133 @@ +#pragma once +#include +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + enum class son_status + { + inactive, + active, + request_maintenance, + in_maintenance, + deregistered + }; + /** + * @class son_statistics_object + * @ingroup object + * @ingroup implementation + * + * This object contains regularly updated statistical data about an SON. It is provided for the purpose of + * separating the SON transaction data that changes frequently from the SON object data that is mostly static. + */ + class son_statistics_object : public graphene::db::abstract_object + { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_son_statistics_object_type; + + son_id_type owner; + // Lifetime total transactions signed + uint64_t total_txs_signed = 0; + // Transactions signed since the last son payouts + uint64_t txs_signed = 0; + // Total Voted Active time i.e. duration selected as part of voted active SONs + uint64_t total_voted_time = 0; + // Total Downtime barring the current down time in seconds, used for stats to present to user + uint64_t total_downtime = 0; + // Current Interval Downtime since last maintenance + uint64_t current_interval_downtime = 0; + // Down timestamp, if son status is in_maintenance use this + fc::time_point_sec last_down_timestamp; + // Last Active heartbeat timestamp + fc::time_point_sec last_active_timestamp; + // Deregistered Timestamp + fc::time_point_sec deregistered_timestamp; + // Total sidechain transactions reported by SON network while SON was active + uint64_t total_sidechain_txs_reported = 0; + // Sidechain transactions reported by this SON + uint64_t sidechain_txs_reported = 0; + }; + + /** + * @class son_object + * @brief tracks information about a SON account. + * @ingroup object + */ + class son_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_object_type; + + account_id_type son_account; + vote_id_type vote_id; + uint64_t total_votes = 0; + string url; + vesting_balance_id_type deposit; + public_key_type signing_key; + vesting_balance_id_type pay_vb; + son_statistics_id_type statistics; + son_status status = son_status::inactive; + flat_map sidechain_public_keys; + + void pay_son_fee(share_type pay, database& db); + bool has_valid_config()const; + }; + + struct by_account; + struct by_vote_id; + using son_multi_index_type = multi_index_container< + son_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + > + > + >; + using son_index = generic_index; + + struct by_owner; + using son_stats_multi_index_type = multi_index_container< + son_statistics_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + > + > + >; + using son_stats_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintenance)(in_maintenance)(deregistered) ) + +FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), + (son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(statistics)(status)(sidechain_public_keys) ) + +FC_REFLECT_DERIVED( graphene::chain::son_statistics_object, + (graphene::db::object), + (owner) + (total_txs_signed) + (txs_signed) + (total_voted_time) + (total_downtime) + (current_interval_downtime) + (last_down_timestamp) + (last_active_timestamp) + (deregistered_timestamp) + (total_sidechain_txs_reported) + (sidechain_txs_reported) + ) diff --git a/libraries/chain/include/graphene/chain/son_proposal_object.hpp b/libraries/chain/include/graphene/chain/son_proposal_object.hpp new file mode 100644 index 000000000..7b1674b81 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_proposal_object.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace chain { + +enum class son_proposal_type +{ + son_deregister_proposal, + son_report_down_proposal +}; + +class son_proposal_object : public abstract_object +{ + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_proposal_object_type; + + son_proposal_id_type get_id()const { return id; } + + proposal_id_type proposal_id; + son_id_type son_id; + son_proposal_type proposal_type; +}; + +struct by_proposal; +using son_proposal_multi_index_container = multi_index_container< + son_proposal_object, + indexed_by< + ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, + ordered_unique< tag< by_proposal >, member< son_proposal_object, proposal_id_type, &son_proposal_object::proposal_id > > + > +>; +using son_proposal_index = generic_index; + +} } // graphene::chain + +FC_REFLECT_ENUM( graphene::chain::son_proposal_type, (son_deregister_proposal)(son_report_down_proposal) ) + +FC_REFLECT_DERIVED( graphene::chain::son_proposal_object, (graphene::chain::object), (proposal_id)(son_id)(proposal_type) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp new file mode 100644 index 000000000..f3780c1f6 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_evaluator.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace graphene { namespace chain { + +class create_son_wallet_deposit_evaluator : public evaluator +{ +public: + typedef son_wallet_deposit_create_operation operation_type; + + void_result do_evaluate(const son_wallet_deposit_create_operation& o); + object_id_type do_apply(const son_wallet_deposit_create_operation& o); +}; + +class process_son_wallet_deposit_evaluator : public evaluator +{ +public: + typedef son_wallet_deposit_process_operation operation_type; + + void_result do_evaluate(const son_wallet_deposit_process_operation& o); + object_id_type do_apply(const son_wallet_deposit_process_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp new file mode 100644 index 000000000..ae68a64f0 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_deposit_object + * @brief tracks information about a SON wallet deposit. + * @ingroup object + */ + class son_wallet_deposit_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_deposit_object_type; + + time_point_sec timestamp; + uint32_t block_num; + sidechain_type sidechain = sidechain_type::unknown; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + std::string sidechain_currency; + safe sidechain_amount; + chain::account_id_type peerplays_from; + chain::account_id_type peerplays_to; + chain::asset peerplays_asset; + + std::map expected_reports; + std::set received_reports; + + bool confirmed = false; + + bool processed = false; + }; + + struct by_sidechain_uid; + struct by_sidechain_and_confirmed_and_processed; + using son_wallet_deposit_multi_index_type = multi_index_container< + son_wallet_deposit_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member, + member + > + > + > + >; + using son_wallet_deposit_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_deposit_object, (graphene::db::object), + (timestamp) (block_num) (sidechain) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) + (peerplays_from) (peerplays_to) (peerplays_asset) + (expected_reports) (received_reports) + (confirmed) (processed) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp new file mode 100644 index 000000000..78e8655f6 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class recreate_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_recreate_operation operation_type; + + void_result do_evaluate(const son_wallet_recreate_operation& o); + object_id_type do_apply(const son_wallet_recreate_operation& o); +}; + +class update_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_update_operation operation_type; + + void_result do_evaluate(const son_wallet_update_operation& o); + object_id_type do_apply(const son_wallet_update_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp new file mode 100644 index 000000000..315def339 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_object + * @brief tracks information about a SON wallet. + * @ingroup object + */ + class son_wallet_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_object_type; + + time_point_sec valid_from; + time_point_sec expires; + + flat_map addresses; + vector sons; + }; + + struct by_valid_from; + struct by_expires; + using son_wallet_multi_index_type = multi_index_container< + son_wallet_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + > + > + >; + using son_wallet_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object), + (valid_from) (expires) (addresses) (sons) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp new file mode 100644 index 000000000..f5c08cd3b --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_evaluator.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace graphene { namespace chain { + +class create_son_wallet_withdraw_evaluator : public evaluator +{ +public: + typedef son_wallet_withdraw_create_operation operation_type; + + void_result do_evaluate(const son_wallet_withdraw_create_operation& o); + object_id_type do_apply(const son_wallet_withdraw_create_operation& o); +}; + +class process_son_wallet_withdraw_evaluator : public evaluator +{ +public: + typedef son_wallet_withdraw_process_operation operation_type; + + void_result do_evaluate(const son_wallet_withdraw_process_operation& o); + object_id_type do_apply(const son_wallet_withdraw_process_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp new file mode 100644 index 000000000..d65f5cab7 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_wallet_withdraw_object + * @brief tracks information about a SON wallet withdrawal. + * @ingroup object + */ + class son_wallet_withdraw_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_wallet_withdraw_object_type; + + time_point_sec timestamp; + uint32_t block_num; + sidechain_type sidechain = sidechain_type::unknown; + std::string peerplays_uid; + std::string peerplays_transaction_id; + chain::account_id_type peerplays_from; + chain::asset peerplays_asset; + sidechain_type withdraw_sidechain; + std::string withdraw_address; + std::string withdraw_currency; + safe withdraw_amount; + + std::map expected_reports; + std::set received_reports; + + bool confirmed = false; + + bool processed = false; + }; + + struct by_peerplays_uid; + struct by_withdraw_sidechain_and_confirmed_and_processed; + using son_wallet_withdraw_multi_index_type = multi_index_container< + son_wallet_withdraw_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member, + member + > + > + > + >; + using son_wallet_withdraw_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_wallet_withdraw_object, (graphene::db::object), + (timestamp) (block_num) (sidechain) + (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) + (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) + (expected_reports) (received_reports) + (confirmed) (processed) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index b03b8f7b0..1e87246ea 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -118,10 +118,32 @@ namespace graphene { namespace chain { void on_withdraw(const vesting_policy_context& ctx); }; + /** + * @brief Cant withdraw anything while balance is in this policy. + * + * This policy is needed to register SON users where balance may be claimable only after + * the SON object is deleted(plus a linear policy). + * When deleting a SON member the dormant mode will be replaced by a linear policy. + * + * @note New funds may not be added to a dormant vesting balance. + */ + struct dormant_vesting_policy + { + asset get_allowed_withdraw(const vesting_policy_context& ctx)const; + bool is_deposit_allowed(const vesting_policy_context& ctx)const; + bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; } + bool is_withdraw_allowed(const vesting_policy_context& ctx)const; + void on_deposit(const vesting_policy_context& ctx); + void on_deposit_vested(const vesting_policy_context&) + { FC_THROW( "May not deposit vested into a linear vesting balance." ); } + void on_withdraw(const vesting_policy_context& ctx); + }; + typedef fc::static_variant< linear_vesting_policy, - cdd_vesting_policy - > vesting_policy; + cdd_vesting_policy, + dormant_vesting_policy + > vesting_policy; /** * Vesting balance object is a balance that is locked by the blockchain for a period of time. @@ -140,7 +162,7 @@ namespace graphene { namespace chain { /// The vesting policy stores details on when funds vest, and controls when they may be withdrawn vesting_policy policy; - /// We can have 2 types of vesting, gpos and all the rest + /// We can have 3 types of vesting, gpos, son and the rest vesting_balance_type balance_type = vesting_balance_type::normal; vesting_balance_object() {} @@ -224,6 +246,8 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy, (coin_seconds_earned_last_update) ) +FC_REFLECT(graphene::chain::dormant_vesting_policy, ) + FC_REFLECT_TYPENAME( graphene::chain::vesting_policy ) FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object), diff --git a/libraries/chain/include/graphene/chain/vote_count.hpp b/libraries/chain/include/graphene/chain/vote_count.hpp index f76a784d4..ab2f36129 100644 --- a/libraries/chain/include/graphene/chain/vote_count.hpp +++ b/libraries/chain/include/graphene/chain/vote_count.hpp @@ -63,6 +63,17 @@ struct vote_counter out_auth = auth; } + void finish_2_3( authority& out_auth ) + { + if( total_votes == 0 ) + return; + assert( total_votes <= std::numeric_limits::max() ); + uint32_t weight = uint32_t( total_votes ); + weight = (weight * 2 / 3) + 1; + auth.weight_threshold = weight; + out_auth = auth; + } + bool is_empty()const { return (total_votes == 0); diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index 1702212d4..b934fd01b 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -30,6 +30,9 @@ namespace graphene { namespace chain { +class witness_schedule_object; +class son_schedule_object; + typedef hash_ctr_rng< /* HashClass = */ fc::sha256, /* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH @@ -51,6 +54,22 @@ typedef generic_far_future_witness_scheduler< /* debug = */ true > far_future_witness_scheduler; +typedef generic_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > son_scheduler; + +typedef generic_far_future_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > far_future_son_scheduler; + class witness_schedule_object : public graphene::db::abstract_object { public: @@ -71,6 +90,26 @@ class witness_schedule_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_son_schedule_object_type; + + vector< son_id_type > current_shuffled_sons; + + son_scheduler scheduler; + uint32_t last_scheduling_block; + uint64_t slots_since_genesis = 0; + fc::array< char, sizeof(secret_hash_type) > rng_seed; + + /** + * Not necessary for consensus, but used for figuring out the participation rate. + * The nth bit is 0 if the nth slot was unfilled, else it is 1. + */ + fc::uint128 recent_slots_filled; +}; + } } @@ -94,6 +133,26 @@ FC_REFLECT_DERIVED( (recent_slots_filled) (current_shuffled_witnesses) ) +FC_REFLECT( graphene::chain::son_scheduler, + (_turns) + (_tokens) + (_min_token_count) + (_ineligible_waiting_for_token) + (_ineligible_no_turn) + (_eligible) + (_schedule) + (_lame_duck) + ) +FC_REFLECT_DERIVED( + graphene::chain::son_schedule_object, + (graphene::db::object), + (scheduler) + (last_scheduling_block) + (slots_since_genesis) + (rng_seed) + (recent_slots_filled) + (current_shuffled_sons) +) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_scheduler ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_schedule_object ) diff --git a/libraries/chain/nft_evaluator.cpp b/libraries/chain/nft_evaluator.cpp index ace3f91b2..f7f007ff0 100644 --- a/libraries/chain/nft_evaluator.cpp +++ b/libraries/chain/nft_evaluator.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include namespace graphene { namespace chain { @@ -18,6 +20,10 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat (*op.revenue_partner)(db()); FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid"); } + if(op.account_role) { + const auto& ar_obj = (*op.account_role)(db()); + FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -32,6 +38,7 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat obj.revenue_split = op.revenue_split; obj.is_transferable = op.is_transferable; obj.is_sellable = op.is_sellable; + obj.account_role = op.account_role; }); return new_nft_metadata_object.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -57,6 +64,10 @@ void_result nft_metadata_update_evaluator::do_evaluate( const nft_metadata_updat (*op.revenue_partner)(db()); FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid"); } + if(op.account_role) { + const auto& ar_obj = (*op.account_role)(db()); + FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -77,6 +88,8 @@ void_result nft_metadata_update_evaluator::do_apply( const nft_metadata_update_o obj.is_transferable = *op.is_transferable; if( op.is_sellable.valid() ) obj.is_sellable = *op.is_sellable; + if( op.account_role.valid() ) + obj.account_role = op.account_role; }); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -144,6 +157,17 @@ void_result nft_safe_transfer_from_evaluator::do_evaluate( const nft_safe_transf const auto& nft_meta_obj = itr_nft->nft_metadata_id(db()); FC_ASSERT( nft_meta_obj.is_transferable == true, "NFT is not transferable"); + if (nft_meta_obj.account_role) + { + const auto &ar_idx = db().get_index_type().indices().get(); + auto ar_itr = ar_idx.find(*nft_meta_obj.account_role); + if(ar_itr != ar_idx.end()) + { + FC_ASSERT(db().account_role_valid(*ar_itr, op.operator_, get_type()), "Account role not valid"); + FC_ASSERT(db().account_role_valid(*ar_itr, op.to), "Account role not valid"); + } + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/offer_evaluator.cpp b/libraries/chain/offer_evaluator.cpp index 0d1b1947e..1b8874662 100644 --- a/libraries/chain/offer_evaluator.cpp +++ b/libraries/chain/offer_evaluator.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,6 +40,15 @@ namespace graphene } const auto &nft_meta_obj = nft_obj.nft_metadata_id(d); FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable"); + if (nft_meta_obj.account_role) + { + const auto &ar_idx = db().get_index_type().indices().get(); + auto ar_itr = ar_idx.find(*nft_meta_obj.account_role); + if(ar_itr != ar_idx.end()) + { + FC_ASSERT(db().account_role_valid(*ar_itr, op.issuer, get_type()), "Account role not valid"); + } + } } FC_ASSERT(op.offer_expiration_date > d.head_block_time(), "Expiration should be in future"); FC_ASSERT(op.fee.amount >= 0, "Invalid fee"); @@ -100,6 +111,18 @@ namespace graphene FC_ASSERT(!is_approved, "Bidder cannot already be an approved account of the item"); FC_ASSERT(!is_approved_operator, "Bidder cannot already be an approved operator of the item"); } + + const auto &nft_meta_obj = nft_obj.nft_metadata_id(d); + FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable"); + if (nft_meta_obj.account_role) + { + const auto &ar_idx = db().get_index_type().indices().get(); + auto ar_itr = ar_idx.find(*nft_meta_obj.account_role); + if(ar_itr != ar_idx.end()) + { + FC_ASSERT(db().account_role_valid(*ar_itr, op.bidder, get_type()), "Account role not valid"); + } + } } FC_ASSERT(op.bid_price.asset_id == offer.minimum_price.asset_id, "Asset type mismatch"); @@ -335,4 +358,4 @@ namespace graphene FC_CAPTURE_AND_RETHROW((op)) } } // namespace chain -} // namespace graphene \ No newline at end of file +} // namespace graphene diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ba714c211..e7d216efb 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,7 @@ struct proposal_operation_hardfork_visitor void operator()(const vesting_balance_create_operation &vbco) const { if(block_time < HARDFORK_GPOS_TIME) - FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" ); + FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" ); } void operator()(const custom_permission_create_operation &v) const { @@ -196,6 +197,41 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_set_approval_for_all_operation not allowed yet!" ); } + void operator()(const account_role_create_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_create_operation not allowed yet!" ); + } + + void operator()(const account_role_update_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_update_operation not allowed yet!" ); + } + + void operator()(const account_role_delete_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" ); + } + + void operator()(const son_create_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" ); + } + + void operator()(const son_update_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_update_operation not allowed yet!" ); + } + + void operator()(const son_deregister_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_deregister_operation not allowed yet!" ); + } + + void operator()(const son_heartbeat_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" ); + } + + void operator()(const son_report_down_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" ); + } + + void operator()(const son_maintenance_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" ); + } // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { @@ -204,19 +240,38 @@ struct proposal_operation_hardfork_visitor } }; -void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) +void son_hardfork_visitor::operator()( const son_deregister_operation &v ) +{ + db.create([&]( son_proposal_object& son_prop ) { + son_prop.proposal_type = son_proposal_type::son_deregister_proposal; + son_prop.proposal_id = prop_id; + son_prop.son_id = v.son_id; + }); +} + +void son_hardfork_visitor::operator()( const son_report_down_operation &v ) +{ + db.create([&]( son_proposal_object& son_prop ) { + son_prop.proposal_type = son_proposal_type::son_report_down_proposal; + son_prop.proposal_id = prop_id; + son_prop.son_id = v.son_id; + }); +} + +void_result proposal_create_evaluator::do_evaluate( const proposal_create_operation& o ) { try { const database& d = db(); + auto block_time = d.head_block_time(); - proposal_operation_hardfork_visitor vtor( d.head_block_time() ); + proposal_operation_hardfork_visitor vtor( block_time ); vtor( o ); const auto& global_parameters = d.get_global_properties().parameters; - FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." ); - FC_ASSERT( o.expiration_time <= d.head_block_time() + global_parameters.maximum_proposal_lifetime, + FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." ); + FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime, "Proposal expiration time is too far in the future."); - FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()), + FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - block_time), "Proposal review period must be less than its overall lifetime." ); { @@ -225,7 +280,8 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati vector other; for( auto& op : o.proposed_ops ) { - operation_get_required_authorities(op.op, auths, auths, other); + operation_get_required_authorities( op.op, auths, auths, other, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(block_time) ); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? @@ -248,18 +304,19 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati } } - for( const op_wrapper& op : o.proposed_ops ) + for (const op_wrapper& op : o.proposed_ops) _proposed_trx.operations.push_back(op.op); _proposed_trx.validate(); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } -object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o) +object_id_type proposal_create_evaluator::do_apply( const proposal_create_operation& o ) { try { database& d = db(); + auto chain_time = d.head_block_time(); - const proposal_object& proposal = d.create([&](proposal_object& proposal) { + const proposal_object& proposal = d.create( [&o, this, chain_time](proposal_object& proposal) { _proposed_trx.expiration = o.expiration_time; proposal.proposed_transaction = _proposed_trx; proposal.proposer = o.fee_paying_account; @@ -273,7 +330,8 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati // TODO: consider caching values from evaluate? for( auto& op : _proposed_trx.operations ) - operation_get_required_authorities(op, required_active, proposal.required_owner_approvals, other); + operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) ); //All accounts which must provide both owner and active authority should be omitted from the active authority set; //owner authority approval implies active authority approval. @@ -282,10 +340,16 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin())); }); + son_hardfork_visitor son_vtor(d, proposal.id); + for(auto& op: o.proposed_ops) + { + op.op.visit(son_vtor); + } + return proposal.id; } FC_CAPTURE_AND_RETHROW( (o) ) } -void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o) +void_result proposal_update_evaluator::do_evaluate( const proposal_update_operation& o ) { try { database& d = db(); diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index a2f6d1ae1..662c700a8 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -24,12 +24,13 @@ #include #include #include +#include namespace graphene { namespace chain { -bool proposal_object::is_authorized_to_execute(database& db) const +bool proposal_object::is_authorized_to_execute( database& db ) const { - transaction_evaluation_state dry_run_eval(&db); + transaction_evaluation_state dry_run_eval( &db ); try { verify_authority( proposed_transaction.operations, @@ -38,6 +39,7 @@ bool proposal_object::is_authorized_to_execute(database& db) const [&]( account_id_type id ){ return &id(db).owner; }, [&]( account_id_type id, const operation& op ){ return db.get_account_custom_authorities(id, op); }, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ), db.get_global_properties().parameters.max_authority_depth, true, /* allow committee */ available_active_approvals, diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index c79811b9c..2405369a8 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -174,15 +174,22 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; + auto needed_sons = num_son; for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) --needed_witnesses; else if ( id.type() == vote_id_type::committee && needed_committee ) --needed_committee; - - FC_ASSERT( needed_witnesses == 0 && needed_committee == 0, - "May not specify fewer witnesses or committee members than the number voted for."); + else if ( id.type() == vote_id_type::son && needed_sons ) + --needed_sons; + + FC_ASSERT( needed_witnesses == 0, + "May not specify fewer witnesses than the number voted for."); + FC_ASSERT( needed_committee == 0, + "May not specify fewer committee members than the number voted for."); + FC_ASSERT( needed_sons == 0, + "May not specify fewer SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/protocol/account_role.cpp b/libraries/chain/protocol/account_role.cpp new file mode 100644 index 000000000..1d71d6cc5 --- /dev/null +++ b/libraries/chain/protocol/account_role.cpp @@ -0,0 +1,61 @@ +#include +#include + +namespace graphene +{ + namespace chain + { + + void account_role_create_operation::validate() const + { + FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); + FC_ASSERT(allowed_operations.size() > 0, "Allowed operations should be positive"); + FC_ASSERT(whitelisted_accounts.size() > 0, "Whitelisted accounts should be positive"); + } + + void account_role_update_operation::validate() const + { + FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); + for (auto aop : allowed_operations_to_add) + { + FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid"); + FC_ASSERT(allowed_operations_to_remove.find(aop) == allowed_operations_to_remove.end(), + "Cannot add and remove allowed operation at the same time."); + } + for (auto aop : allowed_operations_to_remove) + { + FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid"); + FC_ASSERT(allowed_operations_to_add.find(aop) == allowed_operations_to_add.end(), + "Cannot add and remove allowed operation at the same time."); + } + + for (auto acc : accounts_to_add) + { + FC_ASSERT(accounts_to_remove.find(acc) == accounts_to_remove.end(), + "Cannot add and remove accounts at the same time."); + } + + for (auto acc : accounts_to_remove) + { + FC_ASSERT(accounts_to_add.find(acc) == accounts_to_add.end(), + "Cannot add and remove accounts at the same time."); + } + } + + void account_role_delete_operation::validate() const + { + FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); + } + + share_type account_role_create_operation::calculate_fee(const fee_parameters_type &k) const + { + return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte); + } + + share_type account_role_update_operation::calculate_fee(const fee_parameters_type &k) const + { + return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte); + } + + } // namespace chain +} // namespace graphene diff --git a/libraries/chain/protocol/nft.cpp b/libraries/chain/protocol/nft.cpp index 802bf4258..4a66f330b 100644 --- a/libraries/chain/protocol/nft.cpp +++ b/libraries/chain/protocol/nft.cpp @@ -45,7 +45,6 @@ void nft_metadata_create_operation::validate() const FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided"); FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided"); - FC_ASSERT(base_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI"); } void nft_metadata_update_operation::validate() const @@ -55,14 +54,11 @@ void nft_metadata_update_operation::validate() const FC_ASSERT(is_valid_nft_token_name(*name), "Invalid NFT name provided"); if(symbol) FC_ASSERT(is_valid_nft_token_name(*symbol), "Invalid NFT symbol provided"); - if(base_uri) - FC_ASSERT((*base_uri).length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI"); } void nft_mint_operation::validate() const { FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); - FC_ASSERT(token_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Token URI"); } share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_type &k) const @@ -72,7 +68,7 @@ share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_typ share_type nft_metadata_update_operation::calculate_fee(const fee_parameters_type &k) const { - return k.fee; + return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte ); } share_type nft_mint_operation::calculate_fee(const fee_parameters_type &k) const diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 7db51078c..385e14dcc 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -53,25 +53,38 @@ struct operation_validator struct operation_get_required_auth { - typedef void result_type; + using result_type = void; flat_set& active; flat_set& owner; vector& other; + bool ignore_custom_op_reqd_auths; operation_get_required_auth( flat_set& a, - flat_set& own, - vector& oth ):active(a),owner(own),other(oth){} + flat_set& own, + vector& oth, + bool ignore_custom_operation_required_auths ) + : active( a ), owner( own ), other( oth ), + ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths ) + {} template - void operator()( const T& v )const - { + void operator()( const T& v ) const { active.insert( v.fee_payer() ); - v.get_required_active_authorities( active ); - v.get_required_owner_authorities( owner ); + v.get_required_active_authorities( active ); + v.get_required_owner_authorities( owner ); v.get_required_authorities( other ); } + + void operator()( const custom_operation& op ) const { + active.insert( op.fee_payer() ); + if( !ignore_custom_op_reqd_auths ) { + op.get_required_active_authorities( active ); + op.get_required_owner_authorities( owner ); + op.get_required_authorities( other ); + } + } }; void operation_validate( const operation& op ) @@ -79,12 +92,13 @@ void operation_validate( const operation& op ) op.visit( operation_validator() ); } -void operation_get_required_authorities( const operation& op, +void operation_get_required_authorities( const operation& op, flat_set& active, flat_set& owner, - vector& other ) + vector& other, + bool ignore_custom_operation_required_auths ) { - op.visit( operation_get_required_auth( active, owner, other ) ); + op.visit( operation_get_required_auth( active, owner, other, ignore_custom_operation_required_auths ) ); } } } // namespace graphene::chain diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 62419948c..98e172380 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -95,10 +95,13 @@ void transaction::set_reference_block( const block_id_type& reference_block ) ref_block_prefix = reference_block._hash[1]; } -void transaction::get_required_authorities( flat_set& active, flat_set& owner, vector& other )const +void transaction::get_required_authorities( flat_set& active, + flat_set& owner, + vector& other, + bool ignore_custom_operation_required_auths )const { - for( const auto& op : operations ) - operation_get_required_authorities( op, active, owner, other ); + for ( const auto& op : operations ) + operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths ); } @@ -249,8 +252,9 @@ void verify_authority( const vector& ops, const flat_set& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion_depth, - bool allow_committe, + bool allow_committee, const flat_set& active_aprovals, const flat_set& owner_approvals ) { try { @@ -276,7 +280,8 @@ void verify_authority( const vector& ops, const flat_set operation_required_active; - operation_get_required_authorities( op, operation_required_active, required_owner, other ); + operation_get_required_authorities( op, operation_required_active, required_owner, other, + ignore_custom_operation_required_auths ); auto itr = operation_required_active.begin(); while ( itr != operation_required_active.end() ) { @@ -289,7 +294,7 @@ void verify_authority( const vector& ops, const flat_set signed_transaction::get_required_signatures( const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_authorities, uint32_t max_recursion_depth )const { flat_set required_active; @@ -370,7 +376,7 @@ set signed_transaction::get_required_signatures( for( const auto& op : operations ) { flat_set operation_required_active; - operation_get_required_authorities( op, operation_required_active, required_owner, other ); + operation_get_required_authorities( op, operation_required_active, required_owner, other, ignore_custom_operation_required_authorities ); auto itr = operation_required_active.begin(); while ( itr != operation_required_active.end() ) { @@ -383,7 +389,7 @@ set signed_transaction::get_required_signatures( required_active.insert( operation_required_active.begin(), operation_required_active.end() ); } - for( const auto& auth : other ) + for (const auto& auth : other) s.check_authority(&auth); for( auto& owner : required_owner ) s.check_authority( get_owner( owner ) ); @@ -407,10 +413,12 @@ set signed_transaction::minimize_required_signatures( const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion ) const { - set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom, max_recursion ); + set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom, + ignore_custom_operation_required_auths, max_recursion ); flat_set< public_key_type > result( s.begin(), s.end() ); for( const public_key_type& k : s ) @@ -418,7 +426,8 @@ set signed_transaction::minimize_required_signatures( result.erase( k ); try { - graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, max_recursion ); + graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, + ignore_custom_operation_required_auths, max_recursion ); continue; // element stays erased if verify_authority is ok } catch( const tx_missing_owner_auth& e ) {} @@ -434,6 +443,7 @@ void signed_transaction::verify_authority( const std::function& get_active, const std::function& get_owner, const std::function(account_id_type, const operation&)>& get_custom, + bool ignore_custom_operation_required_auths, uint32_t max_recursion )const { try { graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion ); diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp new file mode 100644 index 000000000..ae8026278 --- /dev/null +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -0,0 +1,114 @@ +#include + +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void_result add_sidechain_address_evaluator::do_evaluate(const sidechain_address_add_operation& op) +{ try{ + FC_ASSERT( op.deposit_public_key.length() > 0 && op.deposit_address.length() == 0 && op.deposit_address_data.length() == 0, "User should add a valid deposit public key and a null deposit address"); + const auto& sdpke_idx = db().get_index_type().indices().get(); + FC_ASSERT( sdpke_idx.find(boost::make_tuple(op.sidechain, op.deposit_public_key, time_point_sec::maximum())) == sdpke_idx.end(), "An active deposit key already exists" ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address_add_operation& op) +{ try { + const auto &sidechain_addresses_idx = db().get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum())); + + if (addr_itr != sidechain_addresses_idx.end()) + { + db().modify(*addr_itr, [&](sidechain_address_object &sao) { + sao.expires = db().head_block_time(); + }); + } + + const auto& new_sidechain_address_object = db().create( [&]( sidechain_address_object& obj ){ + obj.sidechain_address_account = op.sidechain_address_account; + obj.sidechain = op.sidechain; + obj.deposit_public_key = op.deposit_public_key; + obj.deposit_address = ""; + obj.deposit_address_data = ""; + obj.withdraw_public_key = op.withdraw_public_key; + obj.withdraw_address = op.withdraw_address; + obj.valid_from = db().head_block_time(); + obj.expires = time_point_sec::maximum(); + }); + return new_sidechain_address_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_address_update_operation& op) +{ try { + const auto& sidx = db().get_index_type().indices().get(); + const auto& son_obj = sidx.find(op.payer); + FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" ); + const auto& sdpke_idx = db().get_index_type().indices().get(); + FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid"); + FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key"); + FC_ASSERT( sdpke_idx.find(boost::make_tuple(op.sidechain, *op.deposit_public_key, time_point_sec::maximum())) != sdpke_idx.end(), "Invalid update operation by SON" ); + FC_ASSERT( db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account, "Invalid sidechain address account" ); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end(), "Invalid sidechain address ID" ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_address_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.sidechain_address_id); + if(itr != idx.end()) + { + // Case of change of Active SONs, store the outgoing address object with proper valid_from and expires updated + if(itr->deposit_address.length() > 0) { + db().create( [&]( sidechain_address_object& obj ) { + obj.sidechain_address_account = op.sidechain_address_account; + obj.sidechain = op.sidechain; + obj.deposit_public_key = *op.deposit_public_key; + obj.deposit_address = itr->deposit_address; + obj.deposit_address_data = itr->deposit_address_data; + obj.withdraw_public_key = *op.withdraw_public_key; + obj.withdraw_address = *op.withdraw_address; + obj.valid_from = itr->valid_from; + obj.expires = db().head_block_time(); + }); + db().modify(*itr, [&](sidechain_address_object &sao) { + if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address; + if(op.deposit_address_data.valid()) sao.deposit_address_data = *op.deposit_address_data; + sao.valid_from = db().head_block_time(); + }); + } else { + // Case of SON creating deposit address for a user input + db().modify(*itr, [&op](sidechain_address_object &sao) { + if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address; + if(op.deposit_address_data.valid()) sao.deposit_address_data = *op.deposit_address_data; + }); + } + } + return op.sidechain_address_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_sidechain_address_evaluator::do_evaluate(const sidechain_address_delete_operation& op) +{ try { + FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address_delete_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto sidechain_address = idx.find(op.sidechain_address_id); + if(sidechain_address != idx.end()) { + db().modify(*sidechain_address, [&](sidechain_address_object &sao) { + sao.expires = db().head_block_time(); + }); + } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp new file mode 100644 index 000000000..12bf2f42f --- /dev/null +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -0,0 +1,159 @@ +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + + FC_ASSERT((op.object_id.is() || op.object_id.is() || op.object_id.is()), "Invalid object id"); + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.object_id); + FC_ASSERT(sto_obj == sto_idx.end(), "Sidechain transaction for a given object is already created"); + + FC_ASSERT(!op.transaction.empty(), "Sidechain transaction data not set"); + FC_ASSERT(op.signers.size() > 0, "Sidechain transaction signers not set"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) +{ try { + const auto &new_sidechain_transaction_object = db().create([&](sidechain_transaction_object &sto) { + sto.sidechain = op.sidechain; + sto.object_id = op.object_id; + sto.transaction = op.transaction; + sto.signers = op.signers; + std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) { + return std::make_pair(si.son_id, std::string()); + }); + for (const auto &si : op.signers) { + sto.total_weight = sto.total_weight + si.weight; + } + sto.sidechain_transaction = ""; + sto.current_weight = 0; + sto.threshold = sto.total_weight * 2 / 3 + 1; + sto.status = sidechain_transaction_status::valid; + }); + return new_sidechain_transaction_object.id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_transaction_sign_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); + + const auto &son_idx = db().get_index_type().indices().get(); + const auto &son_obj = son_idx.find(op.signer); + FC_ASSERT(son_obj != son_idx.end(), "SON object not found"); + + bool expected = false; + for (auto signature : sto_obj->signatures) { + if (signature.first == son_obj->id) { + expected = signature.second.empty(); + } + } + FC_ASSERT(expected, "Signer not expected"); + + FC_ASSERT(sto_obj->status == sidechain_transaction_status::valid, "Invalid transaction status"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_transaction_sign_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + const auto &son_idx = db().get_index_type().indices().get(); + auto son_obj = son_idx.find(op.signer); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + for (size_t i = 0; i < sto.signatures.size(); i++) { + if (sto.signatures.at(i).first == son_obj->id) { + sto.signatures.at(i).second = op.signature; + } + } + for (size_t i = 0; i < sto.signers.size(); i++) { + if (sto.signers.at(i).son_id == son_obj->id) { + sto.current_weight = sto.current_weight + sto.signers.at(i).weight; + } + } + if (sto.current_weight >= sto.threshold) { + sto.status = sidechain_transaction_status::complete; + } + }); + + db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) { + sso.txs_signed += 1; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_transaction_send_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); + + FC_ASSERT(sto_obj->status == sidechain_transaction_status::complete, "Invalid transaction status"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_transaction_send_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.sidechain_transaction = op.sidechain_transaction; + sto.status = sidechain_transaction_status::sent; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + + +void_result sidechain_transaction_settle_evaluator::do_evaluate(const sidechain_transaction_settle_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); + + FC_ASSERT(sto_obj->status == sidechain_transaction_status::sent, "Invalid transaction status"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_settle_evaluator::do_apply(const sidechain_transaction_settle_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.status = sidechain_transaction_status::settled; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +} // namespace chain +} // namespace graphene diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp new file mode 100644 index 000000000..9c48c3e09 --- /dev/null +++ b/libraries/chain/son_evaluator.cpp @@ -0,0 +1,243 @@ +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_evaluator::do_evaluate(const son_create_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT(db().get(op.owner_account).is_lifetime_member(), "Only Lifetime members may register a SON."); + FC_ASSERT(op.deposit(db()).policy.which() == vesting_policy::tag::value, + "Deposit balance must have dormant vesting policy"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_evaluator::do_apply(const son_create_operation& op) +{ try { + vote_id_type vote_id; + db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { + vote_id = get_next_vote_id(p, vote_id_type::son); + }); + + const auto& new_son_object = db().create( [&]( son_object& obj ){ + obj.son_account = op.owner_account; + obj.vote_id = vote_id; + obj.url = op.url; + obj.deposit = op.deposit; + obj.signing_key = op.signing_key; + obj.sidechain_public_keys = op.sidechain_public_keys; + obj.pay_vb = op.pay_vb; + obj.statistics = db().create([&](son_statistics_object& s){s.owner = obj.id;}).id; + }); + return new_son_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_son_evaluator::do_evaluate(const son_update_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_id) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_son_evaluator::do_apply(const son_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_object &so) { + if(op.new_url.valid()) so.url = *op.new_url; + if(op.new_deposit.valid()) so.deposit = *op.new_deposit; + if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key; + if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys; + if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb; + }); + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result deregister_son_evaluator::do_evaluate(const son_deregister_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass + // Only son account can deregister + FC_ASSERT(db().is_son_dereg_valid(op.son_id) && op.payer == db().get_global_properties().parameters.son_account()); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_id) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result deregister_son_evaluator::do_apply(const son_deregister_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + const auto& ss_idx = db().get_index_type().indices().get(); + auto son = idx.find(op.son_id); + if(son != idx.end()) { + vesting_balance_object deposit = son->deposit(db()); + linear_vesting_policy new_vesting_policy; + new_vesting_policy.begin_timestamp = db().head_block_time(); + new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period(); + new_vesting_policy.begin_balance = deposit.balance.amount; + + db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) { + vbo.policy = new_vesting_policy; + }); + + db().modify(*son, [&op](son_object &so) { + so.status = son_status::deregistered; + }); + + auto stats_obj = ss_idx.find(son->statistics); + if(stats_obj != ss_idx.end()) { + db().modify(*stats_obj, [&]( son_statistics_object& sso) { + sso.deregistered_timestamp = db().head_block_time(); + }); + } + } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + FC_ASSERT( itr != idx.end() ); + FC_ASSERT(itr->son_account == op.owner_account); + auto stats = itr->statistics( db() ); + // Inactive SONs need not send heartbeats + FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); + // Account for network delays + fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); + // Account for server ntp sync difference + fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); + FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); + FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); + FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); + FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + const global_property_object& gpo = db().get_global_properties(); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); + bool is_son_active = true; + + if(it_son == active_son_ids.end()) { + is_son_active = false; + } + + if(itr->status == son_status::in_maintenance) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); + sso.last_active_timestamp = op.ts; + } ); + + db().modify(*itr, [&is_son_active](son_object &so) { + if(is_son_active) { + so.status = son_status::active; + } else { + so.status = son_status::inactive; + } + }); + } else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.last_active_timestamp = op.ts; + } ); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_id) != idx.end() ); + auto itr = idx.find(op.son_id); + auto stats = itr->statistics( db() ); + FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); + FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { + db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) + { + sso.last_down_timestamp = op.down_ts; + }); + + db().modify(*itr, [&op](son_object &so) { + so.status = son_status::in_maintenance; + }); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + FC_ASSERT( itr != idx.end() ); + // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states + if(op.request_type == son_maintenance_request_type::request_maintenance) { + FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance"); + } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request"); + } else { + FC_ASSERT(false, "Invalid maintenance operation"); + } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_id); + if(itr != idx.end()) + { + if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) { + db().modify(*itr, [](son_object &so) { + so.status = son_status::request_maintenance; + }); + } else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + db().modify(*itr, [](son_object &so) { + so.status = son_status::active; + }); + } + } + return op.son_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/chain/son_object.cpp b/libraries/chain/son_object.cpp new file mode 100644 index 000000000..bf54c833c --- /dev/null +++ b/libraries/chain/son_object.cpp @@ -0,0 +1,15 @@ +#include +#include + +namespace graphene { namespace chain { + void son_object::pay_son_fee(share_type pay, database& db) { + db.adjust_balance(son_account, pay); + } + + bool son_object::has_valid_config()const { + return ((std::string(signing_key).length() > 0) && + (sidechain_public_keys.size() > 0) && + (sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) && + (sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0)); + } +}} diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp new file mode 100644 index 000000000..24a87e472 --- /dev/null +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -0,0 +1,157 @@ +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_create_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + + const auto &son_idx = db().get_index_type().indices().get(); + const auto so = son_idx.find(op.son_id); + FC_ASSERT(so != son_idx.end(), "SON not found"); + FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner"); + + const auto &ss_idx = db().get_index_type().indices().get(); + FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists"); + + const auto &swdo_idx = db().get_index_type().indices().get(); + const auto swdo = swdo_idx.find(op.sidechain_uid); + if (swdo == swdo_idx.end()) { + auto &gpo = db().get_global_properties(); + bool expected = false; + for (auto &si : gpo.active_sons) { + if (op.son_id == si.son_id) { + expected = true; + break; + } + } + FC_ASSERT(expected, "Only active SON can create deposit"); + } else { + bool exactly_the_same = true; + exactly_the_same = exactly_the_same && (swdo->sidechain == op.sidechain); + exactly_the_same = exactly_the_same && (swdo->sidechain_uid == op.sidechain_uid); + exactly_the_same = exactly_the_same && (swdo->sidechain_transaction_id == op.sidechain_transaction_id); + exactly_the_same = exactly_the_same && (swdo->sidechain_from == op.sidechain_from); + exactly_the_same = exactly_the_same && (swdo->sidechain_to == op.sidechain_to); + exactly_the_same = exactly_the_same && (swdo->sidechain_currency == op.sidechain_currency); + exactly_the_same = exactly_the_same && (swdo->sidechain_amount == op.sidechain_amount); + exactly_the_same = exactly_the_same && (swdo->peerplays_from == op.peerplays_from); + exactly_the_same = exactly_the_same && (swdo->peerplays_to == op.peerplays_to); + exactly_the_same = exactly_the_same && (swdo->peerplays_asset == op.peerplays_asset); + FC_ASSERT(exactly_the_same, "Invalid withdraw confirmation"); + + bool expected = false; + for (auto &exp : swdo->expected_reports) { + if (op.son_id == exp.first) { + expected = true; + break; + } + } + FC_ASSERT(expected, "Confirmation from this SON not expected"); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_create_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.sidechain_uid); + if (itr == idx.end()) { + const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swdo ){ + swdo.timestamp = op.timestamp; + swdo.block_num = op.block_num; + swdo.sidechain = op.sidechain; + swdo.sidechain_uid = op.sidechain_uid; + swdo.sidechain_transaction_id = op.sidechain_transaction_id; + swdo.sidechain_from = op.sidechain_from; + swdo.sidechain_to = op.sidechain_to; + swdo.sidechain_currency = op.sidechain_currency; + swdo.sidechain_amount = op.sidechain_amount; + swdo.peerplays_from = op.peerplays_from; + swdo.peerplays_to = op.peerplays_to; + swdo.peerplays_asset = op.peerplays_asset; + + auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons) { + swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); + + auto stats_itr = db().get_index_type().indices().get().find(si.son_id); + db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { + sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1; + if (si.son_id == op.son_id) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + } + }); + } + + swdo.received_reports.insert(op.son_id); + + uint64_t total_weight = 0; + for (const auto exp : swdo.expected_reports) { + total_weight = total_weight + exp.second; + } + uint64_t current_weight = 0; + for (const auto rec : swdo.received_reports) { + current_weight = current_weight + swdo.expected_reports.find(rec)->second; + } + swdo.confirmed = (current_weight > (total_weight * 2 / 3)); + + swdo.processed = false; + }); + return new_son_wallet_deposit_object.id; + } else { + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.received_reports.insert(op.son_id); + + uint64_t total_weight = 0; + for (const auto exp : swdo.expected_reports) { + total_weight = total_weight + exp.second; + } + uint64_t current_weight = 0; + for (const auto rec : swdo.received_reports) { + current_weight = current_weight + swdo.expected_reports.find(rec)->second; + } + swdo.confirmed = (current_weight > (total_weight * 2 / 3)); + }); + auto stats_itr = db().get_index_type().indices().get().find(op.son_id); + db().modify(*stats_itr, [&op](son_statistics_object &sso) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + }); + return (*itr).id; + } +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_process_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); + + const auto& idx = db().get_index_type().indices().get(); + const auto& itr = idx.find(op.son_wallet_deposit_id); + FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); + FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_deposit_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.processed = true; + }); + } + return op.son_wallet_deposit_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp new file mode 100644 index 000000000..0baed1cb0 --- /dev/null +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include + +namespace graphene { namespace chain { + +void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*itr).sons; + auto new_wallet_sons = op.sons; + + bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); + if (son_sets_equal) { + for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { + son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i); + } + } + + FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed."); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + db().modify(*itr, [&, op](son_wallet_object &swo) { + swo.expires = db().head_block_time(); + }); + } + + const auto& new_son_wallet_object = db().create( [&]( son_wallet_object& obj ){ + obj.valid_from = db().head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons = op.sons; + }); + return new_son_wallet_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); + //auto itr = idx.find(op.son_wallet_id); + //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || + // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_id); + if (itr != idx.end()) + { + if (itr->addresses.find(op.sidechain) == itr->addresses.end()) { + db().modify(*itr, [&op](son_wallet_object &swo) { + swo.addresses[op.sidechain] = op.address; + }); + } + } + return op.son_wallet_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp new file mode 100644 index 000000000..bf6adaf96 --- /dev/null +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -0,0 +1,155 @@ +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + + const auto &son_idx = db().get_index_type().indices().get(); + const auto so = son_idx.find(op.son_id); + FC_ASSERT(so != son_idx.end(), "SON not found"); + FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner"); + + const auto &ss_idx = db().get_index_type().indices().get(); + FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists"); + + const auto &swwo_idx = db().get_index_type().indices().get(); + const auto swwo = swwo_idx.find(op.peerplays_uid); + if (swwo == swwo_idx.end()) { + auto &gpo = db().get_global_properties(); + bool expected = false; + for (auto &si : gpo.active_sons) { + if (op.son_id == si.son_id) { + expected = true; + break; + } + } + FC_ASSERT(expected, "Only active SON can create deposit"); + } else { + bool exactly_the_same = true; + exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain); + exactly_the_same = exactly_the_same && (swwo->peerplays_uid == op.peerplays_uid); + exactly_the_same = exactly_the_same && (swwo->peerplays_transaction_id == op.peerplays_transaction_id); + exactly_the_same = exactly_the_same && (swwo->peerplays_from == op.peerplays_from); + exactly_the_same = exactly_the_same && (swwo->peerplays_asset == op.peerplays_asset); + exactly_the_same = exactly_the_same && (swwo->withdraw_sidechain == op.withdraw_sidechain); + exactly_the_same = exactly_the_same && (swwo->withdraw_address == op.withdraw_address); + exactly_the_same = exactly_the_same && (swwo->withdraw_currency == op.withdraw_currency); + exactly_the_same = exactly_the_same && (swwo->withdraw_amount == op.withdraw_amount); + FC_ASSERT(exactly_the_same, "Invalid withdraw confirmation"); + + bool expected = false; + for (auto &exp : swwo->expected_reports) { + if (op.son_id == exp.first) { + expected = true; + break; + } + } + FC_ASSERT(expected, "Confirmation from this SON not expected"); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_create_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.peerplays_uid); + if (itr == idx.end()) { + const auto& new_son_wallet_withdraw_object = db().create( [&]( son_wallet_withdraw_object& swwo ){ + swwo.timestamp = op.timestamp; + swwo.block_num = op.block_num; + swwo.sidechain = op.sidechain; + swwo.peerplays_uid = op.peerplays_uid; + swwo.peerplays_transaction_id = op.peerplays_transaction_id; + swwo.peerplays_from = op.peerplays_from; + swwo.peerplays_asset = op.peerplays_asset; + swwo.withdraw_sidechain = op.withdraw_sidechain; + swwo.withdraw_address = op.withdraw_address; + swwo.withdraw_currency = op.withdraw_currency; + swwo.withdraw_amount = op.withdraw_amount; + + auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons) { + swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); + + auto stats_itr = db().get_index_type().indices().get().find(si.son_id); + db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { + sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1; + if (si.son_id == op.son_id) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + } + }); + } + + swwo.received_reports.insert(op.son_id); + + uint64_t total_weight = 0; + for (const auto exp : swwo.expected_reports) { + total_weight = total_weight + exp.second; + } + uint64_t current_weight = 0; + for (const auto rec : swwo.received_reports) { + current_weight = current_weight + swwo.expected_reports.find(rec)->second; + } + swwo.confirmed = (current_weight > (total_weight * 2 / 3)); + + swwo.processed = false; + }); + return new_son_wallet_withdraw_object.id; + } else { + db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) { + swwo.received_reports.insert(op.son_id); + + uint64_t total_weight = 0; + for (const auto exp : swwo.expected_reports) { + total_weight = total_weight + exp.second; + } + uint64_t current_weight = 0; + for (const auto rec : swwo.received_reports) { + current_weight = current_weight + swwo.expected_reports.find(rec)->second; + } + swwo.confirmed = (current_weight > (total_weight * 2 / 3)); + }); + auto stats_itr = db().get_index_type().indices().get().find(op.son_id); + db().modify(*stats_itr, [&op](son_statistics_object &sso) { + sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1; + }); + return (*itr).id; + } +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); + + const auto& idx = db().get_index_type().indices().get(); + const auto& itr = idx.find(op.son_wallet_withdraw_id); + FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); + FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_process_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_withdraw_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) { + swwo.processed = true; + }); + } + return op.son_wallet_withdraw_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index e81383b64..56eef1933 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -42,6 +42,9 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); + if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass + FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() ); + if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass FC_ASSERT( op.balance_type == vesting_balance_type::normal); @@ -79,6 +82,11 @@ struct init_policy_visitor policy.coin_seconds_earned_last_update = now; p = policy; } + void operator()( const dormant_vesting_policy_initializer& i )const + { + dormant_vesting_policy policy; + p = policy; + } }; object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op ) @@ -110,8 +118,6 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance } obj.balance_type = op.balance_type; } ); - - return vbo.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -133,7 +139,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan const time_point_sec now = d.head_block_time(); const vesting_balance_object& vbo = op.vesting_balance( d ); - if(vbo.balance_type == vesting_balance_type::normal) + if(vbo.balance_type == vesting_balance_type::normal || vbo.balance_type == vesting_balance_type::son) { FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) ); FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "Account has insufficient ${balance_type} Vested Balance to withdraw", @@ -173,7 +179,7 @@ void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_ //Handling all GPOS withdrawls separately from normal and SONs(future extension). // One request/transaction would be sufficient to withdraw from multiple vesting balance ids const vesting_balance_object& vbo = op.vesting_balance( d ); - if(vbo.balance_type == vesting_balance_type::normal) + if(vbo.balance_type == vesting_balance_type::normal || vbo.balance_type == vesting_balance_type::son) { // Allow zero balance objects to stick around, (1) to comply // with the chain's "objects live forever" design principle, (2) diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 4674c9742..3334d4f6a 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -169,6 +169,33 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c return (ctx.amount <= get_allowed_withdraw(ctx)); } +asset dormant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const +{ + share_type allowed_withdraw = 0; + return asset( allowed_withdraw, ctx.balance.asset_id ); +} + +void dormant_vesting_policy::on_deposit(const vesting_policy_context& ctx) +{ +} + +bool dormant_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount.asset_id == ctx.balance.asset_id) + && sum_below_max_shares(ctx.amount, ctx.balance); +} + +void dormant_vesting_policy::on_withdraw(const vesting_policy_context& ctx) +{ +} + +bool dormant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount.asset_id == ctx.balance.asset_id) + && (ctx.amount <= get_allowed_withdraw(ctx)); +} + + #define VESTING_VISITOR(NAME, MAYBE_CONST) \ struct NAME ## _visitor \ { \ diff --git a/libraries/fc b/libraries/fc index 9fa98d9a9..fb27454cd 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 9fa98d9a93ada3e84f48812b5c42b053b0dbf9da +Subproject commit fb27454cdf1626e5e108e42848bd1bcfe60c9540 diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 58728a9e5..d2a5be164 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -10,4 +10,5 @@ add_subdirectory( generate_genesis ) add_subdirectory( generate_uia_sharedrop_genesis ) add_subdirectory( debug_witness ) add_subdirectory( snapshot ) +add_subdirectory( peerplays_sidechain ) add_subdirectory( es_objects ) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 67322f800..d1aa2f71d 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -123,12 +124,15 @@ void account_history_plugin_impl::update_account_histories( const signed_block& // get the set of accounts this operation applies to flat_set impacted; vector other; - operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here + // fee payer is added here + operation_get_required_authorities( op.op, impacted, impacted, other, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - graphene::chain::operation_get_impacted_accounts( op.op, impacted ); + graphene::chain::operation_get_impacted_accounts( op.op, impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) ); if( op.op.which() == operation::tag< lottery_end_operation >::value ) { auto lop = op.op.get< lottery_end_operation >(); diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index d49129b08..3eadda344 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -65,7 +65,7 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c void delayed_node_plugin::connect() { - my->client_connection = std::make_shared(*my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); + my->client_connection = std::make_shared(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); my->database_api = my->client_connection->get_remote_api(0); my->client_connection_closed = my->client_connection->closed.connect([this] { connection_failed(); diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index 484aef9cf..dc167b05a 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include namespace graphene { namespace elasticsearch { @@ -157,12 +157,15 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b // get the set of accounts this operation applies to flat_set impacted; vector other; - operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here + // fee_payer is added here + operation_get_required_authorities( op.op, impacted, impacted, other, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - graphene::chain::operation_get_impacted_accounts( op.op, impacted ); + operation_get_impacted_accounts( op.op, impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); for( auto& a : other ) for( auto& item : a.account_auths ) diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt new file mode 100755 index 000000000..3e37b6fe1 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -0,0 +1,51 @@ +file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp") + +add_library( peerplays_sidechain + peerplays_sidechain_plugin.cpp + sidechain_net_manager.cpp + sidechain_net_handler.cpp + sidechain_net_handler_bitcoin.cpp + sidechain_net_handler_peerplays.cpp + bitcoin/bech32.cpp + bitcoin/bitcoin_address.cpp + bitcoin/bitcoin_script.cpp + bitcoin/bitcoin_transaction.cpp + bitcoin/segwit_addr.cpp + bitcoin/utils.cpp + bitcoin/sign_bitcoin_transaction.cpp + ) + +if (ENABLE_DEV_FEATURES) + set(ENABLE_MULTIPLE_SONS 1) + set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1) +endif() +unset(ENABLE_DEV_FEATURES) +unset(ENABLE_DEV_FEATURES CACHE) + +if (ENABLE_MULTIPLE_SONS) + message ("Multiple SONs per software instance are supported") + target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS) +endif() +unset(ENABLE_MULTIPLE_SONS) +unset(ENABLE_MULTIPLE_SONS CACHE) + +if (ENABLE_PEERPLAYS_ASSET_DEPOSITS) + message ("Depositing Peerplays assets enabled") + target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS) +endif() +unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) +unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) + +target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) +target_include_directories( peerplays_sidechain + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +install( TARGETS + peerplays_sidechain + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/peerplays_sidechain" ) + diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp new file mode 100644 index 000000000..69d157e49 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +// #include + +namespace { + +typedef std::vector data; + +/** The Bech32 character set for encoding. */ +const char *CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Bech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data &y) { + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data &v) { + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Bech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint32_t c = 1; + for (auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 25; + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x1ffffff) << 5) ^ v_i; + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) + c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} + if (c0 & 2) + c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} + if (c0 & 4) + c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} + if (c0 & 8) + c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} + if (c0 & 16) + c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) { + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string &hrp) { + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string &hrp, const data &values) { + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Bech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string &hrp, const data &values) { + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 6); // Append 6 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(6); + for (size_t i = 0; i < 6; ++i) { + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +} // namespace + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 { + +/** Encode a Bech32 string. */ +std::string Encode(const std::string &hrp, const data &values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Bech32 string. */ +std::pair Decode(const std::string &str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c < 33 || c > 126) + return {}; + if (c >= 'a' && c <= 'z') + lower = true; + if (c >= 'A' && c <= 'Z') + upper = true; + } + if (lower && upper) + return {}; + size_t pos = str.rfind('1'); + if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 6)}; +} + +}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32 diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp new file mode 100644 index 000000000..4c10899f7 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp @@ -0,0 +1,455 @@ +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +bool bitcoin_address::operator==(const bitcoin_address &btc_addr) const { + return (this->address == btc_addr.address) && + (this->type == btc_addr.type) && + (this->raw_address == btc_addr.raw_address) && + (this->network_type == btc_addr.network_type); +} + +bytes bitcoin_address::get_script() const { + switch (type) { + case payment_type::NULLDATA: + return script_builder() << op::RETURN << raw_address; + case payment_type::P2PK: + return script_builder() << raw_address << op::CHECKSIG; + case payment_type::P2PKH: + return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG; + case payment_type::P2SH: + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: + return script_builder() << op::HASH160 << raw_address << op::EQUAL; + case payment_type::P2WPKH: + case payment_type::P2WSH: + return script_builder() << op::_0 << raw_address; + default: + return raw_address; + } +} + +payment_type bitcoin_address::determine_type() { + if (is_p2pk()) { + return payment_type::P2PK; + } else if (is_p2wpkh()) { + return payment_type::P2WPKH; + } else if (is_p2wsh()) { + return payment_type::P2WSH; + } else if (is_p2pkh()) { + return payment_type::P2PKH; + } else if (is_p2sh()) { + return payment_type::P2SH; + } else { + return payment_type::NULLDATA; + } +} + +bytes bitcoin_address::determine_raw_address() { + bytes result; + switch (type) { + case payment_type::P2PK: { + result = parse_hex(address); + break; + } + case payment_type::P2WPKH: + case payment_type::P2WSH: { + std::string prefix(address.compare(0, 4, "bcrt") == 0 ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2)); + const auto &decode_bech32 = segwit_addr::decode(prefix, address); + result = bytes(decode_bech32.second.begin(), decode_bech32.second.end()); + break; + } + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: + case payment_type::P2PKH: + case payment_type::P2SH: { + bytes hex_addr = fc::from_base58(address); + result = bytes(hex_addr.begin() + 1, hex_addr.begin() + 21); + break; + } + case payment_type::NULLDATA: + return result; + } + return result; +} + +bool bitcoin_address::check_segwit_address(const size_segwit_address &size) const { + if (!address.compare(0, 4, "bcrt") || !address.compare(0, 2, "bc") || !address.compare(0, 2, "tb")) { + std::string prefix(!address.compare(0, 4, "bcrt") ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2)); + + const auto &decode_bech32 = segwit_addr::decode(prefix, address); + + if (decode_bech32.first == -1 || decode_bech32.second.size() != size) { + return false; + } + + return true; + } + return false; +} + +bool bitcoin_address::is_p2pk() const { + try { + bool prefix = !address.compare(0, 2, "02") || !address.compare(0, 2, "03"); + if (address.size() == 66 && prefix) { + parse_hex(address); + return true; + } + } catch (fc::exception e) { + return false; + } + return false; +} + +bool bitcoin_address::is_p2wpkh() const { + return check_segwit_address(size_segwit_address::P2WPKH); +} + +bool bitcoin_address::is_p2wsh() const { + return check_segwit_address(size_segwit_address::P2WSH); +} + +bool bitcoin_address::is_p2pkh() const { + try { + bytes hex_addr = fc::from_base58(address); + if (hex_addr.size() == 25 && (static_cast(hex_addr[0]) == 0x00 || + static_cast(hex_addr[0]) == 0x6f)) { + return true; + } + return false; + } catch (fc::exception e) { + return false; + } +} + +bool bitcoin_address::is_p2sh() const { + try { + bytes hex_addr = fc::from_base58(address); + if (hex_addr.size() == 25 && (static_cast(hex_addr[0]) == 0x05 || + static_cast(hex_addr[0]) == 0xc4)) { + return true; + } + return false; + } catch (fc::exception e) { + return false; + } +} + +btc_multisig_address::btc_multisig_address(const size_t n_required, const accounts_keys &keys) : + keys_required(n_required), + witnesses_keys(keys) { + create_redeem_script(); + create_address(); + type = payment_type::P2SH; +} + +size_t btc_multisig_address::count_intersection(const accounts_keys &keys) const { + FC_ASSERT(keys.size() > 0); + + int intersections_count = 0; + for (auto &key : keys) { + auto witness_key = witnesses_keys.find(key.first); + if (witness_key == witnesses_keys.end()) + continue; + if (key.second == witness_key->second) + intersections_count++; + } + return intersections_count; +} + +void btc_multisig_address::create_redeem_script() { + FC_ASSERT(keys_required > 0); + FC_ASSERT(keys_required < witnesses_keys.size()); + redeem_script.clear(); + redeem_script.push_back(op_num[keys_required - 1]); + for (const auto &key : witnesses_keys) { + std::stringstream ss; + ss << std::hex << key.second.key_data.size(); + auto key_size_hex = parse_hex(ss.str()); + redeem_script.insert(redeem_script.end(), key_size_hex.begin(), key_size_hex.end()); + redeem_script.insert(redeem_script.end(), key.second.key_data.begin(), key.second.key_data.end()); + } + redeem_script.push_back(op_num[witnesses_keys.size() - 1]); + redeem_script.push_back(OP_CHECKMULTISIG); +} + +void btc_multisig_address::create_address() { + FC_ASSERT(redeem_script.size() > 0); + raw_address.clear(); + fc::sha256 hash256 = fc::sha256::hash(redeem_script.data(), redeem_script.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + bytes temp_addr_hash(parse_hex(hash160.str())); + + raw_address.push_back(OP_HASH160); + std::stringstream ss; + ss << std::hex << temp_addr_hash.size(); + auto address_size_hex = parse_hex(ss.str()); + raw_address.insert(raw_address.end(), address_size_hex.begin(), address_size_hex.end()); + raw_address.insert(raw_address.end(), temp_addr_hash.begin(), temp_addr_hash.end()); + raw_address.push_back(OP_EQUAL); +} + +btc_multisig_segwit_address::btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys) : + btc_multisig_address(n_required, keys) { + create_witness_script(); + create_segwit_address(); + type = payment_type::P2SH; +} + +bool btc_multisig_segwit_address::operator==(const btc_multisig_segwit_address &addr) const { + if (address != addr.address || redeem_script != addr.redeem_script || + witnesses_keys != addr.witnesses_keys || witness_script != addr.witness_script || + raw_address != addr.raw_address) + return false; + return true; +} + +std::vector btc_multisig_segwit_address::get_keys() { + std::vector keys; + for (const auto &k : witnesses_keys) { + keys.push_back(k.second); + } + return keys; +} + +void btc_multisig_segwit_address::create_witness_script() { + const auto redeem_sha256 = fc::sha256::hash(redeem_script.data(), redeem_script.size()); + witness_script.push_back(OP_0); + witness_script.push_back(0x20); // PUSH_32 + witness_script.insert(witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size()); +} + +void btc_multisig_segwit_address::create_segwit_address() { + fc::sha256 hash256 = fc::sha256::hash(witness_script.data(), witness_script.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + + raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size()); + address = fc::to_base58(get_address_bytes(raw_address)); +} + +bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) { + bytes address_bytes(1, TESTNET_SCRIPT); // 1 byte version + address_bytes.insert(address_bytes.end(), script_hash.begin(), script_hash.end()); + fc::sha256 hash256 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size())); + address_bytes.insert(address_bytes.end(), hash256.data(), hash256.data() + 4); // 4 byte checksum + + return address_bytes; +} + +btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector> &keys_data, + network ntype) { + network_type = ntype; + create_redeem_script(keys_data); + create_witness_script(); + create_segwit_address(); + type = payment_type::P2WSH; +} + +void btc_weighted_multisig_address::create_redeem_script(const std::vector> &keys_data) { + script_builder builder; + uint32_t total_weight = 0; + builder << uint32_t(0); + for (auto &p : keys_data) { + total_weight += p.second; + builder << op::SWAP; + builder << p.first.serialize(); + builder << op::CHECKSIG; + builder << op::IF; + builder << uint32_t(p.second); + builder << op::ADD; + builder << op::ENDIF; + } + uint32_t threshold_weight = total_weight * 2 / 3 + 1; + builder << threshold_weight; + builder << op::GREATERTHANOREQUAL; + + redeem_script_ = builder; + + fc::sha256 sh = fc::sha256::hash(redeem_script_); + raw_address = bytes(sh.data(), sh.data() + sh.data_size()); +} + +void btc_weighted_multisig_address::create_witness_script() { + script_builder builder; + builder << op::_0; + builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); + + witness_script_ = builder; +} + +void btc_weighted_multisig_address::create_segwit_address() { + std::string hrp; + switch (network_type) { + case (network::mainnet): + hrp = "bc"; + break; + case (network::testnet): + hrp = "tb"; + break; + case (network::regtest): + hrp = "bcrt"; + break; + } + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); +} + +btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, + const uint8_t nrequired, const std::vector &keys_data, + network ntype) { + network_type = ntype; + create_redeem_script(user_key_data, nrequired, keys_data); + create_witness_script(); + create_segwit_address(); + type = payment_type::P2WSH; +} +void btc_one_or_m_of_n_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, + const uint8_t nrequired, const std::vector &keys_data) { + script_builder builder; + builder << op::IF; + builder << user_key_data.serialize(); + builder << op::CHECKSIG; + builder << op::ELSE; + builder << static_cast(nrequired); + for (auto &key : keys_data) { + builder << key.serialize(); + } + builder << static_cast(keys_data.size()); + builder << op::CHECKMULTISIG; + builder << op::ENDIF; + redeem_script_ = builder; + fc::sha256 sh = fc::sha256::hash(redeem_script_); + raw_address = bytes(sh.data(), sh.data() + sh.data_size()); +} +void btc_one_or_m_of_n_multisig_address::create_witness_script() { + script_builder builder; + builder << op::_0; + builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); + witness_script_ = builder; +} +void btc_one_or_m_of_n_multisig_address::create_segwit_address() { + std::string hrp; + switch (network_type) { + case (network::mainnet): + hrp = "bc"; + break; + case (network::testnet): + hrp = "tb"; + break; + case (network::regtest): + hrp = "bcrt"; + break; + } + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); +} + +btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, + const std::vector> &keys_data, + bitcoin_address::network ntype) { + network_type = ntype; + create_redeem_script(user_key_data, keys_data); + create_witness_script(); + create_segwit_address(); + type = payment_type::P2WSH; +} + +void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data) { + script_builder builder; + builder << user_key_data.serialize(); + builder << op::CHECKSIG; + builder << op::IF; + builder << op::_1; + builder << op::ELSE; + uint32_t total_weight = 0; + builder << uint32_t(0); + for (auto &p : keys_data) { + total_weight += p.second; + builder << op::SWAP; + builder << p.first.serialize(); + builder << op::CHECKSIG; + builder << op::IF; + builder << uint32_t(p.second); + builder << op::ADD; + builder << op::ENDIF; + } + uint32_t threshold_weight = total_weight * 2 / 3 + 1; + builder << threshold_weight; + builder << op::GREATERTHANOREQUAL; + builder << op::ENDIF; + redeem_script_ = builder; + fc::sha256 sh = fc::sha256::hash(redeem_script_); + raw_address = bytes(sh.data(), sh.data() + sh.data_size()); +} + +void btc_one_or_weighted_multisig_address::create_witness_script() { + script_builder builder; + builder << op::_0; + builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); + witness_script_ = builder; +} + +void btc_one_or_weighted_multisig_address::create_segwit_address() { + std::string hrp; + switch (network_type) { + case (network::mainnet): + hrp = "bc"; + break; + case (network::testnet): + hrp = "tb"; + break; + case (network::regtest): + hrp = "bcrt"; + break; + } + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); +} + +btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector> &keys_data, bitcoin_address::network ntype) : + btc_one_or_weighted_multisig_address(user_key_data, keys_data, ntype), + latency_(latency) { + create_redeem_script(user_key_data, keys_data); + create_witness_script(); + create_segwit_address(); +} + +void btc_timelocked_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data) { + script_builder builder; + builder << user_key_data.serialize(); + builder << op::CHECKSIG; + builder << op::IF; + builder << uint32_t(latency_); + builder << op::CHECKSEQUENCEVERIFY; + builder << op::DROP; + builder << op::_1; + builder << op::ELSE; + uint32_t total_weight = 0; + builder << uint32_t(0); + for (auto &p : keys_data) { + total_weight += p.second; + builder << op::SWAP; + builder << p.first.serialize(); + builder << op::CHECKSIG; + builder << op::IF; + builder << uint32_t(p.second); + builder << op::ADD; + builder << op::ENDIF; + } + uint32_t threshold_weight = total_weight * 2 / 3 + 1; + builder << threshold_weight; + builder << op::GREATERTHANOREQUAL; + builder << op::ENDIF; + redeem_script_ = builder; + fc::sha256 sh = fc::sha256::hash(redeem_script_); + raw_address = bytes(sh.data(), sh.data() + sh.data_size()); +} + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_script.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_script.cpp new file mode 100644 index 000000000..07078147a --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_script.cpp @@ -0,0 +1,65 @@ +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +script_builder &script_builder::operator<<(op opcode) { + const auto op_byte = static_cast(opcode); + if (op_byte < 0 || op_byte > 0xff) + throw std::runtime_error("script_builder::operator<<(OP): invalid opcode"); + script.push_back(op_byte); + return *this; +} + +script_builder &script_builder::operator<<(uint32_t number) { + if (number == 0) { + script.push_back(static_cast(op::_0)); + } else if (number <= 16) { + script.push_back(static_cast(op::_1) + number - 1); + } else { + bytes pack_buf; + while (number) { + pack_buf.push_back(number & 0xff); + number >>= 8; + } + // - If the most significant byte is >= 0x80 and the value is positive, push a + // new zero-byte to make the significant byte < 0x80 again. So, the result can + // be 5 bytes max. + if (pack_buf.back() & 0x80) + pack_buf.push_back(0); + *this << pack_buf; + } + + return *this; +} + +script_builder &script_builder::operator<<(size_t size) { + write_compact_size(script, size); + return *this; +} + +script_builder &script_builder::operator<<(const bytes &sc) { + write_compact_size(script, sc.size()); + script.insert(script.end(), sc.begin(), sc.end()); + return *this; +} + +script_builder &script_builder::operator<<(const fc::sha256 &hash) { + write_compact_size(script, hash.data_size()); + script.insert(script.end(), hash.data(), hash.data() + hash.data_size()); + return *this; +} + +script_builder &script_builder::operator<<(const fc::ripemd160 &hash) { + write_compact_size(script, hash.data_size()); + script.insert(script.end(), hash.data(), hash.data() + hash.data_size()); + return *this; +} + +script_builder &script_builder::operator<<(const fc::ecc::public_key_data &pubkey_data) { + write_compact_size(script, pubkey_data.size()); + script.insert(script.end(), pubkey_data.begin(), pubkey_data.begin() + pubkey_data.size()); + return *this; +} + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_transaction.cpp new file mode 100644 index 000000000..b4fde6dc0 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_transaction.cpp @@ -0,0 +1,257 @@ +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +bool out_point::operator==(const out_point &op) const { + if (this->hash == op.hash && + this->n == op.n) { + return true; + } + return false; +} + +bool tx_in::operator==(const tx_in &ti) const { + if (this->prevout == ti.prevout && + this->scriptSig == ti.scriptSig && + this->nSequence == ti.nSequence) { + return true; + } + return false; +} + +bool tx_out::operator==(const tx_out &to) const { + if (this->value == to.value && + this->scriptPubKey == to.scriptPubKey) { + return true; + } + return false; +} + +bool tx_out::is_p2wsh() const { + if (scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast(0x00) && scriptPubKey[1] == static_cast(0x20)) { + return true; + } + return false; +} + +bool tx_out::is_p2wpkh() const { + if (scriptPubKey.size() == 22 && scriptPubKey[0] == static_cast(0x00) && scriptPubKey[1] == static_cast(0x14)) { + return true; + } + return false; +} + +bool tx_out::is_p2pkh() const { + if (scriptPubKey.size() == 25 && scriptPubKey[0] == static_cast(0x76) && scriptPubKey[1] == static_cast(0xa9) && + scriptPubKey[2] == static_cast(0x14) && scriptPubKey[23] == static_cast(0x88) && scriptPubKey[24] == static_cast(0xac)) { + return true; + } + return false; +} + +bool tx_out::is_p2sh() const { + if (scriptPubKey.size() == 23 && scriptPubKey[0] == static_cast(0xa9) && + scriptPubKey[1] == static_cast(0x14) && scriptPubKey[22] == static_cast(0x87)) { + return true; + } + return false; +} + +bool tx_out::is_p2pk() const { + if (scriptPubKey.size() == 35 && scriptPubKey[0] == static_cast(0x21) && scriptPubKey[34] == static_cast(0xac)) { + return true; + } + return false; +} + +bytes tx_out::get_data_or_script() const { + if (is_p2pkh()) { + return bytes(scriptPubKey.begin() + 3, scriptPubKey.begin() + 23); + } else if (is_p2sh() || is_p2wpkh()) { + return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22); + } else if (is_p2wsh()) { + return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 34); + } else if (is_p2pk()) { + return bytes(scriptPubKey.begin() + 1, scriptPubKey.begin() + 34); + } + return scriptPubKey; +} + +bool bitcoin_transaction::operator!=(const bitcoin_transaction &bt) const { + if (this->nVersion != bt.nVersion || + this->vin != bt.vin || + this->vout != bt.vout || + this->nLockTime != bt.nLockTime) { + return true; + } + return false; +} + +fc::sha256 bitcoin_transaction::get_hash() const { + const auto bytes = pack(*this, true); + const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size())); + std::reverse(hash.data(), hash.data() + hash.data_size()); + return hash; +} + +fc::sha256 bitcoin_transaction::get_txid() const { + const auto bytes = pack(*this, false); + const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size())); + std::reverse(hash.data(), hash.data() + hash.data_size()); + return hash; +} + +size_t bitcoin_transaction::get_vsize() const { + static const auto witness_scale_factor = 4; + + fc::datastream no_wit_ds; + pack(no_wit_ds, *this, false); + + fc::datastream wit_ds; + pack(wit_ds, *this, true); + + const size_t weight = no_wit_ds.tellp() * (witness_scale_factor - 1) + wit_ds.tellp(); + const size_t vsize = (weight + witness_scale_factor - 1) / witness_scale_factor; + + return vsize; +} + +void bitcoin_transaction_builder::set_version(int32_t version) { + tx.nVersion = version; +} + +void bitcoin_transaction_builder::set_locktime(uint32_t lock_time) { + tx.nLockTime = lock_time; +} + +void bitcoin_transaction_builder::add_in(const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) { + add_in(payment_type::P2WSH, txid, n_out, script_code, front, sequence); +} + +void bitcoin_transaction_builder::add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) { + out_point prevout; + prevout.hash = txid; + prevout.n = n_out; + + tx_in txin; + txin.prevout = prevout; + txin.nSequence = sequence; + + add_in(type, txin, script_code, front); +} + +void bitcoin_transaction_builder::add_in(payment_type type, tx_in txin, const bytes &script_code, bool front) { + switch (type) { + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: + FC_ASSERT(script_code != bytes()); + txin.scriptSig = script_code; + break; + default: { + if (txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000")) { //coinbase + FC_ASSERT(script_code != bytes()); + txin.scriptSig = script_code; + } + break; + } + } + + if (front) { + tx.vin.insert(tx.vin.begin(), txin); + } else { + tx.vin.push_back(txin); + } +} + +void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front) { + // TODO: add checks + const auto address_bytes = fc::from_base58(base58_address); + add_out(type, amount, bytes(address_bytes.begin() + 1, address_bytes.begin() + 1 + 20), front); +} + +void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front) { + FC_ASSERT(is_payment_to_pubkey(type)); + + if (type == payment_type::P2PK) { + const auto pubkey_bytes = bytes(pubkey.begin(), pubkey.begin() + pubkey.size()); + add_out(type, amount, pubkey_bytes, front); + } else { + const auto hash256 = fc::sha256::hash(pubkey.begin(), pubkey.size()); + const auto hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + add_out(type, amount, bytes(hash160.data(), hash160.data() + hash160.data_size()), front); + } +} + +void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const bytes &script_code, bool front) { + tx_out out; + out.value = amount; + out.scriptPubKey = get_script_pubkey(type, script_code); + + if (front) { + tx.vout.insert(tx.vout.begin(), out); + } else { + tx.vout.push_back(out); + } +} + +void bitcoin_transaction_builder::add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front) { + switch (address.get_type()) { + case payment_type::P2PK: { + bytes raw_address(address.get_raw_address()); + fc::ecc::public_key_data public_key; + std::copy(raw_address.begin(), raw_address.end(), public_key.begin()); + add_out(address.get_type(), amount, public_key, front); + break; + } + case payment_type::P2PKH: + case payment_type::P2SH: + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: { + add_out(address.get_type(), amount, address.get_address(), front); + break; + } + case payment_type::P2WPKH: + case payment_type::P2WSH: { + add_out(address.get_type(), amount, address.get_raw_address(), front); + break; + } + default: + break; + } +} + +inline bool bitcoin_transaction_builder::is_payment_to_pubkey(payment_type type) { + switch (type) { + case payment_type::P2PK: + case payment_type::P2PKH: + case payment_type::P2WPKH: + case payment_type::P2SH_WPKH: + return true; + default: + return false; + } +} + +bytes bitcoin_transaction_builder::get_script_pubkey(payment_type type, const bytes &script_code) { + switch (type) { + case payment_type::NULLDATA: + return script_builder() << op::RETURN << script_code; + case payment_type::P2PK: + return script_builder() << script_code << op::CHECKSIG; + case payment_type::P2PKH: + return script_builder() << op::DUP << op::HASH160 << script_code << op::EQUALVERIFY << op::CHECKSIG; + case payment_type::P2SH: + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: + return script_builder() << op::HASH160 << script_code << op::EQUAL; + case payment_type::P2WPKH: + case payment_type::P2WSH: + return script_builder() << op::_0 << script_code; + default: + return script_code; + } +} +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp new file mode 100644 index 000000000..2fd9be43e --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp @@ -0,0 +1,82 @@ +/* Copyright (c) 2017 Pieter Wuille + * + * 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 +#include + +namespace { + +typedef std::vector data; + +/** Convert from one power-of-2 number base to another. */ +template +bool convertbits(data &out, const data &in) { + int acc = 0; + int bits = 0; + const int maxv = (1 << tobits) - 1; + const int max_acc = (1 << (frombits + tobits - 1)) - 1; + for (size_t i = 0; i < in.size(); ++i) { + int value = in[i]; + acc = ((acc << frombits) | value) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + } + if (pad) { + if (bits) + out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + +} // namespace + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr { + +/** Decode a SegWit address. */ +std::pair decode(const std::string &hrp, const std::string &addr) { + std::pair dec = bech32::Decode(addr); + if (dec.first != hrp || dec.second.size() < 1) + return std::make_pair(-1, data()); + data conv; + if (!convertbits<5, 8, false>(conv, data(dec.second.begin() + 1, dec.second.end())) || + conv.size() < 2 || conv.size() > 40 || dec.second[0] > 16 || (dec.second[0] == 0 && conv.size() != 20 && conv.size() != 32)) { + return std::make_pair(-1, data()); + } + return std::make_pair(dec.second[0], conv); +} + +/** Encode a SegWit address. */ +std::string encode(const std::string &hrp, int witver, const data &witprog) { + data enc; + enc.push_back(witver); + convertbits<8, 5, true>(enc, witprog); + std::string ret = bech32::Encode(hrp, enc); + if (decode(hrp, ret).first == -1) + return ""; + return ret; +} + +}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp new file mode 100644 index 000000000..edb45c5ba --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp @@ -0,0 +1,153 @@ +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +const secp256k1_context_t *btc_context() { + static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; +} + +fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptCode, int64_t amount, + size_t in_index, int hash_type, bool is_witness) { + fc::datastream ps; + if (is_witness) + pack_tx_witness_signature(ps, scriptCode, tx, in_index, amount, hash_type); + else + pack_tx_signature(ps, scriptCode, tx, in_index, hash_type); + + std::vector vec(ps.tellp()); + if (!vec.empty()) { + fc::datastream ds(vec.data(), vec.size()); + if (is_witness) + pack_tx_witness_signature(ds, scriptCode, tx, in_index, amount, hash_type); + else + pack_tx_signature(ds, scriptCode, tx, in_index, hash_type); + } + + return fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size())); +} + +std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign) { + bytes sig; + sig.resize(72); + int sig_len = sig.size(); + + FC_ASSERT(secp256k1_ecdsa_sign( + context_sign, + reinterpret_cast(hash.data()), + reinterpret_cast(sig.data()), + &sig_len, + reinterpret_cast(privkey.data()), + secp256k1_nonce_function_rfc6979, + nullptr)); // TODO: replace assert with exception + + sig.resize(sig_len); + + return sig; +} + +std::vector sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector &redeem_scripts, + const std::vector &amounts, const bytes &privkey, + const secp256k1_context_t *context_sign, int hash_type) { + FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size()); + FC_ASSERT(!privkey.empty()); + + std::vector signatures; + for (size_t i = 0; i < tx.vin.size(); i++) { + const auto sighash = get_signature_hash(tx, redeem_scripts[i], static_cast(amounts[i]), i, hash_type, true); + auto sig = privkey_sign(privkey, sighash, context_sign); + sig.push_back(static_cast(hash_type)); + + signatures.push_back(sig); + } + return signatures; +} + +void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector &redeem_scripts, bool use_mulisig_workaround) { + FC_ASSERT(tx.vin.size() == redeem_scripts.size()); + + for (size_t i = 0; i < tx.vin.size(); i++) { + if (use_mulisig_workaround) + tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), bytes()); // Bitcoin workaround CHECKMULTISIG bug + tx.vin[i].scriptWitness.push_back(redeem_scripts[i]); + } +} + +bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) { + std::vector sig_temp(sig.begin(), sig.end()); + std::vector pubkey_temp(pubkey.begin(), pubkey.end()); + std::vector msg_temp(msg.begin(), msg.end()); + + int result = secp256k1_ecdsa_verify(context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size()); + return result == 1; +} + +std::vector> sort_sigs(const bitcoin_transaction &tx, const std::vector &redeem_scripts, + const std::vector &amounts, const secp256k1_context_t *context) { + FC_ASSERT(redeem_scripts.size() == amounts.size()); + + using data = std::pair; + struct comp { + bool operator()(const data &lhs, const data &rhs) const { + return lhs.first < rhs.first; + } + }; + + std::vector> new_stacks; + + for (size_t i = 0; i < redeem_scripts.size(); i++) { + const std::vector &keys = get_pubkey_from_redeemScript(redeem_scripts[i]); + const auto &sighash = get_signature_hash(tx, redeem_scripts[i], static_cast(amounts[i]), i, 1, true).str(); + bytes sighash_temp(parse_hex(sighash)); + + std::vector stack(tx.vin[i].scriptWitness); + std::vector marker(tx.vin[i].scriptWitness.size(), false); + std::set sigs; + + for (size_t j = 0; j < keys.size(); j++) { + for (size_t l = 0; l < stack.size(); l++) { + if (!verify_sig(stack[l], keys[j], sighash_temp, context) || marker[l]) + continue; + sigs.insert(std::make_pair(j, stack[l])); + marker[l] = true; + break; + } + } + + std::vector temp_sig; + for (auto s : sigs) { + temp_sig.push_back(s.second); + } + new_stacks.push_back(temp_sig); + } + return new_stacks; +} + +void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector> &signature_set) { + for (unsigned int i = 0; i < signature_set.size(); i++) { + std::vector signatures = signature_set[i]; + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + for (unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.push_back(signatures[i]); + } +} + +void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector> &signature_set) { + for (unsigned int i = 0; i < signature_set.size(); i++) { + std::vector signatures = signature_set[i]; + FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number"); + // push signatures in reverse order because script starts to check the top signature on the stack first + for (unsigned int i = 0; i < tx.vin.size(); i++) + tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]); + } +} + +void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector> &signature_set) { + add_signatures_to_transaction_weighted_multisig(tx, signature_set); + for (size_t itr = 0; itr < tx.vin.size(); itr++) { + tx.vin[0].scriptWitness.push_back(bytes()); + } +} + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp new file mode 100644 index 000000000..6d802a2ff --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +fc::ecc::public_key_data create_public_key_data(const std::vector &public_key) { + FC_ASSERT(public_key.size() == 33); + fc::ecc::public_key_data key; + for (size_t i = 0; i < 33; i++) { + key.at(i) = public_key[i]; + } + return key; +} + +bytes get_privkey_bytes(const std::string &privkey_base58) { + const auto privkey = fc::from_base58(privkey_base58); + // Remove version and hash + return bytes(privkey.cbegin() + 1, privkey.cbegin() + 1 + 32); +} + +bytes parse_hex(const std::string &str) { + bytes vec(str.size() / 2); + fc::from_hex(str, vec.data(), vec.size()); + return vec; +} + +std::vector get_pubkey_from_redeemScript(bytes script) { + FC_ASSERT(script.size() >= 37); + + script.erase(script.begin()); + script.erase(script.end() - 2, script.end()); + + std::vector result; + uint64_t count = script.size() / 34; + for (size_t i = 0; i < count; i++) { + result.push_back(bytes(script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1)))); + } + return result; +} + +bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key) { + bytes result; + result.resize(key.size()); + std::copy(key.begin(), key.end(), result.begin()); + return result; +} + +std::vector read_byte_arrays_from_string(const std::string &string_buf) { + std::stringstream ss(string_buf); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + std::vector data; + for (auto &v : json) { + std::string hex = v.second.data(); + bytes item; + item.resize(hex.size() / 2); + fc::from_hex(hex, (char *)&item[0], item.size()); + data.push_back(item); + } + return data; +} + +std::string write_transaction_signatures(const std::vector &data) { + std::string res = "["; + for (unsigned int idx = 0; idx < data.size(); ++idx) { + res += "\"" + fc::to_hex((char *)&data[idx][0], data[idx].size()) + "\""; + if (idx != data.size() - 1) + res += ","; + } + res += "]"; + return res; +} + +void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector &in_amounts, std::string &redeem_script) { + std::stringstream ss(string_buf); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + tx_hex = json.get("tx_hex"); + in_amounts.clear(); + for (auto &v : json.get_child("in_amounts")) + in_amounts.push_back(fc::to_uint64(v.second.data())); + redeem_script = json.get("redeem_script"); +} + +std::string write_transaction_data(const std::string &tx, const std::vector &in_amounts, const std::string &redeem_script) { + std::string res = "{\"tx_hex\":\"" + tx + "\",\"in_amounts\":["; + for (unsigned int idx = 0; idx < in_amounts.size(); ++idx) { + res += fc::to_string(in_amounts[idx]); + if (idx != in_amounts.size() - 1) + res += ","; + } + res += "],\"redeem_script\":\"" + redeem_script + "\"}"; + return res; +} + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bech32.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bech32.hpp new file mode 100644 index 000000000..c98d22bff --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bech32.hpp @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 { + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string &hrp, const std::vector &values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair> Decode(const std::string &str); + +}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32 diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp new file mode 100644 index 000000000..b9467e2fb --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp @@ -0,0 +1,249 @@ +#pragma once + +#include +#include + +using namespace graphene::chain; + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15 + +class bitcoin_address { + +public: + enum network { + mainnet, + testnet, + regtest + }; + + bitcoin_address() = default; + + bitcoin_address(const std::string &addr, network ntype = network::regtest) : + address(addr), + type(determine_type()), + raw_address(determine_raw_address()), + network_type(ntype) { + } + + bool operator==(const bitcoin_address &btc_addr) const; + + payment_type get_type() const { + return type; + } + + std::string get_address() const { + return address; + } + + bytes get_raw_address() const { + return raw_address; + } + + bytes get_script() const; + + network get_network_type() const { + return network_type; + } + +private: + enum size_segwit_address { P2WSH = 32, + P2WPKH = 20 }; + + payment_type determine_type(); + + bytes determine_raw_address(); + + bool check_segwit_address(const size_segwit_address &size) const; + + bool is_p2pk() const; + + bool is_p2wpkh() const; + + bool is_p2wsh() const; + + bool is_p2pkh() const; + + bool is_p2sh() const; + +public: + std::string address; + + payment_type type; + + bytes raw_address; + + network network_type; +}; + +class btc_multisig_address : public bitcoin_address { + +public: + btc_multisig_address() = default; + + btc_multisig_address(const size_t n_required, const accounts_keys &keys); + + size_t count_intersection(const accounts_keys &keys) const; + + bytes get_redeem_script() const { + return redeem_script; + } + +private: + void create_redeem_script(); + + void create_address(); + +public: + enum address_types { MAINNET_SCRIPT = 5, + TESTNET_SCRIPT = 196 }; + + enum { OP_0 = 0x00, + OP_EQUAL = 0x87, + OP_HASH160 = 0xa9, + OP_CHECKMULTISIG = 0xae }; + + bytes redeem_script; + + size_t keys_required = 0; + + accounts_keys witnesses_keys; +}; + +// multisig segwit address (P2WSH) +// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa +class btc_multisig_segwit_address : public btc_multisig_address { + +public: + btc_multisig_segwit_address() = default; + + btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys); + + bool operator==(const btc_multisig_segwit_address &addr) const; + + bytes get_witness_script() const { + return witness_script; + } + + std::vector get_keys(); + +private: + void create_witness_script(); + + void create_segwit_address(); + + bytes get_address_bytes(const bytes &script_hash); + +public: + bytes witness_script; +}; + +class btc_weighted_multisig_address : public bitcoin_address { + +public: + btc_weighted_multisig_address() = default; + + btc_weighted_multisig_address(const std::vector> &keys_data, + network network_type = network::regtest); + + bytes get_redeem_script() const { + return redeem_script_; + } + bytes get_witness_script() const { + return witness_script_; + } + +private: + void create_redeem_script(const std::vector> &keys_data); + void create_witness_script(); + void create_segwit_address(); + +public: + bytes redeem_script_; + bytes witness_script_; +}; + +class btc_one_or_m_of_n_multisig_address : public bitcoin_address { +public: + btc_one_or_m_of_n_multisig_address() = default; + btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector &keys_data, + network network_type = network::regtest); + bytes get_redeem_script() const { + return redeem_script_; + } + bytes get_witness_script() const { + return witness_script_; + } + +private: + void create_redeem_script(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector &keys_data); + void create_witness_script(); + void create_segwit_address(); + +public: + bytes redeem_script_; + bytes witness_script_; +}; + +class btc_one_or_weighted_multisig_address : public bitcoin_address { +public: + btc_one_or_weighted_multisig_address() = default; + btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data, + network network_type = network::regtest); + bytes get_redeem_script() const { + return redeem_script_; + } + bytes get_witness_script() const { + return witness_script_; + } + +protected: + void create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data); + void create_witness_script(); + void create_segwit_address(); + +public: + bytes redeem_script_; + bytes witness_script_; +}; + +class btc_timelocked_one_or_weighted_multisig_address : public btc_one_or_weighted_multisig_address { +public: + btc_timelocked_one_or_weighted_multisig_address() = default; + btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector> &keys_data, + network network_type = network::regtest); + +private: + void create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data); + + uint32_t latency_; +}; + +}}} // namespace graphene::peerplays_sidechain::bitcoin + +FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::bitcoin_address::network, + (mainnet)(testnet)(regtest)) + +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_address, (address)(type)(raw_address)(network_type)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_address, (graphene::peerplays_sidechain::bitcoin::bitcoin_address), + (redeem_script)(keys_required)(witnesses_keys)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_segwit_address, (graphene::peerplays_sidechain::bitcoin::btc_multisig_address), (witness_script)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_weighted_multisig_address, + (graphene::peerplays_sidechain::bitcoin::bitcoin_address), + (redeem_script_)(witness_script_)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_one_or_m_of_n_multisig_address, + (graphene::peerplays_sidechain::bitcoin::bitcoin_address), + (redeem_script_)(witness_script_)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_one_or_weighted_multisig_address, + (graphene::peerplays_sidechain::bitcoin::bitcoin_address), + (redeem_script_)(witness_script_)); + +FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_timelocked_one_or_weighted_multisig_address, + (graphene::peerplays_sidechain::bitcoin::btc_one_or_weighted_multisig_address), + (latency_)); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp new file mode 100644 index 000000000..7b7464498 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +enum class op { + // push value + _0 = 0x00, + _1 = 0x51, + _2 = 0x52, + _3 = 0x53, + _4 = 0x54, + _5 = 0x55, + _6 = 0x56, + _7 = 0x57, + _8 = 0x58, + _9 = 0x59, + _10 = 0x5a, + _11 = 0x5b, + _12 = 0x5c, + _13 = 0x5d, + _14 = 0x5e, + _15 = 0x5f, + _16 = 0x60, + + // control + IF = 0x63, + NOTIF = 0x64, + ELSE = 0x67, + ENDIF = 0x68, + RETURN = 0x6a, + + // stack ops + DROP = 0x75, + DUP = 0x76, + SWAP = 0x7c, + + // bit logic + EQUAL = 0x87, + EQUALVERIFY = 0x88, + ADD = 0x93, + GREATERTHAN = 0xa0, + GREATERTHANOREQUAL = 0xa2, + + // crypto + HASH160 = 0xa9, + CHECKSIG = 0xac, + CHECKMULTISIG = 0xae, + // Locktime + CHECKLOCKTIMEVERIFY = 0xb1, + CHECKSEQUENCEVERIFY = 0xb2, +}; + +class script_builder { + +public: + script_builder &operator<<(op opcode); + + script_builder &operator<<(uint32_t number); + + script_builder &operator<<(size_t size); + + script_builder &operator<<(const bytes &sc); + + script_builder &operator<<(const fc::sha256 &hash); + + script_builder &operator<<(const fc::ripemd160 &hash); + + script_builder &operator<<(const fc::ecc::public_key_data &pubkey_data); + + operator bytes() const { + return std::move(script); + } + +private: + bytes script; +}; + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp new file mode 100644 index 000000000..5668ffc2a --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp @@ -0,0 +1,106 @@ +#pragma once +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +struct out_point { + fc::sha256 hash; + uint32_t n = 0; + out_point() = default; + out_point(fc::sha256 _hash, uint32_t _n) : + hash(_hash), + n(_n) { + } + bool operator==(const out_point &op) const; +}; + +struct tx_in { + + bool operator==(const tx_in &ti) const; + + out_point prevout; + bytes scriptSig; + uint32_t nSequence = 0xffffffff; + std::vector scriptWitness; +}; + +struct tx_out { + int64_t value = 0; + bytes scriptPubKey; + + bool operator==(const tx_out &to) const; + + bool is_p2wsh() const; + + bool is_p2wpkh() const; + + bool is_p2pkh() const; + + bool is_p2sh() const; + + bool is_p2pk() const; + + bytes get_data_or_script() const; +}; + +struct bitcoin_transaction { + + bool operator!=(const bitcoin_transaction &bt) const; + + int32_t nVersion = 1; + std::vector vin; + std::vector vout; + uint32_t nLockTime = 0; + + fc::sha256 get_hash() const; + fc::sha256 get_txid() const; + size_t get_vsize() const; +}; + +class bitcoin_transaction_builder { + +public: + bitcoin_transaction_builder() = default; + + bitcoin_transaction_builder(const bitcoin_transaction _tx) : + tx(_tx) { + } + + void set_version(int32_t version); + + void set_locktime(uint32_t lock_time); + + void add_in(const fc::sha256 &txid, uint32_t n_out, + const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff); + + void add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out, + const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff); + + void add_in(payment_type type, tx_in txin, const bytes &script_code, bool front = false); + + void add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front = false); + + void add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front = false); + + void add_out(payment_type type, int64_t amount, const bytes &script_code, bool front = false); + + void add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front = false); + + bitcoin_transaction get_transaction() const { + return tx; + } + +private: + inline bool is_payment_to_pubkey(payment_type type); + + bytes get_script_pubkey(payment_type type, const bytes &script_code); + + bitcoin_transaction tx; +}; + +}}} // namespace graphene::peerplays_sidechain::bitcoin + +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::out_point, (hash)(n)) +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_in, (prevout)(scriptSig)(nSequence)(scriptWitness)) +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_out, (value)(scriptPubKey)) +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_transaction, (nVersion)(vin)(vout)(nLockTime)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp new file mode 100644 index 000000000..b7f2748e3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp @@ -0,0 +1,34 @@ +/* Copyright (c) 2017 Pieter Wuille + * + * 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 +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr { + +/** Decode a SegWit address. Returns (witver, witprog). witver = -1 means failure. */ +std::pair> decode(const std::string &hrp, const std::string &addr); + +/** Encode a SegWit address. Empty string means failure. */ +std::string encode(const std::string &hrp, int witver, const std::vector &witprog); + +}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/serialize.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/serialize.hpp new file mode 100644 index 000000000..2f1dfffe3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/serialize.hpp @@ -0,0 +1,354 @@ +#pragma once + +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +inline void write_compact_size(bytes &vec, size_t size) { + bytes sb; + sb.reserve(2); + if (size < 253) { + sb.insert(sb.end(), static_cast(size)); + } else if (size <= std::numeric_limits::max()) { + uint16_t tmp = htole16(static_cast(size)); + sb.insert(sb.end(), static_cast(253)); + sb.insert(sb.end(), reinterpret_cast(tmp), reinterpret_cast(tmp) + sizeof(tmp)); + } else if (size <= std::numeric_limits::max()) { + uint32_t tmp = htole32(static_cast(size)); + sb.insert(sb.end(), static_cast(254)); + sb.insert(sb.end(), reinterpret_cast(tmp), reinterpret_cast(tmp) + sizeof(tmp)); + } else { + uint64_t tmp = htole64(static_cast(size)); + sb.insert(sb.end(), static_cast(255)); + sb.insert(sb.end(), reinterpret_cast(tmp), reinterpret_cast(tmp) + sizeof(tmp)); + } + vec.insert(vec.end(), sb.begin(), sb.end()); +} + +template +inline void pack_compact_size(Stream &s, size_t size) { + if (size < 253) { + fc::raw::pack(s, static_cast(size)); + } else if (size <= std::numeric_limits::max()) { + fc::raw::pack(s, static_cast(253)); + fc::raw::pack(s, htole16(static_cast(size))); + } else if (size <= std::numeric_limits::max()) { + fc::raw::pack(s, static_cast(254)); + fc::raw::pack(s, htole32(static_cast(size))); + } else { + fc::raw::pack(s, static_cast(255)); + fc::raw::pack(s, htole64(static_cast(size))); + } +} + +template +inline uint64_t unpack_compact_size(Stream &s) { + uint8_t size; + uint64_t size_ret; + + fc::raw::unpack(s, size); + + if (size < 253) { + size_ret = size; + } else if (size == 253) { + uint16_t tmp; + fc::raw::unpack(s, tmp); + size_ret = le16toh(tmp); + if (size_ret < 253) + FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()"); + } else if (size == 254) { + uint32_t tmp; + fc::raw::unpack(s, tmp); + size_ret = le32toh(tmp); + if (size_ret < 0x10000u) + FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()"); + } else { + uint32_t tmp; + fc::raw::unpack(s, tmp); + size_ret = le64toh(tmp); + if (size_ret < 0x100000000ULL) + FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()"); + } + + if (size_ret > 0x08000000) + FC_THROW_EXCEPTION(fc::parse_error_exception, "unpack_compact_size(): size too large"); + + return size_ret; +} + +template +inline void unpack(Stream &s, int64_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) { + s.read((char *)&u, sizeof(u)); +} + +template +inline void unpack(Stream &s, int32_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) { + s.read((char *)&u, sizeof(u)); +} + +template +inline void pack(Stream &s, const std::vector &v) { + pack_compact_size(s, v.size()); + if (!v.empty()) + s.write(v.data(), v.size()); +} + +template +inline void unpack(Stream &s, std::vector &v) { + const auto size = unpack_compact_size(s); + v.resize(size); + if (size) + s.read(v.data(), size); +} + +template +inline void pack(Stream &s, const T &val) { + fc::raw::pack(s, val); +} + +template +inline void unpack(Stream &s, T &val) { + fc::raw::unpack(s, val); +} + +template +inline void pack(Stream &s, const out_point &op) { + fc::sha256 reversed(op.hash); + std::reverse(reversed.data(), reversed.data() + reversed.data_size()); + s.write(reversed.data(), reversed.data_size()); + pack(s, op.n); +} + +template +inline void unpack(Stream &s, out_point &op) { + uint64_t hash_size = op.hash.data_size(); + std::unique_ptr hash_data(new char[hash_size]); + s.read(hash_data.get(), hash_size); + std::reverse(hash_data.get(), hash_data.get() + hash_size); + + op.hash = fc::sha256(hash_data.get(), hash_size); + unpack(s, op.n); +} + +template +inline void pack(Stream &s, const tx_in &in) { + pack(s, in.prevout); + pack(s, in.scriptSig); + pack(s, in.nSequence); +} + +template +inline void unpack(Stream &s, tx_in &in) { + unpack(s, in.prevout); + unpack(s, in.scriptSig); + unpack(s, in.nSequence); +} + +template +inline void pack(Stream &s, const tx_out &out) { + pack(s, out.value); + pack(s, out.scriptPubKey); +} + +template +inline void unpack(Stream &s, tx_out &out) { + unpack(s, out.value); + unpack(s, out.scriptPubKey); +} + +template +inline void pack(Stream &s, const bitcoin_transaction &tx, bool with_witness = true) { + uint8_t flags = 0; + + if (with_witness) { + for (const auto &in : tx.vin) { + if (!in.scriptWitness.empty()) { + flags |= 1; + break; + } + } + } + + pack(s, tx.nVersion); + + if (flags) { + pack_compact_size(s, 0); + pack(s, flags); + } + + pack_compact_size(s, tx.vin.size()); + for (const auto &in : tx.vin) + pack(s, in); + + pack_compact_size(s, tx.vout.size()); + for (const auto &out : tx.vout) + pack(s, out); + + if (flags & 1) { + for (const auto in : tx.vin) { + pack_compact_size(s, in.scriptWitness.size()); + for (const auto &sc : in.scriptWitness) + pack(s, sc); + } + } + + pack(s, tx.nLockTime); +} + +template +inline void unpack(Stream &s, bitcoin_transaction &tx) { + uint8_t flags = 0; + + unpack(s, tx.nVersion); + + auto vin_size = unpack_compact_size(s); + if (vin_size == 0) { + unpack(s, flags); + vin_size = unpack_compact_size(s); + } + + tx.vin.reserve(vin_size); + for (size_t i = 0; i < vin_size; i++) { + tx_in in; + unpack(s, in); + tx.vin.push_back(in); + } + + const auto vout_size = unpack_compact_size(s); + tx.vout.reserve(vout_size); + for (size_t i = 0; i < vout_size; i++) { + tx_out out; + unpack(s, out); + tx.vout.push_back(out); + } + + if (flags & 1) { + for (auto &in : tx.vin) { + uint64_t stack_size = unpack_compact_size(s); + in.scriptWitness.reserve(stack_size); + for (uint64_t i = 0; i < stack_size; i++) { + std::vector script; + unpack(s, script); + in.scriptWitness.push_back(script); + } + } + } + + unpack(s, tx.nLockTime); +} + +inline std::vector pack(const bitcoin_transaction &v, bool with_witness = true) { + fc::datastream ps; + pack(ps, v, with_witness); + std::vector vec(ps.tellp()); + + if (!vec.empty()) { + fc::datastream ds(vec.data(), size_t(vec.size())); + pack(ds, v, with_witness); + } + return vec; +} + +inline bitcoin_transaction unpack(const std::vector &s) { + try { + bitcoin_transaction tmp; + if (!s.empty()) { + fc::datastream ds(s.data(), size_t(s.size())); + unpack(ds, tmp); + } + return tmp; + } + FC_RETHROW_EXCEPTIONS(warn, "error unpacking ${type}", ("type", "transaction")) +} + +template +inline void pack_tx_signature(Stream &s, const std::vector &scriptPubKey, const bitcoin_transaction &tx, unsigned int in_index, int hash_type) { + pack(s, tx.nVersion); + + pack_compact_size(s, tx.vin.size()); + for (size_t i = 0; i < tx.vin.size(); i++) { + const auto &in = tx.vin[i]; + pack(s, in.prevout); + if (i == in_index) + pack(s, scriptPubKey); + else + pack_compact_size(s, 0); // Blank signature + pack(s, in.nSequence); + } + + pack_compact_size(s, tx.vout.size()); + for (const auto &out : tx.vout) + pack(s, out); + + pack(s, tx.nLockTime); + pack(s, hash_type); +} + +template +inline void pack_tx_witness_signature(Stream &s, const std::vector &scriptCode, const bitcoin_transaction &tx, unsigned int in_index, int64_t amount, int hash_type) { + + fc::sha256 hash_prevouts; + fc::sha256 hash_sequence; + fc::sha256 hash_output; + + { + fc::datastream ps; + for (const auto in : tx.vin) + pack(ps, in.prevout); + + std::vector vec(ps.tellp()); + if (vec.size()) { + fc::datastream ds(vec.data(), size_t(vec.size())); + for (const auto in : tx.vin) + pack(ds, in.prevout); + } + + hash_prevouts = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size())); + } + + { + fc::datastream ps; + for (const auto in : tx.vin) + pack(ps, in.nSequence); + + std::vector vec(ps.tellp()); + if (vec.size()) { + fc::datastream ds(vec.data(), size_t(vec.size())); + for (const auto in : tx.vin) + pack(ds, in.nSequence); + } + + hash_sequence = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size())); + }; + + { + fc::datastream ps; + for (const auto out : tx.vout) + pack(ps, out); + + std::vector vec(ps.tellp()); + if (vec.size()) { + fc::datastream ds(vec.data(), size_t(vec.size())); + for (const auto out : tx.vout) + pack(ds, out); + } + + hash_output = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size())); + } + + pack(s, tx.nVersion); + pack(s, hash_prevouts); + pack(s, hash_sequence); + + pack(s, tx.vin[in_index].prevout); + pack(s, scriptCode); + pack(s, amount); + pack(s, tx.vin[in_index].nSequence); + + pack(s, hash_output); + pack(s, tx.nLockTime); + pack(s, hash_type); +} + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp new file mode 100644 index 000000000..418085624 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +class bitcoin_transaction; + +const secp256k1_context_t *btc_context(); + +fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptPubKey, int64_t amount, + size_t in_index, int hash_type, bool is_witness); + +std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign = nullptr); + +std::vector sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector &redeem_scripts, + const std::vector &amounts, const bytes &privkey, + const secp256k1_context_t *context_sign = nullptr, int hash_type = 1); + +void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector &redeem_scripts, bool use_mulisig_workaround = true); + +bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context); + +std::vector> sort_sigs(const bitcoin_transaction &tx, const std::vector &redeem_scripts, + const std::vector &amounts, const secp256k1_context_t *context); + +void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector> &signature_set); + +void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector> &signature_set); + +void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector> &signature_set); + +}}} // namespace graphene::peerplays_sidechain::bitcoin diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/types.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/types.hpp new file mode 100644 index 000000000..0997194b3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/types.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +class bitcoin_transaction; + +using bytes = std::vector; +using accounts_keys = std::map; +using full_btc_transaction = std::pair; + +enum class payment_type { + NULLDATA, + P2PK, + P2PKH, + P2SH, + P2WPKH, + P2WSH, + P2SH_WPKH, + P2SH_WSH +}; + +enum class sidechain_proposal_type { + ISSUE_BTC, + SEND_BTC_TRANSACTION, + REVERT_BTC_TRANSACTION +}; + +struct prev_out { + bool operator!=(const prev_out &obj) const { + if (this->hash_tx != obj.hash_tx || + this->n_vout != obj.n_vout || + this->amount != obj.amount) { + return true; + } + return false; + } + + std::string hash_tx; + uint32_t n_vout; + uint64_t amount; +}; + +}}} // namespace graphene::peerplays_sidechain::bitcoin + +FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH)); +FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::sidechain_proposal_type, (ISSUE_BTC)(SEND_BTC_TRANSACTION)(REVERT_BTC_TRANSACTION)); +FC_REFLECT(graphene::peerplays_sidechain::bitcoin::prev_out, (hash_tx)(n_vout)(amount)); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp new file mode 100644 index 000000000..4e1c4b589 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace bitcoin { + +fc::ecc::public_key_data create_public_key_data(const std::vector &public_key); + +bytes get_privkey_bytes(const std::string &privkey_base58); + +bytes parse_hex(const std::string &str); + +std::vector get_pubkey_from_redeemScript(bytes script); + +bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key); + +std::vector read_byte_arrays_from_string(const std::string &string_buf); + +std::string write_transaction_signatures(const std::vector &data); + +void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector &in_amounts, std::string &redeem_script); + +std::string write_transaction_data(const std::string &tx, const std::vector &in_amounts, const std::string &redeem_script); + +}}} // namespace graphene::peerplays_sidechain::bitcoin \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp new file mode 100644 index 000000000..706182368 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +using namespace graphene::chain; + +using bytes = std::vector; + +struct prev_out { + bool operator!=(const prev_out &obj) const { + if (this->hash_tx != obj.hash_tx || + this->n_vout != obj.n_vout || + this->amount != obj.amount) { + return true; + } + return false; + } + + std::string hash_tx; + uint32_t n_vout; + uint64_t amount; +}; + +struct info_for_vin { + info_for_vin() = default; + + info_for_vin(const prev_out &_out, const std::string &_address, bytes _script = bytes(), bool _resend = false); + + bool operator!=(const info_for_vin &obj) const; + + struct comparer { + bool operator()(const info_for_vin &lhs, const info_for_vin &rhs) const; + }; + + static uint64_t count_id_info_for_vin; + uint64_t id; + + fc::sha256 identifier; + + prev_out out; + std::string address; + bytes script; + + bool used = false; + bool resend = false; +}; + +struct sidechain_event_data { + fc::time_point_sec timestamp; + uint32_t block_num; + sidechain_type sidechain; + std::string sidechain_uid; + std::string sidechain_transaction_id; + std::string sidechain_from; + std::string sidechain_to; + std::string sidechain_currency; + fc::safe sidechain_amount; + account_id_type peerplays_from; + account_id_type peerplays_to; + asset peerplays_asset; +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp new file mode 100644 index 000000000..80086c10e --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +using namespace chain; + +namespace detail { +class peerplays_sidechain_plugin_impl; +} + +class peerplays_sidechain_plugin : public graphene::app::plugin { +public: + peerplays_sidechain_plugin(); + virtual ~peerplays_sidechain_plugin(); + + std::string plugin_name() const override; + virtual void plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map &options) override; + virtual void plugin_startup() override; + + std::unique_ptr my; + + std::set &get_sons(); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); + bool is_son_deregistered(son_id_type son_id); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp new file mode 100644 index 000000000..50dfd5052 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler { +public: + sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler(); + + sidechain_type get_sidechain(); + std::vector get_sidechain_deposit_addresses(); + std::vector get_sidechain_withdraw_addresses(); + std::string get_private_key(std::string public_key); + + bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional proposal_op = boost::none); + bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer); + bool approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id); + void sidechain_event_data_received(const sidechain_event_data &sed); + + void process_proposals(); + void process_active_sons_change(); + void create_deposit_addresses(); + void process_deposits(); + void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); + void settle_sidechain_transactions(); + + virtual bool process_proposal(const proposal_object &po) = 0; + virtual void process_primary_wallet() = 0; + virtual void process_sidechain_addresses() = 0; + virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0; + virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; + virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto) = 0; + virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0; + virtual int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto) = 0; + +protected: + peerplays_sidechain_plugin &plugin; + graphene::chain::database &database; + sidechain_type sidechain; + + std::map private_keys; + +private: +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp new file mode 100644 index 000000000..eb218a4c3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -0,0 +1,132 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class btc_txout { +public: + std::string txid_; + unsigned int out_num_; + uint64_t amount_; +}; + +class bitcoin_rpc_client { +public: + bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password); + + std::string addmultisigaddress(const uint32_t nrequired, const std::vector public_keys); + std::string combinepsbt(const vector &psbts); + std::string createmultisig(const uint32_t nrequired, const std::vector public_keys); + std::string createpsbt(const std::vector &ins, const fc::flat_map outs); + std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); + std::string createwallet(const std::string &wallet_name); + std::string decodepsbt(std::string const &tx_psbt); + std::string decoderawtransaction(std::string const &tx_hex); + std::string encryptwallet(const std::string &passphrase); + uint64_t estimatesmartfee(uint16_t conf_target = 128); + std::string finalizepsbt(std::string const &tx_psbt); + std::string getaddressinfo(const std::string &address); + std::string getblock(const std::string &block_hash, int32_t verbosity = 2); + std::string getrawtransaction(const std::string &txid, const bool verbose = false); + std::string gettransaction(const std::string &txid, const bool include_watch_only = false); + std::string getblockchaininfo(); + void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false); + std::vector listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999); + std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999); + std::string loadwallet(const std::string &filename); + std::string sendrawtransaction(const std::string &tx_hex); + std::string signrawtransactionwithwallet(const std::string &tx_hash); + std::string unloadwallet(const std::string &filename); + //std::string walletlock(); + std::string walletprocesspsbt(std::string const &tx_psbt); + //bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); + +private: + fc::http::reply send_post_request(std::string body, bool show_log = false); + + std::string ip; + uint32_t rpc_port; + std::string user; + std::string password; + std::string wallet; + std::string wallet_password; + + fc::http::header authorization; +}; + +// ============================================================================= + +class zmq_listener { +public: + zmq_listener(std::string _ip, uint32_t _zmq); + + fc::signal event_received; + +private: + void handle_zmq(); + std::vector receive_multipart(); + + std::string ip; + uint32_t zmq_port; + + zmq::context_t ctx; + zmq::socket_t socket; +}; + +// ============================================================================= + +class sidechain_net_handler_bitcoin : public sidechain_net_handler { +public: + sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler_bitcoin(); + + bool process_proposal(const proposal_object &po); + void process_primary_wallet(); + void process_sidechain_addresses(); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto); + std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto); + +private: + std::string ip; + uint32_t zmq_port; + uint32_t rpc_port; + std::string rpc_user; + std::string rpc_password; + std::string wallet; + std::string wallet_password; + + std::unique_ptr bitcoin_client; + std::unique_ptr listener; + + fc::future on_changed_objects_task; + bitcoin::bitcoin_address::network network_type; + + std::string create_primary_wallet_address(const std::vector &son_pubkeys); + + std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address); + std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); + std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); + + std::string create_transaction(const std::vector &inputs, const fc::flat_map outputs, std::string &redeem_script); + std::string sign_transaction(const sidechain_transaction_object &sto); + std::string send_transaction(const sidechain_transaction_object &sto); + + void handle_event(const std::string &event_data); + std::string get_redeemscript_for_userdeposit(const std::string &user_address); + std::vector extract_info_from_block(const std::string &_block); + void on_changed_objects(const vector &ids, const flat_set &accounts); + void on_changed_objects_cb(const vector &ids, const flat_set &accounts); +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp new file mode 100644 index 000000000..aa094d952 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler_peerplays : public sidechain_net_handler { +public: + sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler_peerplays(); + + bool process_proposal(const proposal_object &po); + void process_primary_wallet(); + void process_sidechain_addresses(); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto); + std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto); + +private: + void on_applied_block(const signed_block &b); +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp new file mode 100644 index 000000000..c2d40e143 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_manager { +public: + sidechain_net_manager(peerplays_sidechain_plugin &_plugin); + virtual ~sidechain_net_manager(); + + bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options); + void process_proposals(); + void process_active_sons_change(); + void create_deposit_addresses(); + void process_deposits(); + void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); + void settle_sidechain_transactions(); + +private: + peerplays_sidechain_plugin &plugin; + graphene::chain::database &database; + std::vector> net_handlers; +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp new file mode 100644 index 000000000..93dfe1c8d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -0,0 +1,698 @@ +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace bpo = boost::program_options; + +namespace graphene { namespace peerplays_sidechain { + +namespace detail { + +class peerplays_sidechain_plugin_impl { +public: + peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin); + virtual ~peerplays_sidechain_plugin_impl(); + + void plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg); + void plugin_initialize(const boost::program_options::variables_map &opt); + void plugin_startup(); + + std::set &get_sons(); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); + bool is_active_son(son_id_type son_id); + bool is_son_deregistered(son_id_type son_id); + bool is_son_deregister_op_valid(const chain::operation &op); + bool is_son_down_op_valid(const chain::operation &op); + bool is_valid_son_proposal(const chain::proposal_object &proposal); + fc::ecc::private_key get_private_key(son_id_type son_id); + fc::ecc::private_key get_private_key(chain::public_key_type public_key); + + void schedule_heartbeat_loop(); + void heartbeat_loop(); + void schedule_son_processing(); + void son_processing(); + void approve_proposals(); + void create_son_down_proposals(); + void create_son_deregister_proposals(); + + void process_proposals(); + void process_active_sons_change(); + void create_deposit_addresses(); + void process_deposits(); + void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); + void settle_sidechain_transactions(); + +private: + peerplays_sidechain_plugin &plugin; + + boost::program_options::variables_map options; + + bool config_ready_son; + bool config_ready_bitcoin; + bool config_ready_peerplays; + + son_id_type current_son_id; + + std::unique_ptr net_manager; + std::set sons; + std::map private_keys; + fc::future _heartbeat_task; + fc::future _son_processing_task; + + bool first_block_skipped; + void on_applied_block(const signed_block &b); +}; + +peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin) : + plugin(_plugin), + config_ready_son(false), + config_ready_bitcoin(false), + config_ready_peerplays(false), + current_son_id(son_id_type(std::numeric_limits().max())), + net_manager(nullptr), + first_block_skipped(false) { +} + +peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { + try { + if (_heartbeat_task.valid()) + _heartbeat_task.cancel_and_wait(__FUNCTION__); + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } + + try { + if (_son_processing_task.valid()) + _son_processing_task.cancel_and_wait(__FUNCTION__); + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } +} + +void peerplays_sidechain_plugin_impl::plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) { + auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); + string son_id_example = fc::json::to_string(chain::son_id_type(5)); + string son_id_example2 = fc::json::to_string(chain::son_id_type(6)); + + cli.add_options()("son-id", bpo::value>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str()); + cli.add_options()("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()); + cli.add_options()("peerplays-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), + "Tuple of [PublicKey, WIF private key] (may specify multiple times)"); + cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); + cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); + cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); + cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); + cli.add_options()("bitcoin-wallet-password", bpo::value(), "Bitcoin wallet password"); + cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), + "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); + cfg.add(cli); +} + +void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map &opt) { + options = opt; + config_ready_son = (options.count("son-id") || options.count("son-ids")) && options.count("peerplays-private-key"); + if (config_ready_son) { + LOAD_VALUE_SET(options, "son-id", sons, chain::son_id_type) + if (options.count("son-ids")) + boost::insert(sons, fc::json::from_string(options.at("son-ids").as()).as>(5)); + config_ready_son = config_ready_son && !sons.empty(); + +#ifndef ENABLE_MULTIPLE_SONS + if (sons.size() > 1) { + FC_THROW("Invalid configuration, multiple SON IDs provided"); + } +#endif + + if (options.count("peerplays-private-key")) { + const std::vector key_id_to_wif_pair_strings = options["peerplays-private-key"].as>(); + for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) { + auto key_id_to_wif_pair = graphene::app::dejsonify>(key_id_to_wif_pair_string, 5); + ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first)); + fc::optional private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second); + if (!private_key) { + // the key isn't in WIF format; see if they are still passing the old native private key format. This is + // just here to ease the transition, can be removed soon + try { + private_key = fc::variant(key_id_to_wif_pair.second, 2).as(1); + } catch (const fc::exception &) { + FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second)); + } + } + private_keys[key_id_to_wif_pair.first] = *private_key; + } + config_ready_son = config_ready_son && !private_keys.empty(); + } + } + if (!config_ready_son) { + wlog("Haven't set up SON parameters"); + throw; + } + + config_ready_bitcoin = options.count("bitcoin-node-ip") && + options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && + options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && + /*options.count( "bitcoin-wallet" ) && options.count( "bitcoin-wallet-password" ) &&*/ + options.count("bitcoin-private-key"); + if (!config_ready_bitcoin) { + wlog("Haven't set up Bitcoin sidechain parameters"); + } + + //config_ready_ethereum = options.count( "ethereum-node-ip" ) && + // options.count( "ethereum-address" ) && options.count( "ethereum-public-key" ) && options.count( "ethereum-private-key" ); + //if (!config_ready_ethereum) { + // wlog("Haven't set up Ethereum sidechain parameters"); + //} + + config_ready_peerplays = true; + if (!config_ready_peerplays) { + wlog("Haven't set up Peerplays sidechain parameters"); + } + + if (!(config_ready_bitcoin /*&& config_ready_ethereum*/ && config_ready_peerplays)) { + wlog("Haven't set up any sidechain parameters"); + throw; + } +} + +void peerplays_sidechain_plugin_impl::plugin_startup() { + + if (config_ready_son) { + ilog("Starting ${n} SON instances", ("n", sons.size())); + + schedule_heartbeat_loop(); + } else { + elog("No sons configured! Please add SON IDs and private keys to configuration."); + } + + net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); + + if (config_ready_bitcoin) { + net_manager->create_handler(sidechain_type::bitcoin, options); + ilog("Bitcoin sidechain handler running"); + } + + //if (config_ready_ethereum) { + // net_manager->create_handler(sidechain_type::ethereum, options); + // ilog("Ethereum sidechain handler running"); + //} + + if (config_ready_peerplays) { + net_manager->create_handler(sidechain_type::peerplays, options); + ilog("Peerplays sidechain handler running"); + } + + plugin.database().applied_block.connect([&](const signed_block &b) { + on_applied_block(b); + }); +} + +std::set &peerplays_sidechain_plugin_impl::get_sons() { + return sons; +} + +const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { + return current_son_id; +} + +const son_object peerplays_sidechain_plugin_impl::get_current_son_object() { + return get_son_object(current_son_id); +} + +const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { + const auto &idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find(son_id); + if (son_obj == idx.end()) + return {}; + return *son_obj; +} + +bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { + const auto &idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find(son_id); + if (son_obj == idx.end()) + return false; + + const chain::global_property_object &gpo = plugin.database().get_global_properties(); + vector active_son_ids; + active_son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); + + return (it != active_son_ids.end()); +} + +bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) { + const auto &idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find(son_id); + if (son_obj == idx.end()) + return true; + + if(son_obj->status == chain::son_status::deregistered) { + return true; + } + + return false; +} + +bool peerplays_sidechain_plugin_impl::is_son_deregister_op_valid(const chain::operation &op) { + son_deregister_operation deregister_op = op.get(); + return plugin.database().is_son_dereg_valid(deregister_op.son_id); +} + +bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operation &op) { + chain::database &d = plugin.database(); + const chain::global_property_object &gpo = d.get_global_properties(); + const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); + const auto &idx = d.get_index_type().indices().get(); + son_report_down_operation down_op = op.get(); + auto son_obj = idx.find(down_op.son_id); + if (son_obj == idx.end()) { + return false; + } + auto stats = son_obj->statistics(d); + fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); + int64_t down_threshold = gpo.parameters.son_down_time(); + if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && + ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { + return true; + } + return false; +} + +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) { + return get_private_key(get_son_object(son_id).signing_key); +} + +fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) { + auto private_key_itr = private_keys.find(public_key); + if (private_key_itr != private_keys.end()) { + return private_key_itr->second; + } + return {}; +} + +void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency(); + + fc::time_point next_wakeup(now + fc::seconds(time_to_next_heartbeat)); + + _heartbeat_task = fc::schedule([this] { + heartbeat_loop(); + }, + next_wakeup, "SON Heartbeat Production"); +} + +void peerplays_sidechain_plugin_impl::heartbeat_loop() { + schedule_heartbeat_loop(); + chain::database &d = plugin.database(); + + for (son_id_type son_id : sons) { + if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { + + ilog("Sending heartbeat for SON ${son}", ("son", son_id)); + chain::son_heartbeat_operation op; + op.owner_account = get_son_object(son_id).son_account; + op.son_id = son_id; + op.ts = fc::time_point::now() + fc::seconds(0); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op); + fc::future fut = fc::async([&]() { + try { + trx.validate(); + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending heartbeat failed with exception ${e}", ("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } +} + +void peerplays_sidechain_plugin_impl::schedule_son_processing() { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_son_processing = 500000; + + fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing)); + + _son_processing_task = fc::schedule([this] { + son_processing(); + }, + next_wakeup, "SON Processing"); +} + +void peerplays_sidechain_plugin_impl::son_processing() { + if (plugin.database().get_global_properties().active_sons.size() <= 0) { + return; + } + + fc::time_point now_fine = fc::time_point::now(); + fc::time_point_sec now = now_fine + fc::microseconds(500000); + if (plugin.database().get_slot_time(1) < now) { + return; // Not synced + } + + chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1); + ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ", + ("scheduled_son_id", scheduled_son_id)("now", now)); + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_son_deregistered(son_id)) { + continue; + } + current_son_id = son_id; + + // These tasks are executed by + // - All active SONs, no matter if scheduled + // - All previously active SONs + approve_proposals(); + process_proposals(); + process_sidechain_transactions(); + + if (plugin.is_active_son(son_id)) { + // Tasks that are executed by scheduled and active SON only + if (current_son_id == scheduled_son_id) { + + create_son_down_proposals(); + + create_son_deregister_proposals(); + + process_active_sons_change(); + + create_deposit_addresses(); + + process_deposits(); + + process_withdrawals(); + + process_sidechain_transactions(); + + send_sidechain_transactions(); + + settle_sidechain_transactions(); + } + } + } +} + +bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposal_object &proposal) { + if (proposal.proposed_transaction.operations.size() == 1) { + int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which(); + chain::operation op = proposal.proposed_transaction.operations[0]; + + if (op_idx_0 == chain::operation::tag::value) { + return is_son_down_op_valid(op); + } + + if (op_idx_0 == chain::operation::tag::value) { + return is_son_deregister_op_valid(op); + } + } + + return false; +} + +void peerplays_sidechain_plugin_impl::approve_proposals() { + + auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { + if (!is_valid_son_proposal(proposal)) { + return; + } + ilog("Sending approval for ${p} from ${s}", ("p", proposal.id)("s", son_id)); + chain::proposal_update_operation puo; + puo.fee_paying_account = get_son_object(son_id).son_account; + puo.proposal = proposal.id; + puo.active_approvals_to_add = {get_son_object(son_id).son_account}; + chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo); + fc::future fut = fc::async([&]() { + try { + trx.validate(); + plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending approval failed with exception ${e}", ("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + }; + + const auto &idx = plugin.database().get_index_type().indices().get(); + vector proposals; + for (const auto &proposal : idx) { + proposals.push_back(proposal.id); + } + + for (const auto proposal_id : proposals) { + + const object *obj = plugin.database().find_object(proposal_id); + const chain::proposal_object *proposal_ptr = dynamic_cast(obj); + if (proposal_ptr == nullptr) { + continue; + } + const proposal_object proposal = *proposal_ptr; + + if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { + continue; + } + + check_approve_proposal(get_current_son_id(), proposal); + } +} + +void peerplays_sidechain_plugin_impl::create_son_down_proposals() { + auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { + chain::database &d = plugin.database(); + const chain::global_property_object &gpo = d.get_global_properties(); + + chain::son_report_down_operation son_down_op; + son_down_op.payer = d.get_global_properties().parameters.son_account(); + son_down_op.son_id = son_id; + son_down_op.down_ts = last_active_ts; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); + return proposal_op; + }; + + chain::database &d = plugin.database(); + const chain::global_property_object &gpo = d.get_global_properties(); + const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); + const auto &idx = d.get_index_type().indices().get(); + std::set sons_being_reported_down = d.get_sons_being_reported_down(); + chain::son_id_type my_son_id = get_current_son_id(); + for (auto son_inf : gpo.active_sons) { + if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { + continue; + } + auto son_obj = idx.find(son_inf.son_id); + auto stats = son_obj->statistics(d); + fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); + int64_t down_threshold = gpo.parameters.son_down_time(); + if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && + ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { + ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); + chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); + fc::future fut = fc::async([&]() { + try { + trx.validate(); + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending son down proposal failed with exception ${e}", ("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } +} + +void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { + chain::database &d = plugin.database(); + std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); + chain::son_id_type my_son_id = get_current_son_id(); + + if (sons_to_be_dereg.size() > 0) { + // We shouldn't raise proposals for the SONs for which a de-reg + // proposal is already raised. + std::set sons_being_dereg = d.get_sons_being_deregistered(); + for (auto &son : sons_to_be_dereg) { + // New SON to be deregistered + if (sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) { + // Creating the de-reg proposal + auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); + if (op.valid()) { + // Signing and pushing into the txs to be included in the block + ilog("Sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); + chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); + fc::future fut = fc::async([&]() { + try { + trx.validate(); + d.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what())); + return false; + } + }); + fut.wait(fc::seconds(10)); + } + } + } + } +} + +void peerplays_sidechain_plugin_impl::process_proposals() { + net_manager->process_proposals(); +} + +void peerplays_sidechain_plugin_impl::process_active_sons_change() { + net_manager->process_active_sons_change(); +} + +void peerplays_sidechain_plugin_impl::create_deposit_addresses() { + net_manager->create_deposit_addresses(); +} + +void peerplays_sidechain_plugin_impl::process_deposits() { + net_manager->process_deposits(); +} + +void peerplays_sidechain_plugin_impl::process_withdrawals() { + net_manager->process_withdrawals(); +} + +void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { + net_manager->process_sidechain_transactions(); +} + +void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { + net_manager->send_sidechain_transactions(); +} + +void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() { + net_manager->settle_sidechain_transactions(); +} + +void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { + if (first_block_skipped) { + schedule_son_processing(); + } else { + first_block_skipped = true; + } +} + +} // namespace detail + +peerplays_sidechain_plugin::peerplays_sidechain_plugin() : + my(new detail::peerplays_sidechain_plugin_impl(*this)) { +} + +peerplays_sidechain_plugin::~peerplays_sidechain_plugin() { + return; +} + +std::string peerplays_sidechain_plugin::plugin_name() const { + return "peerplays_sidechain"; +} + +void peerplays_sidechain_plugin::plugin_set_program_options( + boost::program_options::options_description &cli, + boost::program_options::options_description &cfg) { + my->plugin_set_program_options(cli, cfg); +} + +void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) { + ilog("peerplays sidechain plugin: plugin_initialize() begin"); + my->plugin_initialize(options); + ilog("peerplays sidechain plugin: plugin_initialize() end"); +} + +void peerplays_sidechain_plugin::plugin_startup() { + ilog("peerplays sidechain plugin: plugin_startup() begin"); + my->plugin_startup(); + ilog("peerplays sidechain plugin: plugin_startup() end"); +} + +std::set &peerplays_sidechain_plugin::get_sons() { + return my->get_sons(); +} + +const son_id_type peerplays_sidechain_plugin::get_current_son_id() { + return my->get_current_son_id(); +} + +const son_object peerplays_sidechain_plugin::get_current_son_object() { + return my->get_current_son_object(); +} + +const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { + return my->get_son_object(son_id); +} + +bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { + return my->is_active_son(son_id); +} + +bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) { + return my->is_son_deregistered(son_id); +} + +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) { + return my->get_private_key(son_id); +} + +fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) { + return my->get_private_key(public_key); +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp new file mode 100644 index 000000000..ed404efe3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -0,0 +1,569 @@ +#include + +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + plugin(_plugin), + database(_plugin.database()) { +} + +sidechain_net_handler::~sidechain_net_handler() { +} + +sidechain_type sidechain_net_handler::get_sidechain() { + return sidechain; +} + +std::vector sidechain_net_handler::get_sidechain_deposit_addresses() { + std::vector result; + + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&result](const sidechain_address_object &sao) { + result.push_back(sao.deposit_address); + }); + return result; +} + +std::vector sidechain_net_handler::get_sidechain_withdraw_addresses() { + std::vector result; + + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&result](const sidechain_address_object &sao) { + result.push_back(sao.withdraw_address); + }); + return result; +} + +std::string sidechain_net_handler::get_private_key(std::string public_key) { + auto private_key_itr = private_keys.find(public_key); + if (private_key_itr != private_keys.end()) { + return private_key_itr->second; + } + return std::string(); +} + +bool sidechain_net_handler::proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional proposal_op) { + + bool result = false; + + const auto &idx = database.get_index_type().indices().get(); + vector proposals; + for (const auto &proposal : idx) { + proposals.push_back(proposal.id); + } + + for (const auto proposal_id : proposals) { + const auto &idx = database.get_index_type().indices().get(); + const auto po = idx.find(proposal_id); + if (po != idx.end()) { + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po->proposed_transaction.operations.size() >= 1) { + op_idx_0 = po->proposed_transaction.operations[0].which(); + op_obj_idx_0 = po->proposed_transaction.operations[0]; + } + + switch (op_idx_0) { + case chain::operation::tag::value: { + result = (op_obj_idx_0.get().son_wallet_id == object_id); + break; + } + + case chain::operation::tag::value: { + result = (op_obj_idx_0.get().son_wallet_deposit_id == object_id); + break; + } + + case chain::operation::tag::value: { + result = (op_obj_idx_0.get().son_wallet_withdraw_id == object_id); + break; + } + + case chain::operation::tag::value: { + if (proposal_op) { + chain::operation proposal_op_obj_0 = proposal_op.get(); + result = ((proposal_op_obj_0.get().sidechain_transaction_id == op_obj_idx_0.get().sidechain_transaction_id) && + (proposal_op_obj_0.get().signer == op_obj_idx_0.get().signer) && + (proposal_op_obj_0.get().signature == op_obj_idx_0.get().signature)); + } + break; + } + + default: + return false; + } + } + + if (result) { + break; + } + } + return result; +} + +bool sidechain_net_handler::signer_expected(const sidechain_transaction_object &sto, son_id_type signer) { + bool expected = false; + for (auto signature : sto.signatures) { + if (signature.first == signer) { + expected = signature.second.empty(); + } + } + return expected; +} + +bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id) { + + proposal_update_operation op; + op.fee_paying_account = plugin.get_son_object(son_id).son_account; + op.proposal = proposal_id; + op.active_approvals_to_add = {plugin.get_son_object(son_id).son_account}; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}", + ("son_id", son_id)("proposal_id", proposal_id)("e", e.what())); + return false; + } +} + +void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data &sed) { + ilog("sidechain_event_data:"); + ilog(" timestamp: ${timestamp}", ("timestamp", sed.timestamp)); + ilog(" block_num: ${block_num}", ("block_num", sed.block_num)); + ilog(" sidechain: ${sidechain}", ("sidechain", sed.sidechain)); + ilog(" sidechain_uid: ${uid}", ("uid", sed.sidechain_uid)); + ilog(" sidechain_transaction_id: ${transaction_id}", ("transaction_id", sed.sidechain_transaction_id)); + ilog(" sidechain_from: ${from}", ("from", sed.sidechain_from)); + ilog(" sidechain_to: ${to}", ("to", sed.sidechain_to)); + ilog(" sidechain_currency: ${currency}", ("currency", sed.sidechain_currency)); + ilog(" sidechain_amount: ${amount}", ("amount", sed.sidechain_amount)); + ilog(" peerplays_from: ${peerplays_from}", ("peerplays_from", sed.peerplays_from)); + ilog(" peerplays_to: ${peerplays_to}", ("peerplays_to", sed.peerplays_to)); + ilog(" peerplays_asset: ${peerplays_asset}", ("peerplays_asset", sed.peerplays_asset)); + + const chain::global_property_object &gpo = database.get_global_properties(); + + asset_id_type btc_asset_id = database.get_global_properties().parameters.btc_asset(); + std::string btc_asset_id_str = fc::to_string(btc_asset_id.space_id) + "." + + fc::to_string(btc_asset_id.type_id) + "." + + fc::to_string((uint64_t)btc_asset_id.instance); + +#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS + // Accepts BTC and peerplays asset deposits + bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0)); + bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)); +#else + // Accepts BTC deposits only + bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("BTC") == 0)); + bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)); +#endif + + // Deposit request + if (deposit_condition) { + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + + son_wallet_deposit_create_operation op; + op.payer = plugin.get_son_object(son_id).son_account; + op.son_id = son_id; + op.timestamp = sed.timestamp; + op.block_num = sed.block_num; + op.sidechain = sed.sidechain; + op.sidechain_uid = sed.sidechain_uid; + op.sidechain_transaction_id = sed.sidechain_transaction_id; + op.sidechain_from = sed.sidechain_from; + op.sidechain_to = sed.sidechain_to; + op.sidechain_currency = sed.sidechain_currency; + op.sidechain_amount = sed.sidechain_amount; + op.peerplays_from = sed.peerplays_from; + op.peerplays_to = sed.peerplays_to; + op.peerplays_asset = sed.peerplays_asset; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + } + } + } + return; + } + + // Withdrawal request + if (withdraw_condition) { + // BTC Payout only (for now) + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) + return; + + for (son_id_type son_id : plugin.get_sons()) { + if (plugin.is_active_son(son_id)) { + + son_wallet_withdraw_create_operation op; + op.payer = plugin.get_son_object(son_id).son_account; + op.son_id = son_id; + op.timestamp = sed.timestamp; + op.block_num = sed.block_num; + op.sidechain = sed.sidechain; + op.peerplays_uid = sed.sidechain_uid; + op.peerplays_transaction_id = sed.sidechain_transaction_id; + op.peerplays_from = sed.peerplays_from; + op.peerplays_asset = sed.peerplays_asset; + // BTC payout only (for now) + op.withdraw_sidechain = sidechain_type::bitcoin; + op.withdraw_address = addr_itr->withdraw_address; + op.withdraw_currency = "BTC"; + price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; + op.withdraw_amount = sed.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + } + } + } + return; + } +} + +void sidechain_net_handler::process_proposals() { + const auto &idx = database.get_index_type().indices().get(); + vector proposals; + for (const auto &proposal : idx) { + proposals.push_back(proposal.id); + } + + for (const auto proposal_id : proposals) { + const auto &idx = database.get_index_type().indices().get(); + const auto po = idx.find(proposal_id); + if (po != idx.end()) { + + if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) { + continue; + } + + bool should_process = false; + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po->proposed_transaction.operations.size() >= 1) { + op_idx_0 = po->proposed_transaction.operations[0].which(); + op_obj_idx_0 = po->proposed_transaction.operations[0]; + } + + int32_t op_idx_1 = -1; + chain::operation op_obj_idx_1; + + if (po->proposed_transaction.operations.size() >= 2) { + op_idx_1 = po->proposed_transaction.operations[1].which(); + op_obj_idx_1 = po->proposed_transaction.operations[1]; + } + + switch (op_idx_0) { + case chain::operation::tag::value: { + should_process = (op_obj_idx_0.get().sidechain == sidechain); + break; + } + + case chain::operation::tag::value: { + son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(swdo_id); + if (swdo != idx.end()) { + should_process = (swdo->sidechain == sidechain); + } + break; + } + + case chain::operation::tag::value: { + son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(swwo_id); + if (swwo != idx.end()) { + should_process = (swwo->withdraw_sidechain == sidechain); + } + break; + } + + case chain::operation::tag::value: { + sidechain_transaction_id_type st_id = op_obj_idx_0.get().sidechain_transaction_id; + son_id_type signer = op_obj_idx_0.get().signer; + const auto &idx = database.get_index_type().indices().get(); + const auto sto = idx.find(st_id); + if (sto != idx.end()) { + should_process = ((sto->sidechain == sidechain) && (sto->status == sidechain_transaction_status::valid) && signer_expected(*sto, signer)); + } + break; + } + + case chain::operation::tag::value: { + sidechain_transaction_id_type st_id = op_obj_idx_0.get().sidechain_transaction_id; + const auto &idx = database.get_index_type().indices().get(); + const auto sto = idx.find(st_id); + if (sto != idx.end()) { + should_process = (sto->sidechain == sidechain); + } + break; + } + + default: + should_process = false; + elog("=================================================="); + elog("Proposal not processed ${po}", ("po", *po)); + elog("=================================================="); + } + + if (should_process) { + bool should_approve = process_proposal(*po); + if (should_approve) { + approve_proposal(po->id, plugin.get_current_son_id()); + } + } + } + } +} + +void sidechain_net_handler::process_active_sons_change() { + process_primary_wallet(); +} + +void sidechain_net_handler::create_deposit_addresses() { + if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + return; + } + process_sidechain_addresses(); +} + +void sidechain_net_handler::process_deposits() { + if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + return; + } + + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { + if (swdo.id == object_id_type(0, 0, 0)) { + return; + } + //Ignore the deposits which are not valid anymore, considered refunds. + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, swdo.sidechain_to, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) { + return; + } + + ilog("Deposit to process: ${swdo}", ("swdo", swdo)); + + bool process_deposit_result = process_deposit(swdo); + + if (!process_deposit_result) { + wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); + return; + } + }); +} + +void sidechain_net_handler::process_withdrawals() { + if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + return; + } + + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { + if (swwo.id == object_id_type(0, 0, 0)) { + return; + } + + ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); + + bool process_withdrawal_result = process_withdrawal(swwo); + + if (!process_withdrawal_result) { + wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); + return; + } + }); +} + +void sidechain_net_handler::process_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) { + return; + } + + ilog("Sidechain transaction to process: ${sto}", ("sto", sto.id)); + + std::string processed_sidechain_tx = process_sidechain_transaction(sto); + + if (processed_sidechain_tx.empty()) { + wlog("Sidechain transaction not processed: ${sto}", ("sto", sto.id)); + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + sidechain_transaction_sign_operation sts_op; + sts_op.signer = plugin.get_current_son_id(); + sts_op.payer = gpo.parameters.son_account(); + sts_op.sidechain_transaction_id = sto.id; + sts_op.signature = processed_sidechain_tx; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + proposal_op.proposed_ops.emplace_back(sts_op); + + if (proposal_exists(chain::operation::tag::value, sto.id, proposal_op.proposed_ops[0].op)) { + return; + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::send_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + if (sto.id == object_id_type(0, 0, 0)) { + return; + } + + ilog("Sidechain transaction to send: ${sto}", ("sto", sto.id)); + + std::string sidechain_transaction = send_sidechain_transaction(sto); + + if (sidechain_transaction.empty()) { + wlog("Sidechain transaction not sent: ${sto}", ("sto", sto.id)); + return; + } + + sidechain_transaction_send_operation sts_op; + sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.sidechain_transaction_id = sto.id; + sts_op.sidechain_transaction = sidechain_transaction; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::settle_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + if (sto.id == object_id_type(0, 0, 0)) { + return; + } + + if (proposal_exists(chain::operation::tag::value, sto.id)) { + return; + } + + ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id)); + + int64_t settle_amount = settle_sidechain_transaction(sto); + + if (settle_amount < 0) { + wlog("Sidechain transaction not settled: ${sto}", ("sto", sto.id)); + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + sidechain_transaction_settle_operation sts_op; + sts_op.payer = gpo.parameters.son_account(); + sts_op.sidechain_transaction_id = sto.id; + proposal_op.proposed_ops.emplace_back(sts_op); + + if (settle_amount != 0) { + if (sto.object_id.is()) { + asset_issue_operation ai_op; + ai_op.fee = asset(2001000); + ai_op.issuer = gpo.parameters.son_account(); + ai_op.asset_to_issue = asset(settle_amount, database.get_global_properties().parameters.btc_asset()); + ai_op.issue_to_account = database.get(sto.object_id).peerplays_from; + proposal_op.proposed_ops.emplace_back(ai_op); + } + + if (sto.object_id.is()) { + asset_reserve_operation ar_op; + ar_op.fee = asset(2001000); + ar_op.payer = gpo.parameters.son_account(); + ar_op.amount_to_reserve = asset(settle_amount, database.get_global_properties().parameters.btc_asset()); + proposal_op.proposed_ops.emplace_back(ar_op); + } + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp new file mode 100644 index 000000000..48f641c3d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -0,0 +1,1911 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +// ============================================================================= + +bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password) : + ip(_ip), + rpc_port(_rpc), + user(_user), + password(_password), + wallet(_wallet), + wallet_password(_wallet_password) { + authorization.key = "Authorization"; + authorization.val = "Basic " + fc::base64_encode(user + ":" + password); +} + +std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector public_keys) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " + "\"method\": \"addmultisigaddress\", \"params\": ["); + std::string params = std::to_string(nrequired) + ", ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); + } + params = params + pubkeys + std::string("]"); + body = body + params + std::string(", null, \"bech32\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::combinepsbt(const vector &psbts) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"combinepsbt\", \"method\": " + "\"combinepsbt\", \"params\": [["); + std::string params = ""; + for (std::string psbt : psbts) { + if (!params.empty()) { + params = params + ","; + } + params = params + std::string("\"") + psbt + std::string("\""); + } + body = body + params + std::string("]] }"); + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::createmultisig(const uint32_t nrequired, const std::vector public_keys) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createmultisig\", " + "\"method\": \"createmultisig\", \"params\": ["); + std::string params = std::to_string(nrequired) + ", ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); + } + params = params + pubkeys + std::string("]"); + body = body + params + std::string(", \"p2sh-segwit\" ] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::createpsbt(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " + "\"method\": \"createpsbt\", \"params\": ["); + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " + "\"method\": \"createrawtransaction\", \"params\": ["); + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createwallet\", \"method\": " + "\"createwallet\", \"params\": [\"" + + wallet_name + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": " + "\"decodepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": " + "\"decoderawtransaction\", \"params\": [\"" + + tx_hex + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"encryptwallet\", \"method\": " + "\"encryptwallet\", \"params\": [\"" + + passphrase + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", " + "\"method\": \"estimatesmartfee\", \"params\": [" + + std::to_string(conf_target) + std::string("] }")); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return 0; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.find("feerate") != json_result.not_found()) { + auto feerate_str = json_result.get("feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } + + if (json_result.find("errors") != json_result.not_found()) { + wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return 20000; +} + +std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " + "\"finalizepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": " + "\"getaddressinfo\", \"params\": [\"" + + address + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": " + "\"getblock\", \"params\": [\"" + + block_hash + "\", " + std::to_string(verbosity) + "] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getrawtransaction\", \"method\": " + "\"getrawtransaction\", \"params\": ["); + + std::string params = "\"" + txid + "\", " + (verbose ? "true" : "false"); + body = body + params + "] }"; + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": " + "\"gettransaction\", \"params\": ["); + + std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false"); + body = body + params + "] }"; + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getblockchaininfo() { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblockchaininfo\", \"method\": " + "\"getblockchaininfo\", \"params\": [] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +void bitcoin_rpc_client::importaddress(const std::string &address_or_script, const std::string &label, const bool rescan, const bool p2sh) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", " + "\"method\": \"importaddress\", \"params\": ["); + + std::string params = "\"" + address_or_script + "\", " + + "\"" + label + "\", " + + (rescan ? "true" : "false") + ", " + + (p2sh ? "true" : "false"); + body = body + params + "] }"; + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return; + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } +} + +std::vector bitcoin_rpc_client::listunspent(const uint32_t minconf, const uint32_t maxconf) { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " + "\"listunspent\", \"params\": [" + + std::to_string(minconf) + "," + std::to_string(maxconf) + "] }"); + + const auto reply = send_post_request(body); + + std::vector result; + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } + } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return result; +} + +std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount, const uint32_t minconf, const uint32_t maxconf) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " + "\"listunspent\", \"params\": [" + + std::to_string(minconf) + "," + std::to_string(maxconf) + ","); + body += std::string("[\""); + body += address; + body += std::string("\"],true,{\"minimumAmount\":"); + body += std::to_string(minimum_amount); + body += std::string("} ] }"); + + const auto reply = send_post_request(body); + + std::vector result; + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } + } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return result; +} + +std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"loadwallet\", \"method\": " + "\"loadwallet\", \"params\": [\"" + + filename + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " + "\"method\": \"sendrawtransaction\", \"params\": [") + + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return json.get("result"); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", " + "\"method\": \"signrawtransactionwithwallet\", \"params\": ["); + std::string params = "\"" + tx_hash + "\""; + body = body + params + std::string("]}"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"unloadwallet\", \"method\": " + "\"unloadwallet\", \"params\": [\"" + + filename + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +//std::string bitcoin_rpc_client::walletlock() { +// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": " +// "\"walletlock\", \"params\": [] }"); +// +// const auto reply = send_post_request(body); +// +// if (reply.body.empty()) { +// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); +// return ""; +// } +// +// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); +// boost::property_tree::ptree json; +// boost::property_tree::read_json(ss, json); +// +// if (reply.status == 200) { +// std::stringstream ss; +// boost::property_tree::json_parser::write_json(ss, json.get_child("result")); +// return ss.str(); +// } +// +// if (json.count("error") && !json.get_child("error").empty()) { +// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); +// } +// return ""; +//} + +std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " + "\"walletprocesspsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +//bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { +// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " +// "\"walletpassphrase\", \"params\": [\"" + +// passphrase + "\", " + std::to_string(timeout) + "] }"); +// +// const auto reply = send_post_request(body); +// +// if (reply.body.empty()) { +// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); +// return false; +// } +// +// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); +// boost::property_tree::ptree json; +// boost::property_tree::read_json(ss, json); +// +// if (reply.status == 200) { +// return true; +// } +// +// if (json.count("error") && !json.get_child("error").empty()) { +// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); +// } +// return false; +//} + +fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) { + fc::http::connection conn; + conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); + + std::string url = "http://" + ip + ":" + std::to_string(rpc_port); + + if (wallet.length() > 0) { + url = url + "/wallet/" + wallet; + } + + fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); + + if (show_log) { + ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request: ${body}", ("body", body)); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + ilog("### Response: ${ss}", ("ss", ss.str())); + } + + return reply; +} + +// ============================================================================= + +zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : + ip(_ip), + zmq_port(_zmq), + ctx(1), + socket(ctx, ZMQ_SUB) { + std::thread(&zmq_listener::handle_zmq, this).detach(); +} + +std::vector zmq_listener::receive_multipart() { + std::vector msgs; + + int32_t more; + size_t more_size = sizeof(more); + while (true) { + zmq::message_t msg; + socket.recv(&msg, 0); + socket.getsockopt(ZMQ_RCVMORE, &more, &more_size); + + if (!more) + break; + msgs.push_back(std::move(msg)); + } + + return msgs; +} + +void zmq_listener::handle_zmq() { + int linger = 0; + socket.setsockopt(ZMQ_SUBSCRIBE, "hashblock", 9); + socket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); + //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); + socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); + + while (true) { + try { + auto msg = receive_multipart(); + const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); + const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); + event_received(block_hash); + } catch (zmq::error_t &e) { + } + } +} + +// ============================================================================= + +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain_net_handler(_plugin, options) { + sidechain = sidechain_type::bitcoin; + + ip = options.at("bitcoin-node-ip").as(); + zmq_port = options.at("bitcoin-node-zmq-port").as(); + rpc_port = options.at("bitcoin-node-rpc-port").as(); + rpc_user = options.at("bitcoin-node-rpc-user").as(); + rpc_password = options.at("bitcoin-node-rpc-password").as(); + wallet = ""; + if (options.count("bitcoin-wallet")) { + wallet = options.at("bitcoin-wallet").as(); + } + wallet_password = ""; + if (options.count("bitcoin-wallet-password")) { + wallet_password = options.at("bitcoin-wallet-password").as(); + } + + if (options.count("bitcoin-private-key")) { + const std::vector pub_priv_keys = options["bitcoin-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Bitcoin Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { + FC_THROW("Invalid public private key pair."); + } + private_keys[key_pair.first] = key_pair.second; + } + } + + fc::http::connection conn; + try { + conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); + } catch (fc::exception e) { + elog("No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip)("port", rpc_port)); + FC_ASSERT(false); + } + + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(ip, rpc_port, rpc_user, rpc_password, wallet, wallet_password)); + if (!wallet.empty()) { + bitcoin_client->loadwallet(wallet); + } + + std::string blockchain_info = bitcoin_client->getblockchaininfo(); + std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end())); + boost::property_tree::ptree bci_json; + boost::property_tree::read_json(bci_ss, bci_json); + using namespace bitcoin; + network_type = bitcoin_address::network::mainnet; + if (bci_json.count("chain")) { + std::string chain = bci_json.get("chain"); + if (chain == "test") { + network_type = bitcoin_address::network::testnet; + } else if (chain == "regtest") { + network_type = bitcoin_address::network::regtest; + } + } + + listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); + listener->event_received.connect([this](const std::string &event_data) { + std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); + }); + + database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { + on_changed_objects(ids, accounts); + }); +} + +sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { + try { + if (on_changed_objects_task.valid()) { + on_changed_objects_task.cancel_and_wait(__FUNCTION__); + } + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } +} + +bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { + + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + + bool should_approve = false; + + const chain::global_property_object &gpo = database.get_global_properties(); + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po.proposed_transaction.operations.size() >= 1) { + op_idx_0 = po.proposed_transaction.operations[0].which(); + op_obj_idx_0 = po.proposed_transaction.operations[0]; + } + + int32_t op_idx_1 = -1; + chain::operation op_obj_idx_1; + + if (po.proposed_transaction.operations.size() >= 2) { + op_idx_1 = po.proposed_transaction.operations[1].which(); + op_obj_idx_1 = po.proposed_transaction.operations[1]; + } + + switch (op_idx_0) { + + case chain::operation::tag::value: { + bool address_ok = false; + bool transaction_ok = false; + std::string new_pw_address = ""; + son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(swo_id); + if (swo != idx.end()) { + + auto active_sons = gpo.active_sons; + vector wallet_sons = swo->sons; + + bool son_sets_equal = (active_sons.size() == wallet_sons.size()); + + if (son_sets_equal) { + for (size_t i = 0; i < active_sons.size(); i++) { + son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i); + } + } + + if (son_sets_equal) { + auto active_sons = gpo.active_sons; + vector son_pubkeys_bitcoin; + for (const son_info &si : active_sons) { + son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + } + + string reply_str = create_primary_wallet_address(active_sons); + + std::stringstream active_pw_ss(reply_str); + boost::property_tree::ptree active_pw_pt; + boost::property_tree::read_json(active_pw_ss, active_pw_pt); + if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { + std::stringstream res; + boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); + new_pw_address = active_pw_pt.get("result.address"); + + address_ok = (op_obj_idx_0.get().address == res.str()); + } + } + + if (po.proposed_transaction.operations.size() >= 2) { + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(object_id); + if (swo != idx.end()) { + tx_str = create_primary_wallet_transaction(*swo, new_pw_address); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } else { + transaction_ok = true; + } + } + + should_approve = address_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + bool transaction_ok = false; + son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(swdo_id); + if (swdo != idx.end()) { + + std::string swdo_txid = swdo->sidechain_transaction_id; + std::string swdo_address = swdo->sidechain_to; + uint64_t swdo_amount = swdo->sidechain_amount.value; + uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); + + std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true); + std::stringstream tx_ss(tx_str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); + + if (tx_json.count("error") && tx_json.get_child("error").empty()) { + + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + std::string tx_address = ""; + int64_t tx_amount = -1; + int64_t tx_vout = -1; + + for (auto &input : tx_json.get_child("result.vout")) { + std::string tx_vout_s = input.second.get("n"); + tx_vout = std::stoll(tx_vout_s); + if (tx_vout == swdo_vout) { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == swdo_address) { + tx_address = address.second.data(); + break; + } + } + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; + } + } + + process_ok = (swdo_txid == tx_txid) && + (swdo_address == tx_address) && + (swdo_amount == tx_amount) && + (swdo_vout == tx_vout) && + (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(object_id); + if (swdo != idx.end()) { + tx_str = create_deposit_transaction(*swdo); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } + + should_approve = process_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + bool transaction_ok = false; + son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(swwo_id); + if (swwo != idx.end()) { + + uint32_t swwo_block_num = swwo->block_num; + std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; + uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); + + const auto &block = database.fetch_block_by_number(swwo_block_num); + + for (const auto &tx : block->transactions) { + if (tx.id().str() == swwo_peerplays_transaction_id) { + operation op = tx.operations[swwo_op_idx]; + transfer_operation t_op = op.get(); + + price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; + asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + + process_ok = (t_op.to == gpo.parameters.son_account()) && + (swwo->peerplays_from == t_op.from) && + (swwo->peerplays_asset == peerplays_asset); + break; + } + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(object_id); + if (swwo != idx.end()) { + tx_str = create_withdrawal_transaction(*swwo); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } + + should_approve = process_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + using namespace bitcoin; + should_approve = true; + son_id_type signer = op_obj_idx_0.get().signer; + std::string signature = op_obj_idx_0.get().signature; + sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get().sidechain_transaction_id; + std::vector in_amounts; + std::string tx_hex; + std::string redeem_script; + const auto &st_idx = database.get_index_type().indices().get(); + const auto sto = st_idx.find(sidechain_transaction_id); + if (sto == st_idx.end()) { + should_approve = false; + break; + } + + const auto &s_idx = database.get_index_type().indices().get(); + const auto son = s_idx.find(signer); + if (son == s_idx.end()) { + should_approve = false; + break; + } + + read_transaction_data(sto->transaction, tx_hex, in_amounts, redeem_script); + bitcoin_transaction tx = unpack(parse_hex(tx_hex)); + bitcoin::bytes pubkey = parse_hex(son->sidechain_public_keys.at(sidechain_type::bitcoin)); + vector sigs = read_byte_arrays_from_string(signature); + for (size_t i = 0; i < tx.vin.size(); i++) { + const auto &sighash_str = get_signature_hash(tx, parse_hex(redeem_script), static_cast(in_amounts[i]), i, 1, true).str(); + const bitcoin::bytes &sighash_hex = parse_hex(sighash_str); + should_approve = should_approve && verify_sig(sigs[i], pubkey, sighash_hex, btc_context()); + } + break; + } + + case chain::operation::tag::value: { + should_approve = true; + break; + } + + default: + should_approve = false; + elog("=================================================="); + elog("Proposal not considered for approval ${po}", ("po", po)); + elog("=================================================="); + } + + return should_approve; +} + +void sidechain_net_handler_bitcoin::process_primary_wallet() { + const auto &swi = database.get_index_type().indices().get(); + const auto &active_sw = swi.rbegin(); + if (active_sw != swi.rend()) { + + if ((active_sw->addresses.find(sidechain_type::bitcoin) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain_type::bitcoin).empty())) { + + if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + string reply_str = create_primary_wallet_address(active_sons); + + std::stringstream active_pw_ss(reply_str); + boost::property_tree::ptree active_pw_pt; + boost::property_tree::read_json(active_pw_ss, active_pw_pt); + if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + std::stringstream res; + boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); + + son_wallet_update_operation swu_op; + swu_op.payer = gpo.parameters.son_account(); + swu_op.son_wallet_id = active_sw->id; + swu_op.sidechain = sidechain_type::bitcoin; + swu_op.address = res.str(); + + proposal_op.proposed_ops.emplace_back(swu_op); + + const auto &prev_sw = std::next(active_sw); + if (prev_sw != swi.rend()) { + std::string new_pw_address = active_pw_pt.get("result.address"); + std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); + if (!tx_str.empty()) { + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = prev_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = prev_sw->sons; + proposal_op.proposed_ops.emplace_back(stc_op); + } + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + return; + } + } + } + } +} + +void sidechain_net_handler_bitcoin::process_sidechain_addresses() { + using namespace bitcoin; + + const chain::global_property_object &gpo = database.get_global_properties(); + std::vector> pubkeys; + for (auto &son : gpo.active_sons) { + std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); + auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + pubkeys.push_back(std::make_pair(pubkey, son.weight)); + } + + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&](const sidechain_address_object &sao) { + if (sao.expires == time_point_sec::maximum()) { + auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key))); + + btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type); + std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) + + "\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }"; + + if (addr.get_address() != sao.deposit_address) { + sidechain_address_update_operation op; + op.payer = plugin.get_current_son_object().son_account; + op.sidechain_address_id = sao.id; + op.sidechain_address_account = sao.sidechain_address_account; + op.sidechain = sao.sidechain; + op.deposit_public_key = sao.deposit_public_key; + op.deposit_address = addr.get_address(); + op.deposit_address_data = address_data; + op.withdraw_public_key = sao.withdraw_public_key; + op.withdraw_address = sao.withdraw_address; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + } + }); +} + +bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { + + if (proposal_exists(chain::operation::tag::value, swdo.id)) { + return false; + } + + std::string tx_str = create_deposit_transaction(swdo); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; + proposal_op.proposed_ops.emplace_back(swdp_op); + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swdo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons; + proposal_op.proposed_ops.emplace_back(stc_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { + + if (proposal_exists(chain::operation::tag::value, swwo.id)) { + return false; + } + + std::string tx_str = create_withdrawal_transaction(swwo); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; + proposal_op.proposed_ops.emplace_back(swwp_op); + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swwo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons; + proposal_op.proposed_ops.emplace_back(stc_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +std::string sidechain_net_handler_bitcoin::process_sidechain_transaction(const sidechain_transaction_object &sto) { + return sign_transaction(sto); +} + +std::string sidechain_net_handler_bitcoin::send_sidechain_transaction(const sidechain_transaction_object &sto) { + return send_transaction(sto); +} + +int64_t sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain_transaction_object &sto) { + + if (sto.object_id.is()) { + return 0; + } + + int64_t settle_amount = -1; + + if (sto.sidechain_transaction.empty()) { + return settle_amount; + } + + std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); + std::stringstream tx_ss(tx_str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); + + if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { + return settle_amount; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + + using namespace bitcoin; + std::vector> pubkey_weights; + for (auto si : sto.signers) { + std::string pub_key_str = si.sidechain_public_keys.at(sidechain_type::bitcoin); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); + } + btc_weighted_multisig_address addr(pubkey_weights, network_type); + + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + std::string tx_address = addr.get_address(); + int64_t tx_amount = -1; + + if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { + if (sto.object_id.is()) { + for (auto &input : tx_json.get_child("result.vout")) { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == tx_address) { + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; + } + } + } + settle_amount = tx_amount; + } + + if (sto.object_id.is()) { + auto swwo = database.get(sto.object_id); + settle_amount = swwo.withdraw_amount.value; + } + } + return settle_amount; +} + +std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { + using namespace bitcoin; + + std::vector> pubkey_weights; + for (auto &son : son_pubkeys) { + std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); + } + + btc_weighted_multisig_address addr(pubkey_weights, network_type); + + std::stringstream ss; + + ss << "{\"result\": {\"address\": \"" << addr.get_address() << "\", \"redeemScript\": \"" << fc::to_hex(addr.get_redeem_script()) << "\"" + << "}, \"error\":null}"; + + std::string res = ss.str(); + return res; +} + +std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address) { + + std::stringstream prev_sw_ss(prev_swo.addresses.find(sidechain_type::bitcoin)->second); + boost::property_tree::ptree prev_sw_pt; + boost::property_tree::read_json(prev_sw_ss, prev_sw_pt); + std::string prev_pw_address = prev_sw_pt.get("address"); + std::string prev_redeem_script = prev_sw_pt.get("redeemScript"); + + if (prev_pw_address == new_sw_address) { + wlog("BTC previous and new primary wallet addresses are same. No funds moving needed [from ${prev_sw} to ${new_sw_address}]", ("prev_swo", prev_swo.id)("active_sw", new_sw_address)); + return ""; + } + + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + uint64_t total_amount = 0.0; + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); + + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", prev_pw_address)); + return ""; + } else { + for (const auto &utx : inputs) { + total_amount += utx.amount_; + } + + if (fee_rate >= total_amount) { + elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address)); + return ""; + } + } + + fc::flat_map outputs; + outputs[new_sw_address] = double(total_amount - fee_rate) / 100000000.0; + + return create_transaction(inputs, outputs, prev_redeem_script); +} + +std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_wallet_deposit_object &swdo) { + const auto &idx = database.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + return ""; + } + //Get redeem script for deposit address + std::string redeem_script = get_redeemscript_for_userdeposit(swdo.sidechain_to); + std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + + std::stringstream ss(pw_address_json); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + std::string pw_address = json.get("address"); + + std::string txid = swdo.sidechain_transaction_id; + std::string suid = swdo.sidechain_uid; + std::string nvout = suid.substr(suid.find_last_of("-") + 1); + uint64_t deposit_amount = swdo.sidechain_amount.value; + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + deposit_amount -= fee_rate; // Deduct minimum relay fee + double transfer_amount = (double)deposit_amount / 100000000.0; + + std::vector inputs; + fc::flat_map outputs; + + btc_txout utxo; + utxo.txid_ = txid; + utxo.out_num_ = std::stoul(nvout); + utxo.amount_ = swdo.sidechain_amount.value; + + inputs.push_back(utxo); + + outputs[pw_address] = transfer_amount; + + return create_transaction(inputs, outputs, redeem_script); +} + +std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { + const auto &idx = database.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + return ""; + } + + std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + + std::stringstream ss(pw_address_json); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + std::string pw_address = json.get("address"); + std::string redeem_script = json.get("redeemScript"); + + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + uint64_t total_amount = 0; + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); + + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); + return ""; + } else { + for (const auto &utx : inputs) { + total_amount += utx.amount_; + } + + if ((swwo.withdraw_amount.value > total_amount) || (fee_rate >= swwo.withdraw_amount.value)) { + elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); + return ""; + } + } + + fc::flat_map outputs; + outputs[swwo.withdraw_address] = (swwo.withdraw_amount.value - fee_rate) / 100000000.0; + if ((total_amount - swwo.withdraw_amount.value) > 0.0) { + outputs[pw_address] = double(total_amount - swwo.withdraw_amount.value) / 100000000.0; + } + + return create_transaction(inputs, outputs, redeem_script); +} + +std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector &inputs, const fc::flat_map outputs, std::string &redeem_script) { + using namespace bitcoin; + + bitcoin_transaction_builder tb; + std::vector in_amounts; + + tb.set_version(2); + // Set vins + for (auto in : inputs) { + tb.add_in(fc::sha256(in.txid_), in.out_num_, bitcoin::bytes()); + in_amounts.push_back(in.amount_); + } + // Set vouts + for (auto out : outputs) { + uint64_t satoshis = out.second * 100000000.0; + tb.add_out_all_type(satoshis, out.first); + } + + const auto tx = tb.get_transaction(); + std::string hex_tx = fc::to_hex(pack(tx)); + std::string tx_raw = write_transaction_data(hex_tx, in_amounts, redeem_script); + return tx_raw; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) { + using namespace bitcoin; + std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain); + std::string prvkey = get_private_key(pubkey); + std::vector in_amounts; + std::string tx_hex; + std::string redeem_script; + + fc::optional btc_private_key = graphene::utilities::wif_to_key(prvkey); + if (!btc_private_key) { + elog("Invalid private key ${pk}", ("pk", prvkey)); + return ""; + } + const auto secret = btc_private_key->get_secret(); + bitcoin::bytes privkey_signing(secret.data(), secret.data() + secret.data_size()); + + read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script); + + bitcoin_transaction tx = unpack(parse_hex(tx_hex)); + std::vector redeem_scripts(tx.vin.size(), parse_hex(redeem_script)); + auto sigs = sign_witness_transaction_part(tx, redeem_scripts, in_amounts, privkey_signing, btc_context(), 1); + std::string tx_signature = write_transaction_signatures(sigs); + + return tx_signature; +} + +std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_transaction_object &sto) { + using namespace bitcoin; + std::vector in_amounts; + std::string tx_hex; + std::string redeem_script; + + read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script); + + bitcoin_transaction tx = unpack(parse_hex(tx_hex)); + + std::vector redeem_scripts(tx.vin.size(), parse_hex(redeem_script)); + + uint32_t inputs_number = in_amounts.size(); + vector dummy; + dummy.resize(inputs_number); + //Organise weighted address signatures + //Add dummies for empty signatures + vector> signatures; + for (unsigned idx = 0; idx < sto.signatures.size(); ++idx) { + if (sto.signatures[idx].second.empty()) + signatures.push_back(dummy); + else + signatures.push_back(read_byte_arrays_from_string(sto.signatures[idx].second)); + } + //Add empty sig for user signature for Deposit transaction + if (sto.object_id.type() == son_wallet_deposit_object::type_id) { + add_signatures_to_transaction_user_weighted_multisig(tx, signatures); + } else { + add_signatures_to_transaction_weighted_multisig(tx, signatures); + } + //Add redeemscripts to vins and make tx ready for sending + sign_witness_transaction_finalize(tx, redeem_scripts, false); + std::string final_tx_hex = fc::to_hex(pack(tx)); + std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); + + return res; +} + +void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { + std::string block = bitcoin_client->getblock(event_data); + if (block != "") { + const auto &vins = extract_info_from_block(block); + + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + + for (const auto &v : vins) { + // !!! EXTRACT DEPOSIT ADDRESS FROM SIDECHAIN ADDRESS OBJECT + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, v.address, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) + continue; + + std::stringstream ss; + ss << "bitcoin" + << "-" << v.out.hash_tx << "-" << v.out.n_vout; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = database.head_block_time(); + sed.block_num = database.head_block_num(); + sed.sidechain = addr_itr->sidechain; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = v.out.hash_tx; + sed.sidechain_from = ""; + sed.sidechain_to = v.address; + sed.sidechain_currency = "BTC"; + sed.sidechain_amount = v.out.amount; + sed.peerplays_from = addr_itr->sidechain_address_account; + sed.peerplays_to = database.get_global_properties().parameters.son_account(); + price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; + sed.peerplays_asset = asset(sed.sidechain_amount * btc_price.base.amount / btc_price.quote.amount); + sidechain_event_data_received(sed); + } + } +} + +std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(const std::string &user_address) { + using namespace bitcoin; + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, user_address, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) { + return ""; + } + + const auto &idx = database.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + return ""; + } + + std::vector> pubkey_weights; + for (auto &son : obj->sons) { + std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); + } + auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); + btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type); + return fc::to_hex(deposit_addr.get_redeem_script()); +} + +std::vector sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) { + std::stringstream ss(_block); + boost::property_tree::ptree block; + boost::property_tree::read_json(ss, block); + + std::vector result; + + for (const auto &tx_child : block.get_child("tx")) { + const auto &tx = tx_child.second; + + for (const auto &o : tx.get_child("vout")) { + const auto script = o.second.get_child("scriptPubKey"); + + if (!script.count("addresses")) + continue; + + for (const auto &addr : script.get_child("addresses")) { // in which cases there can be more addresses? + const auto address_base58 = addr.second.get_value(); + info_for_vin vin; + vin.out.hash_tx = tx.get_child("txid").get_value(); + string amount = o.second.get_child("value").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + vin.out.amount = std::stoll(amount); + vin.out.n_vout = o.second.get_child("n").get_value(); + vin.address = address_base58; + result.push_back(vin); + } + } + } + + return result; +} + +void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_changed_objects_processing = 5000; + + fc::time_point next_wakeup(now + fc::microseconds(time_to_next_changed_objects_processing)); + + on_changed_objects_task = fc::schedule([this, ids, accounts] { + on_changed_objects_cb(ids, accounts); + }, + next_wakeup, "SON Processing"); +} + +void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector &ids, const flat_set &accounts) { + for (auto id : ids) { + if (id.is()) { + const auto &swi = database.get_index_type().indices().get(); + auto swo = swi.find(id); + if (swo != swi.end()) { + std::stringstream pw_ss(swo->addresses.at(sidechain)); + boost::property_tree::ptree pw_pt; + boost::property_tree::read_json(pw_ss, pw_pt); + + if (pw_pt.count("address")) { + std::string pw_address = pw_pt.get("address"); + bitcoin_client->importaddress(pw_address); + } + + if (pw_pt.count("redeemScript")) { + std::string pw_redeem_script = pw_pt.get("redeemScript"); + bitcoin_client->importaddress(pw_redeem_script, "", true, true); + } + } + } + } +} + +// ============================================================================= +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp new file mode 100644 index 000000000..3388527a1 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -0,0 +1,306 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain_net_handler(_plugin, options) { + sidechain = sidechain_type::peerplays; + + if (options.count("peerplays-private-key")) { + const std::vector pub_priv_keys = options["peerplays-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Peerplays Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { + FC_THROW("Invalid public private key pair."); + } + private_keys[key_pair.first] = key_pair.second; + } + } + + database.applied_block.connect([&](const signed_block &b) { + on_applied_block(b); + }); +} + +sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { +} + +bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po) { + + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + + bool should_approve = false; + + const chain::global_property_object &gpo = database.get_global_properties(); + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po.proposed_transaction.operations.size() >= 1) { + op_idx_0 = po.proposed_transaction.operations[0].which(); + op_obj_idx_0 = po.proposed_transaction.operations[0]; + } + + switch (op_idx_0) { + + case chain::operation::tag::value: { + should_approve = false; + break; + } + + case chain::operation::tag::value: { + son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(swdo_id); + if (swdo != idx.end()) { + + uint32_t swdo_block_num = swdo->block_num; + std::string swdo_sidechain_transaction_id = swdo->sidechain_transaction_id; + uint32_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); + + const auto &block = database.fetch_block_by_number(swdo_block_num); + + for (const auto &tx : block->transactions) { + if (tx.id().str() == swdo_sidechain_transaction_id) { + operation op = tx.operations[swdo_op_idx]; + transfer_operation t_op = op.get(); + + asset sidechain_asset = asset(swdo->sidechain_amount, fc::variant(swdo->sidechain_currency, 1).as(1)); + price sidechain_asset_price = database.get(sidechain_asset.asset_id).options.core_exchange_rate; + asset peerplays_asset = asset(sidechain_asset.amount * sidechain_asset_price.base.amount / sidechain_asset_price.quote.amount); + + should_approve = (gpo.parameters.son_account() == t_op.to) && + (swdo->peerplays_from == t_op.from) && + (sidechain_asset == t_op.amount) && + (swdo->peerplays_asset == peerplays_asset); + break; + } + } + } + break; + } + + case chain::operation::tag::value: { + should_approve = false; + break; + } + + case chain::operation::tag::value: { + should_approve = true; + break; + } + + default: + should_approve = false; + elog("=================================================="); + elog("Proposal not considered for approval ${po}", ("po", po)); + elog("=================================================="); + } + + return should_approve; +} + +void sidechain_net_handler_peerplays::process_primary_wallet() { + return; +} + +void sidechain_net_handler_peerplays::process_sidechain_addresses() { + const auto &sidechain_addresses_idx = database.get_index_type(); + const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); + const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); + std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, + [&](const sidechain_address_object &sao) { + if (sao.expires == time_point_sec::maximum()) { + if (sao.deposit_address == "") { + sidechain_address_update_operation op; + op.payer = plugin.get_current_son_object().son_account; + op.sidechain_address_id = sao.id; + op.sidechain_address_account = sao.sidechain_address_account; + op.sidechain = sao.sidechain; + op.deposit_public_key = sao.deposit_public_key; + op.deposit_address = sao.withdraw_address; + op.deposit_address_data = sao.withdraw_address; + op.withdraw_public_key = sao.withdraw_public_key; + op.withdraw_address = sao.withdraw_address; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending transaction for update deposit address operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + } + }); + return; +} + +bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { + + const chain::global_property_object &gpo = database.get_global_properties(); + + asset_issue_operation ai_op; + ai_op.issuer = gpo.parameters.son_account(); + price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; + ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset()); + ai_op.issue_to_account = swdo.peerplays_from; + + signed_transaction tx; + auto dyn_props = database.get_dynamic_global_properties(); + tx.set_reference_block(dyn_props.head_block_id); + tx.set_expiration(database.head_block_time() + gpo.parameters.maximum_time_until_expiration); + tx.operations.push_back(ai_op); + database.current_fee_schedule().set_fee(tx.operations.back()); + + std::stringstream ss; + fc::raw::pack(ss, tx, 1000); + std::string tx_str = boost::algorithm::hex(ss.str()); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swdo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(stc_op); + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { + return true; +} + +std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto) { + + std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction)); + signed_transaction trx; + fc::raw::unpack(ss_trx, trx, 1000); + + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + signature_type st = trx.sign(*privkey, database.get_chain_id()); + + std::stringstream ss_st; + fc::raw::pack(ss_st, st, 1000); + std::string st_str = boost::algorithm::hex(ss_st.str()); + + return st_str; +} + +std::string sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) { + + std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction)); + signed_transaction trx; + fc::raw::unpack(ss_trx, trx, 1000); + + for (auto signature : sto.signatures) { + if (!signature.second.empty()) { + std::stringstream ss_st(boost::algorithm::unhex(signature.second)); + signature_type st; + fc::raw::unpack(ss_st, st, 1000); + + trx.signatures.push_back(st); + trx.signees.clear(); + } + } + + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return trx.id().str(); + } catch (fc::exception e) { + elog("Sidechain transaction failed with exception ${e}", ("e", e.what())); + return ""; + } + + return ""; +} + +int64_t sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidechain_transaction_object &sto) { + int64_t settle_amount = 0; + return settle_amount; +} + +void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { + for (const auto &trx : b.transactions) { + size_t operation_index = -1; + for (auto op : trx.operations) { + operation_index = operation_index + 1; + if (op.which() == operation::tag::value) { + transfer_operation transfer_op = op.get(); + if (transfer_op.to != plugin.database().get_global_properties().parameters.son_account()) { + continue; + } + + std::stringstream ss; + ss << "peerplays" + << "-" << trx.id().str() << "-" << operation_index; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = database.head_block_time(); + sed.block_num = database.head_block_num(); + sed.sidechain = sidechain_type::peerplays; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = trx.id().str(); + sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); + sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); + sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); + sed.sidechain_amount = transfer_op.amount.amount; + sed.peerplays_from = transfer_op.from; + sed.peerplays_to = transfer_op.to; + price asset_price = database.get(transfer_op.amount.asset_id).options.core_exchange_rate; + sed.peerplays_asset = asset(transfer_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + sidechain_event_data_received(sed); + } + } + } +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp new file mode 100644 index 000000000..962488a6d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -0,0 +1,90 @@ +#include + +#include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) : + plugin(_plugin), + database(_plugin.database()) { +} + +sidechain_net_manager::~sidechain_net_manager() { +} + +bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) { + + bool ret_val = false; + + switch (sidechain) { + case sidechain_type::bitcoin: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; + } + case sidechain_type::peerplays: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + break; + } + default: + assert(false); + } + + return ret_val; +} + +void sidechain_net_manager::process_proposals() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_proposals(); + } +} + +void sidechain_net_manager::process_active_sons_change() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_active_sons_change(); + } +} + +void sidechain_net_manager::create_deposit_addresses() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->create_deposit_addresses(); + } +} + +void sidechain_net_manager::process_deposits() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_deposits(); + } +} + +void sidechain_net_manager::process_withdrawals() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_withdrawals(); + } +} + +void sidechain_net_manager::process_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->process_sidechain_transactions(); + } +} + +void sidechain_net_manager::send_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->send_sidechain_transactions(); + } +} + +void sidechain_net_manager::settle_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->settle_sidechain_transactions(); + } +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7f5913287..4887f35b5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1294,6 +1294,12 @@ class wallet_api */ map list_committee_members(const string& lowerbound, uint32_t limit); + /** Returns information about the given SON. + * @param owner_account the name or id of the SON account owner, or the id of the SON + * @returns the information about the SON stored in the block chain + */ + son_object get_son(string owner_account); + /** Returns information about the given witness. * @param owner_account the name or id of the witness account owner, or the id of the witness * @returns the information about the witness stored in the block chain @@ -1312,6 +1318,179 @@ class wallet_api */ committee_member_object get_committee_member(string owner_account); + + /** Creates a SON object owned by the given account. + * + * An account can have at most one SON object. + * + * @param owner_account the name or id of the account which is creating the SON + * @param url a URL to include in the SON record in the blockchain. Clients may + * display this when showing a list of SONs. May be blank. + * @param deposit_id vesting balance id for SON deposit + * @param pay_vb_id vesting balance id for SON pay_vb + * @param sidechain_public_keys The new set of sidechain public keys. + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction registering a SON + */ + signed_transaction create_son(string owner_account, + string url, + vesting_balance_id_type deposit_id, + vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, + bool broadcast = false); + + /** Creates a SON object owned by the given account. + * + * Tries to create a SON object owned by the given account using + * existing vesting balances, fails if can't quess matching + * vesting balance objects. If several vesting balance objects matches + * this function uses the recent one. + * + * @param owner_account the name or id of the account which is creating the SON + * @param url a URL to include in the SON record in the blockchain. Clients may + * display this when showing a list of SONs. May be blank. + * @param sidechain_public_keys The new set of sidechain public keys. + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction registering a SON + */ + signed_transaction try_create_son(string owner_account, + string url, + flat_map sidechain_public_keys, + bool broadcast = false); + + /** + * Update a SON object owned by the given account. + * + * @param owner_account The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON. + * @param url Same as for create_son. The empty string makes it remain the same. + * @param block_signing_key The new block signing public key. The empty string makes it remain the same. + * @param sidechain_public_keys The new set of sidechain public keys. The empty string makes it remain the same. + * @param broadcast true if you wish to broadcast the transaction. + */ + signed_transaction update_son(string owner_account, + string url, + string block_signing_key, + flat_map sidechain_public_keys, + bool broadcast = false); + + /** Modify status of the SON owned by the given account to maintenance. + * + * @param owner_account the name or id of the account which is owning the SON + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction + */ + signed_transaction request_son_maintenance(string owner_account, + bool broadcast = false); + + /** Modify status of the SON owned by the given account back to active. + * + * @param owner_account the name or id of the account which is owning the SON + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction + */ + signed_transaction cancel_request_son_maintenance(string owner_account, + bool broadcast = false); + + /** Lists all SONs in the blockchain. + * This returns a list of all account names that own SON, and the associated SON id, + * sorted by name. This lists SONs whether they are currently voted in or not. + * + * Use the \c lowerbound and limit parameters to page through the list. To retrieve all SONs, + * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass + * the last SON name returned as the \c lowerbound for the next \c list_sons() call. + * + * @param lowerbound the name of the first SON to return. If the named SON does not exist, + * the list will start at the SON that comes after \c lowerbound + * @param limit the maximum number of SON to return (max: 1000) + * @returns a list of SON mapping SON names to SON ids + */ + map list_sons(const string& lowerbound, uint32_t limit); + + /** Lists active at the moment SONs. + * This returns a list of all account names that own active SON, and the associated SON id, + * sorted by name. + * @returns a list of active SONs mapping SON names to SON ids + */ + map list_active_sons(); + + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + + /** Adds sidechain address owned by the given account for a given sidechain. + * + * An account can have at most one sidechain address for one sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain a sidechain to whom address belongs + * @param deposit_public_key sidechain public key used for deposit address + * @param withdraw_public_key sidechain public key used for withdraw address + * @param withdraw_address sidechain address for withdrawals + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction adding sidechain address + */ + signed_transaction add_sidechain_address(string account, + sidechain_type sidechain, + string deposit_public_key, + string withdraw_public_key, + string withdraw_address, + bool broadcast = false); + + /** Deletes existing sidechain address owned by the given account for a given sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain a sidechain to whom address belongs + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction updating sidechain address + */ + signed_transaction delete_sidechain_address(string account, + sidechain_type sidechain, + bool broadcast = false); + + /** Retrieves all sidechain addresses owned by given account. + * + * @param account the name or id of the account who owns the address + * @returns the list of all sidechain addresses owned by given account. + */ + vector> get_sidechain_addresses_by_account(string account); + + /** Retrieves all sidechain addresses registered for a given sidechain. + * + * @param sidechain the name of the sidechain + * @returns the list of all sidechain addresses registered for a given sidechain. + */ + vector> get_sidechain_addresses_by_sidechain(sidechain_type sidechain); + + /** Retrieves sidechain address owned by given account for a given sidechain. + * + * @param account the name or id of the account who owns the address + * @param sidechain the name of the sidechain + * @returns the sidechain address owned by given account for a given sidechain. + */ + fc::optional get_sidechain_address_by_account_and_sidechain(string account, sidechain_type sidechain); + + /** Retrieves the total number of sidechain addresses registered in the system. + * + * @returns the total number of sidechain addresses registered in the system. + */ + uint64_t get_sidechain_addresses_count(); + /** Creates a witness object owned by the given account. * * An account can have at most one witness object. @@ -1376,6 +1555,21 @@ class wallet_api bool broadcast = false ); + /** Creates a vesting deposit owned by the given account. + * + * @param owner_account vesting balance owner and creator (the name or id) + * @param amount amount to vest + * @param asset_symbol the symbol of the asset to vest + * @param vesting_type "normal", "gpos" or "son" + * @param broadcast true to broadcast the transaction on the network + * @returns the signed transaction registering a vesting object + */ + signed_transaction create_vesting_balance(string owner_account, + string amount, + string asset_symbol, + vesting_balance_type vesting_type, + bool broadcast = false); + /** * Get information about a vesting balance object. * @@ -1433,6 +1627,61 @@ class wallet_api bool approve, bool broadcast = false); + /** Vote for a given SON. + * + * An account can publish a list of all SONs they approve of. This + * command allows you to add or remove SONs from this list. + * Each account's vote is weighted according to the number of shares of the + * core asset owned by that account at the time the votes are tallied. + * + * @note you cannot vote against a SON, you can only vote for the SON + * or not vote for the SON. + * + * @param voting_account the name or id of the account who is voting with their shares + * @param son the name or id of the SONs' owner account + * @param approve true if you wish to vote in favor of that SON, false to + * remove your vote in favor of that SON + * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction changing your vote for the given SON + */ + signed_transaction vote_for_son(string voting_account, + string son, + bool approve, + bool broadcast = false); + + /** Change your SON votes. + * + * An account can publish a list of all SONs they approve of. + * Each account's vote is weighted according to the number of shares of the + * core asset owned by that account at the time the votes are tallied. + * This command allows you to add or remove one or more SON from this list + * in one call. When you are changing your vote on several SONs, this + * may be easier than multiple `vote_for_sons` and + * `set_desired_witness_and_committee_member_count` calls. + * + * @note you cannot vote against a SON, you can only vote for the SON + * or not vote for the SON. + * + * @param voting_account the name or id of the account who is voting with their shares + * @param sons_to_approve the names or ids of the sons owner accounts you wish + * to approve (these will be added to the list of sons + * you currently approve). This list can be empty. + * @param sons_to_reject the names or ids of the SONs owner accounts you wish + * to reject (these will be removed from the list of SONs + * you currently approve). This list can be empty. + * @param desired_number_of_sons the number of SONs you believe the network + * should have. You must vote for at least this many + * SONs. You can set this to 0 to abstain from + * voting on the number of SONNs. + * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction changing your vote for the given witnesses + */ + signed_transaction update_son_votes(string voting_account, + std::vector sons_to_approve, + std::vector sons_to_reject, + uint16_t desired_number_of_son, + bool broadcast = false); + /** Vote for a given witness. * * An account can publish a list of all witnesses they approve of. This @@ -1881,20 +2130,6 @@ class wallet_api rock_paper_scissors_gesture gesture, bool broadcast); - /** Create a vesting balance including gpos vesting balance after HARDFORK_GPOS_TIME - * @param owner vesting balance owner and creator - * @param amount amount to vest - * @param asset_symbol the symbol of the asset to vest - * @param is_gpos True if the balance is of gpos type - * @param broadcast true if you wish to broadcast the transaction - * @return the signed version of the transaction - */ - signed_transaction create_vesting_balance(string owner, - string amount, - string asset_symbol, - bool is_gpos, - bool broadcast); - signed_transaction create_custom_permission(string owner, string permission_name, authority auth, @@ -1950,6 +2185,7 @@ class wallet_api optional revenue_split, bool is_transferable, bool is_sellable, + optional role_id, bool broadcast); /** @@ -1975,6 +2211,7 @@ class wallet_api optional revenue_split, optional is_transferable, optional is_sellable, + optional role_id, bool broadcast); /** @@ -2113,6 +2350,28 @@ class wallet_api vector get_offer_history_by_item(const nft_id_type item, uint32_t limit, optional lower_id) const; vector get_offer_history_by_bidder(string bidder_account_id_or_name, uint32_t limit, optional lower_id) const; + signed_transaction create_account_role(string owner_account_id_or_name, + string name, + string metadata, + flat_set allowed_operations, + flat_set whitelisted_accounts, + time_point_sec valid_to, + bool broadcast); + signed_transaction update_account_role(string owner_account_id_or_name, + account_role_id_type role_id, + optional name, + optional metadata, + flat_set operations_to_add, + flat_set operations_to_remove, + flat_set accounts_to_add, + flat_set accounts_to_remove, + optional valid_to, + bool broadcast); + signed_transaction delete_account_role(string owner_account_id_or_name, + account_role_id_type role_id, + bool broadcast); + vector get_account_roles_by_owner(string owner_account_id_or_name) const; + void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); void dbg_push_blocks( std::string src_filename, uint32_t count ); @@ -2266,11 +2525,28 @@ FC_API( graphene::wallet::wallet_api, (settle_asset) (whitelist_account) (create_committee_member) + (get_son) (get_witness) (is_witness) (get_committee_member) (list_witnesses) (list_committee_members) + (create_son) + (try_create_son) + (update_son) + (list_sons) + (list_active_sons) + (request_son_maintenance) + (cancel_request_son_maintenance) + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) + (add_sidechain_address) + (delete_sidechain_address) + (get_sidechain_addresses_by_account) + (get_sidechain_addresses_by_sidechain) + (get_sidechain_address_by_account_and_sidechain) + (get_sidechain_addresses_count) (create_witness) (update_witness) (create_worker) @@ -2279,6 +2555,8 @@ FC_API( graphene::wallet::wallet_api, (withdraw_vesting) (withdraw_GPOS_vesting_balance) (vote_for_committee_member) + (vote_for_son) + (update_son_votes) (vote_for_witness) (update_witness_votes) (set_voting_proxy) @@ -2387,6 +2665,10 @@ FC_API( graphene::wallet::wallet_api, (get_offer_history_by_issuer) (get_offer_history_by_item) (get_offer_history_by_bidder) + (create_account_role) + (update_account_role) + (delete_account_role) + (get_account_roles_by_owner) (get_upcoming_tournaments) (get_tournaments) (get_tournaments_by_state) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 97b31370f..4b44ad6e2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -74,6 +74,7 @@ #include #include +#include #include #include @@ -683,6 +684,7 @@ class wallet_api_impl result["participation"] = (100*dynamic_props.recent_slots_filled.popcount()) / 128.0; result["active_witnesses"] = fc::variant(global_props.active_witnesses, GRAPHENE_MAX_NESTED_OBJECTS); result["active_committee_members"] = fc::variant(global_props.active_committee_members, GRAPHENE_MAX_NESTED_OBJECTS); + result["active_sons"] = fc::variant(global_props.active_sons, GRAPHENE_MAX_NESTED_OBJECTS); result["entropy"] = fc::variant(dynamic_props.random, GRAPHENE_MAX_NESTED_OBJECTS); return result; } @@ -853,6 +855,7 @@ class wallet_api_impl // account, false otherwise (but it is stored either way) bool import_key(string account_name_or_id, string wif_key) { + fc::scoped_lock lock(_resync_mutex); fc::optional optional_private_key = wif_to_key(wif_key); if (!optional_private_key) FC_THROW("Invalid private key"); @@ -1394,6 +1397,7 @@ class wallet_api_impl bool broadcast = false, bool save_wallet = true) { try { + fc::scoped_lock lock(_resync_mutex); int active_key_index = find_first_unused_derived_key_index(owner_privkey); fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index); @@ -1824,6 +1828,41 @@ class wallet_api_impl FC_CAPTURE_AND_RETHROW( (owner_account) ) } + son_object get_son(string owner_account) + { + try + { + fc::optional son_id = maybe_id(owner_account); + if (son_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*son_id); + std::vector> son_objects = _remote_db->get_sons(ids_to_get); + if (son_objects.front()) + return *son_objects.front(); + FC_THROW("No SON is registered for id ${id}", ("id", owner_account)); + } + else + { + // then maybe it's the owner account + try + { + account_id_type owner_account_id = get_account_id(owner_account); + fc::optional son = _remote_db->get_son_by_account(owner_account_id); + if (son) + return *son; + else + FC_THROW("No SON is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or SON named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + bool is_witness(string owner_account) { try @@ -1894,10 +1933,204 @@ class wallet_api_impl FC_CAPTURE_AND_RETHROW( (owner_account) ) } + signed_transaction create_son(string owner_account, + string url, + vesting_balance_id_type deposit_id, + vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, + bool broadcast /* = false */) + { try { + fc::scoped_lock lock(_resync_mutex); + account_object son_account = get_account(owner_account); + auto son_public_key = son_account.active.get_keys()[0]; + + son_create_operation son_create_op; + son_create_op.owner_account = son_account.id; + son_create_op.signing_key = son_public_key; + son_create_op.url = url; + son_create_op.deposit = deposit_id; + son_create_op.pay_vb = pay_vb_id; + son_create_op.sidechain_public_keys = sidechain_public_keys; + + if (_remote_db->get_son_by_account(son_create_op.owner_account)) + FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account)); + + signed_transaction tx; + tx.operations.push_back( son_create_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + + signed_transaction update_son(string owner_account, + string url, + string block_signing_key, + flat_map sidechain_public_keys, + bool broadcast /* = false */) + { try { + son_object son = get_son(owner_account); + + son_update_operation son_update_op; + son_update_op.son_id = son.id; + son_update_op.owner_account = son.son_account; + if( url != "" ) + son_update_op.new_url = url; + if( block_signing_key != "" ) { + son_update_op.new_signing_key = public_key_type( block_signing_key ); + } + if( !sidechain_public_keys.empty() ) { + son_update_op.new_sidechain_public_keys = sidechain_public_keys; + } + + signed_transaction tx; + tx.operations.push_back( son_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) } + + signed_transaction request_son_maintenance(string owner_account, + bool broadcast) + { try { + son_object son = get_son(owner_account); + + son_maintenance_operation op; + op.owner_account = son.son_account; + op.son_id = son.id; + op.request_type = son_maintenance_request_type::request_maintenance; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + + signed_transaction cancel_request_son_maintenance(string owner_account, + bool broadcast) + { try { + son_object son = get_son(owner_account); + + son_maintenance_operation op; + op.owner_account = son.son_account; + op.son_id = son.id; + op.request_type = son_maintenance_request_type::cancel_request_maintenance; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + + map list_active_sons() + { try { + global_property_object gpo = get_global_properties(); + vector son_ids; + son_ids.reserve(gpo.active_sons.size()); + std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + std::inserter(son_ids, son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + std::vector> son_objects = _remote_db->get_sons(son_ids); + vector owners; + for(auto obj: son_objects) + { + if (obj) + { + std::string acc_id = account_id_to_string(obj->son_account); + owners.push_back(acc_id); + } + } + vector< optional< account_object> > accs = _remote_db->get_accounts(owners); + std::remove_if(son_objects.begin(), son_objects.end(), + [](const fc::optional& obj) -> bool { return obj.valid(); }); + map result; + std::transform(accs.begin(), accs.end(), son_objects.begin(), + std::inserter(result, result.end()), + [](fc::optional& acct, fc::optional son) { + FC_ASSERT(acct, "Invalid active SONs list in global properties."); + if (son.valid() && son->status != son_status::deregistered) + return std::make_pair(string(acct->name), std::move(son->id)); + return std::make_pair(string(acct->name), std::move(son_id_type())); + }); + return result; + } FC_CAPTURE_AND_RETHROW() } + + optional get_active_son_wallet() + { try { + return _remote_db->get_active_son_wallet(); + } FC_CAPTURE_AND_RETHROW() } + + optional get_son_wallet_by_time_point(time_point_sec time_point) + { try { + return _remote_db->get_son_wallet_by_time_point(time_point); + } FC_CAPTURE_AND_RETHROW() } + + vector> get_son_wallets(uint32_t limit) + { try { + return _remote_db->get_son_wallets(limit); + } FC_CAPTURE_AND_RETHROW() } + + signed_transaction add_sidechain_address(string account, + sidechain_type sidechain, + string deposit_public_key, + string withdraw_public_key, + string withdraw_address, + bool broadcast /* = false */) + { try { + account_id_type sidechain_address_account_id = get_account_id(account); + + sidechain_address_add_operation op; + op.payer = sidechain_address_account_id; + op.sidechain_address_account = sidechain_address_account_id; + op.sidechain = sidechain; + op.deposit_public_key = deposit_public_key; + op.withdraw_public_key = withdraw_public_key; + op.withdraw_address = withdraw_address; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW() } + + signed_transaction delete_sidechain_address(string account, + sidechain_type sidechain, + bool broadcast /* = false */) + { try { + account_id_type sidechain_address_account_id = get_account_id(account); + fc::optional sao = _remote_db->get_sidechain_address_by_account_and_sidechain(sidechain_address_account_id, sidechain); + if (!sao) + FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain)); + + sidechain_address_delete_operation op; + op.payer = sidechain_address_account_id; + op.sidechain_address_id = sao->id; + op.sidechain_address_account = sidechain_address_account_id; + op.sidechain = sidechain; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + + } FC_CAPTURE_AND_RETHROW() } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) { try { + fc::scoped_lock lock(_resync_mutex); account_object witness_account = get_account(owner_account); fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account); int witness_key_index = find_first_unused_derived_key_index(active_private_key); @@ -2069,6 +2302,33 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + signed_transaction create_vesting_balance(string owner_account, + string amount, + string asset_symbol, + vesting_balance_type vesting_type, + bool broadcast /* = false */) + { try { + FC_ASSERT( !is_locked() ); + account_object user_account = get_account(owner_account); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Invalid asset symbol {asst}", ("asst", asset_symbol)); + + vesting_balance_create_operation op; + op.creator = user_account.get_id(); + op.owner = user_account.get_id(); + op.amount = asset_obj->amount_from_string(amount); + op.balance_type = vesting_type; + if (op.balance_type == vesting_balance_type::son) + op.policy = dormant_vesting_policy_initializer {}; + + signed_transaction tx; + tx.operations.push_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -2114,8 +2374,8 @@ class wallet_api_impl vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid ); - if(vbo.balance_type != vesting_balance_type::normal) - FC_THROW("Allowed to withdraw only Normal type vest balances with this method"); + if(vbo.balance_type == vesting_balance_type::gpos) + FC_THROW("Allowed to withdraw only Normal and Son type vest balances with this method"); vesting_balance_withdraw_operation vesting_balance_withdraw_op; @@ -2246,6 +2506,95 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) } + signed_transaction vote_for_son(string voting_account, + string son, + bool approve, + bool broadcast /* = false */) + { try { + + std::vector vbo_info = get_vesting_balances(voting_account); + std::vector::iterator vbo_iter; + vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); + if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the SON account", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); + + account_object voting_account_object = get_account(voting_account); + account_id_type son_account_id = get_account_id(son); + fc::optional son_obj = _remote_db->get_son_by_account(son_account_id); + if (!son_obj) + FC_THROW("Account ${son} is not registered as a son", ("son", son)); + if (approve) + { + auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son)); + } + else + { + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son)); + } + account_update_operation account_update_op; + account_update_op.account = voting_account_object.id; + account_update_op.new_options = voting_account_object.options; + + signed_transaction tx; + tx.operations.push_back( account_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (voting_account)(son)(approve)(broadcast) ) } + + signed_transaction update_son_votes(string voting_account, + std::vector sons_to_approve, + std::vector sons_to_reject, + uint16_t desired_number_of_sons, + bool broadcast /* = false */) + { try { + FC_ASSERT(sons_to_approve.size() || sons_to_reject.size(), "Both accepted and rejected lists can't be empty simultaneously"); + std::vector vbo_info = get_vesting_balances(voting_account); + std::vector::iterator vbo_iter; + vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;}); + if( vbo_info.size() == 0 || vbo_iter == vbo_info.end()) + FC_THROW("Account ${account} has no core Token ${TOKEN} vested and will not be allowed to vote for the SON account", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL)); + + account_object voting_account_object = get_account(voting_account); + for (const std::string& son : sons_to_approve) + { + account_id_type son_owner_account_id = get_account_id(son); + fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); + if (!son_obj) + FC_THROW("Account ${son} is not registered as a SON", ("son", son)); + auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); + } + for (const std::string& son : sons_to_reject) + { + account_id_type son_owner_account_id = get_account_id(son); + fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); + if (!son_obj) + FC_THROW("Account ${son} is not registered as a SON", ("son", son)); + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); + } + voting_account_object.options.num_son = desired_number_of_sons; + + account_update_operation account_update_op; + account_update_op.account = voting_account_object.id; + account_update_op.new_options = voting_account_object.options; + + signed_transaction tx; + tx.operations.push_back( account_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (voting_account)(sons_to_approve)(sons_to_reject)(desired_number_of_sons)(broadcast) ) } + signed_transaction vote_for_witness(string voting_account, string witness, bool approve, @@ -4439,6 +4788,11 @@ map wallet_api::list_committee_members(const st return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit); } +son_object wallet_api::get_son(string owner_account) +{ + return my->get_son(owner_account); +} + witness_object wallet_api::get_witness(string owner_account) { return my->get_witness(owner_account); @@ -4454,6 +4808,139 @@ committee_member_object wallet_api::get_committee_member(string owner_account) return my->get_committee_member(owner_account); } +signed_transaction wallet_api::create_vesting_balance(string owner_account, + string amount, + string asset_symbol, + vesting_balance_type vesting_type, + bool broadcast /* = false */) +{ + return my->create_vesting_balance(owner_account, amount, asset_symbol, vesting_type, broadcast); +} + +signed_transaction wallet_api::create_son(string owner_account, + string url, + vesting_balance_id_type deposit_id, + vesting_balance_id_type pay_vb_id, + flat_map sidechain_public_keys, + bool broadcast /* = false */) +{ + return my->create_son(owner_account, url, deposit_id, pay_vb_id, sidechain_public_keys, broadcast); +} + +signed_transaction wallet_api::try_create_son(string owner_account, + string url, + flat_map sidechain_public_keys, + bool broadcast /* = false */) +{ + vesting_balance_id_type deposit_id; + bool deposit_found = false; + vesting_balance_id_type pay_vb_id; + bool pay_vb_found = false; + vector vbs = get_vesting_balances(owner_account); + for(const auto& vb: vbs) + { + if ((vb.balance_type == vesting_balance_type::son) && + (vb.get_asset_amount() >= my->get_global_properties().parameters.son_vesting_amount()) && + (vb.policy.which() == vesting_policy::tag::value)) + { + deposit_found = true; + deposit_id = vb.id; + } + if ((vb.balance_type == vesting_balance_type::normal) && + (vb.policy.which() == vesting_policy::tag::value)) + { + pay_vb_found = true; + pay_vb_id = vb.id; + } + } + if (!deposit_found || !pay_vb_found) + FC_THROW("Failed to find vesting balance objects"); + return my->create_son(owner_account, url, deposit_id, pay_vb_id, sidechain_public_keys, broadcast); +} + +signed_transaction wallet_api::update_son(string owner_account, + string url, + string block_signing_key, + flat_map sidechain_public_keys, + bool broadcast /* = false */) +{ + return my->update_son(owner_account, url, block_signing_key, sidechain_public_keys, broadcast); +} + +signed_transaction wallet_api::request_son_maintenance(string owner_account, bool broadcast) +{ + return my->request_son_maintenance(owner_account, broadcast); +} + +signed_transaction wallet_api::cancel_request_son_maintenance(string owner_account, bool broadcast) +{ + return my->cancel_request_son_maintenance(owner_account, broadcast); +} + +map wallet_api::list_sons(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_son_accounts(lowerbound, limit); +} + +map wallet_api::list_active_sons() +{ + return my->list_active_sons(); +} + +optional wallet_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional wallet_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +vector> wallet_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + +signed_transaction wallet_api::add_sidechain_address(string account, + sidechain_type sidechain, + string deposit_public_key, + string withdraw_public_key, + string withdraw_address, + bool broadcast /* = false */) +{ + return my->add_sidechain_address(account, sidechain, deposit_public_key, withdraw_public_key, withdraw_address, broadcast); +} + +signed_transaction wallet_api::delete_sidechain_address(string account, + sidechain_type sidechain, + bool broadcast /* = false */) +{ + return my->delete_sidechain_address(account, sidechain, broadcast); +} + +vector> wallet_api::get_sidechain_addresses_by_account(string account) +{ + account_id_type account_id = get_account_id(account); + return my->_remote_db->get_sidechain_addresses_by_account(account_id); +} + +vector> wallet_api::get_sidechain_addresses_by_sidechain(sidechain_type sidechain) +{ + return my->_remote_db->get_sidechain_addresses_by_sidechain(sidechain); +} + +fc::optional wallet_api::get_sidechain_address_by_account_and_sidechain(string account, sidechain_type sidechain) +{ + account_id_type account_id = get_account_id(account); + return my->_remote_db->get_sidechain_address_by_account_and_sidechain(account_id, sidechain); +} + +uint64_t wallet_api::get_sidechain_addresses_count() +{ + return my->_remote_db->get_sidechain_addresses_count(); +} + signed_transaction wallet_api::create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -4523,6 +5010,23 @@ signed_transaction wallet_api::vote_for_committee_member(string voting_account, return my->vote_for_committee_member(voting_account, witness, approve, broadcast); } +signed_transaction wallet_api::vote_for_son(string voting_account, + string son, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_son(voting_account, son, approve, broadcast); +} + +signed_transaction wallet_api::update_son_votes(string voting_account, + std::vector sons_to_approve, + std::vector sons_to_reject, + uint16_t desired_number_of_sons, + bool broadcast /* = false */) +{ + return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast); +} + signed_transaction wallet_api::vote_for_witness(string voting_account, string witness, bool approve, @@ -6332,41 +6836,6 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id, return my->sign_transaction( tx, broadcast ); } -signed_transaction wallet_api::create_vesting_balance(string owner, - string amount, - string asset_symbol, - bool is_gpos, - bool broadcast) -{ - FC_ASSERT( !is_locked() ); - //Can be deleted after GPOS hardfork time - time_point_sec now = time_point::now(); - if(is_gpos && now < HARDFORK_GPOS_TIME) - FC_THROW("GPOS related functionality is not avaiable until next Spring"); - - account_object owner_account = get_account(owner); - account_id_type owner_id = owner_account.id; - - fc::optional asset_obj = get_asset(asset_symbol); - - auto type = vesting_balance_type::normal; - if(is_gpos) - type = vesting_balance_type::gpos; - - vesting_balance_create_operation op; - op.creator = owner_id; - op.owner = owner_id; - op.amount = asset_obj->amount_from_string(amount); - op.balance_type = type; - - signed_transaction trx; - trx.operations.push_back(op); - my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); - trx.validate(); - - return my->sign_transaction( trx, broadcast ); -} - signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_name, string name, string symbol, @@ -6375,6 +6844,7 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na optional revenue_split, bool is_transferable, bool is_sellable, + optional role_id, bool broadcast) { account_object owner_account = my->get_account(owner_account_id_or_name); @@ -6397,6 +6867,7 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na } op.is_transferable = is_transferable; op.is_sellable = is_sellable; + op.account_role = role_id; signed_transaction trx; trx.operations.push_back(op); @@ -6415,6 +6886,7 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na optional revenue_split, optional is_transferable, optional is_sellable, + optional role_id, bool broadcast) { account_object owner_account = my->get_account(owner_account_id_or_name); @@ -6438,6 +6910,7 @@ signed_transaction wallet_api::nft_metadata_update(string owner_account_id_or_na } op.is_transferable = is_transferable; op.is_sellable = is_sellable; + op.account_role = role_id; signed_transaction trx; trx.operations.push_back(op); @@ -6719,6 +7192,88 @@ vector wallet_api::get_offer_history_by_bidder(string bidd account_object bidder_account = my->get_account(bidder_account_id_or_name); return my->_remote_db->get_offer_history_by_bidder(lb_id, bidder_account.id, limit); } + +signed_transaction wallet_api::create_account_role(string owner_account_id_or_name, + string name, + string metadata, + flat_set allowed_operations, + flat_set whitelisted_accounts, + time_point_sec valid_to, + bool broadcast) +{ + account_object owner_account = my->get_account(owner_account_id_or_name); + + account_role_create_operation op; + op.owner = owner_account.id; + op.name = name; + op.metadata = metadata; + op.allowed_operations = allowed_operations; + op.whitelisted_accounts = whitelisted_accounts; + op.valid_to = valid_to; + + signed_transaction trx; + trx.operations.push_back(op); + my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); + trx.validate(); + + return my->sign_transaction( trx, broadcast ); +} + +signed_transaction wallet_api::update_account_role(string owner_account_id_or_name, + account_role_id_type role_id, + optional name, + optional metadata, + flat_set operations_to_add, + flat_set operations_to_remove, + flat_set accounts_to_add, + flat_set accounts_to_remove, + optional valid_to, + bool broadcast) +{ + account_object owner_account = my->get_account(owner_account_id_or_name); + + account_role_update_operation op; + op.owner = owner_account.id; + op.account_role_id = role_id; + op.name = name; + op.metadata = metadata; + op.allowed_operations_to_add = operations_to_add; + op.allowed_operations_to_remove = operations_to_remove; + op.accounts_to_add = accounts_to_add; + op.accounts_to_remove = accounts_to_remove; + op.valid_to = valid_to; + + signed_transaction trx; + trx.operations.push_back(op); + my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); + trx.validate(); + + return my->sign_transaction( trx, broadcast ); +} + +signed_transaction wallet_api::delete_account_role(string owner_account_id_or_name, + account_role_id_type role_id, + bool broadcast) +{ + account_object owner_account = my->get_account(owner_account_id_or_name); + + account_role_delete_operation op; + op.owner = owner_account.id; + op.account_role_id = role_id; + + signed_transaction trx; + trx.operations.push_back(op); + my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); + trx.validate(); + + return my->sign_transaction( trx, broadcast ); +} + +vector wallet_api::get_account_roles_by_owner(string owner_account_id_or_name) const +{ + account_object owner_account = my->get_account(owner_account_id_or_name); + return my->_remote_db->get_account_roles_by_owner(owner_account.id); +} // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info() { @@ -6774,7 +7329,8 @@ vesting_balance_object_with_info::vesting_balance_object_with_info( const vestin : vesting_balance_object( vbo ) { allowed_withdraw = get_allowed_withdraw( now ); - if(vbo.balance_type == vesting_balance_type::gpos) + if(vbo.balance_type == vesting_balance_type::gpos || + ((vbo.balance_type == vesting_balance_type::son) && (vbo.policy.which() == vesting_policy::tag::value))) allowed_withdraw_time = vbo.policy.get().begin_timestamp + vbo.policy.get().vesting_cliff_seconds; else allowed_withdraw_time = now; diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index b7abdabef..fda7f22dd 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -176,7 +176,7 @@ int main( int argc, char** argv ) fc::http::websocket_client client; idump((wdata.ws_server)); auto con = client.connect( wdata.ws_server ); - auto apic = std::make_shared(*con, GRAPHENE_MAX_NESTED_OBJECTS); + auto apic = std::make_shared(con, GRAPHENE_MAX_NESTED_OBJECTS); auto remote_api = apic->get_remote_api< login_api >(1); edump((wdata.ws_user)(wdata.ws_password) ); @@ -209,13 +209,14 @@ int main( int argc, char** argv ) wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " ); })); - auto _websocket_server = std::make_shared(); + std::shared_ptr _websocket_server; if( options.count("rpc-endpoint") ) { + _websocket_server = std::make_shared(); _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){ std::cout << "here... \n"; wlog("." ); - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); @@ -228,11 +229,12 @@ int main( int argc, char** argv ) if( options.count( "rpc-tls-certificate" ) ) cert_pem = options.at("rpc-tls-certificate").as(); - auto _websocket_tls_server = std::make_shared(cert_pem); + std::shared_ptr _websocket_tls_server; if( options.count("rpc-tls-endpoint") ) { + _websocket_tls_server = std::make_shared(cert_pem); _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - auto wsc = std::make_shared(*c, GRAPHENE_MAX_NESTED_OBJECTS); + auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); c->set_session_data( wsc ); }); diff --git a/programs/delayed_node/main.cpp b/programs/delayed_node/main.cpp index 112b7dee4..3e058b64c 100644 --- a/programs/delayed_node/main.cpp +++ b/programs/delayed_node/main.cpp @@ -70,10 +70,19 @@ int main(int argc, char** argv) { bpo::variables_map options; + bpo::options_description cli, cfg; + node.set_program_options(cli, cfg); + cfg_options.add(cfg); + + cfg_options.add_options() + ("plugins", bpo::value()->default_value("delayed_node account_history market_history"), + "Space-separated list of plugins to activate"); + auto delayed_plug = node.register_plugin(); auto history_plug = node.register_plugin(); auto market_history_plug = node.register_plugin(); + // add plugin options to config try { bpo::options_description cli, cfg; @@ -160,6 +169,10 @@ int main(int argc, char** argv) { elog("Error parsing configuration file: ${e}", ("e", e.what())); return 1; } + + if( !options.count("plugins") ) + options.insert( std::make_pair( "plugins", bpo::variable_value(std::string("delayed_node account_history market_history"), true) ) ); + node.initialize(data_dir, options); node.initialize_plugins( options ); diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 94a3296a8..34b861b18 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -47,6 +47,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index c83fc3635..d55783793 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_elasticsearch graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full graphene_snapshot graphene_es_objects fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_elasticsearch graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full graphene_snapshot graphene_es_objects fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index e07cec92b..192a38794 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -344,6 +344,7 @@ "maximum_asset_feed_publishers": 10, "maximum_witness_count": 1001, "maximum_committee_count": 1001, + "maximum_son_count": 15, "maximum_authority_membership": 10, "reserve_percent_of_fee": 2000, "network_percent_of_fee": 2000, @@ -384,7 +385,14 @@ "gpos_period": 15552000, "gpos_subperiod": 2592000, "gpos_period_start": 1601528400, - "gpos_vesting_lockin_period": 2592000 + "gpos_vesting_lockin_period": 2592000, + "son_vesting_amount": 5000000, + "son_vesting_period": 172800, + "son_pay_max": 20000000, + "son_pay_time": 86400, + "son_deregister_time": 43200, + "son_heartbeat_frequency": 180, + "son_down_time": 360 } }, "initial_bts_accounts": [], @@ -526,4 +534,4 @@ "num_special_accounts": 0, "num_special_assets": 0 } -} \ No newline at end of file +} diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 4d49d96f3..7823fed3e 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -35,6 +35,7 @@ //#include #include #include +#include #include #include @@ -42,8 +43,9 @@ #include #include - #include +#include +#include #include #include @@ -51,7 +53,7 @@ #include #ifdef WIN32 -# include +# include #else # include #endif @@ -67,12 +69,23 @@ int main(int argc, char** argv) { bpo::options_description cfg_options("Graphene Witness Node"); app_options.add_options() ("help,h", "Print this help message and exit.") - ("version", "Display the version info and exit") - ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; + ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), + "Directory containing databases, configuration file, etc.") + ("version,v", "Display version information") + ("plugins", bpo::value() + ->default_value("witness account_history market_history accounts_list affiliate_stats bookie"), + "Space-separated list of plugins to activate"); bpo::variables_map options; + bpo::options_description cli, cfg; + node->set_program_options(cli, cfg); + cfg_options.add(cfg); + + cfg_options.add_options() + ("plugins", bpo::value()->default_value("witness account_history market_history accounts_list affiliate_stats bookie"), + "Space-separated list of plugins to activate"); + auto witness_plug = node->register_plugin(); auto debug_witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); @@ -84,8 +97,10 @@ int main(int argc, char** argv) { auto list_plug = node->register_plugin(); auto affiliate_stats_plug = node->register_plugin(); auto bookie_plug = node->register_plugin(); + auto peerplays_sidechain = node->register_plugin(); auto snapshot_plug = node->register_plugin(); + // add plugin options to config try { bpo::options_description cli, cfg; @@ -100,11 +115,6 @@ int main(int argc, char** argv) { return 1; } - if( options.count("help") ) - { - std::cout << app_options << "\n"; - return 0; - } if (options.count("version")) { std::string witness_version(graphene::utilities::git_revision_description); @@ -118,6 +128,11 @@ int main(int argc, char** argv) { std::cout << "Boost: " << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", ".") << "\n"; return 0; } + if( options.count("help") ) + { + std::cout << app_options << "\n"; + return 0; + } fc::path data_dir; if( options.count("data-dir") ) @@ -126,10 +141,24 @@ int main(int argc, char** argv) { if( data_dir.is_relative() ) data_dir = fc::current_path() / data_dir; } - app::load_configuration_options(data_dir, cfg_options, options); + std::set plugins; + boost::split(plugins, options.at("plugins").as(), [](char c){return c == ' ';}); + + if(plugins.count("account_history") && plugins.count("elasticsearch")) { + std::cerr << "Plugin conflict: Cannot load both account_history plugin and elasticsearch plugin\n"; + return 1; + } + + std::for_each(plugins.begin(), plugins.end(), [node](const std::string& plug) mutable { + if (!plug.empty()) { + node->enable_plugin(plug); + } + }); + bpo::notify(options); + node->initialize(data_dir, options); node->initialize_plugins( options ); @@ -156,7 +185,7 @@ int main(int argc, char** argv) { node->shutdown_plugins(); node->shutdown(); delete node; - return 0; + return EXIT_SUCCESS; } catch( const fc::exception& e ) { // deleting the node can yield, so do this outside the exception handler unhandled_exception = e; @@ -167,6 +196,6 @@ int main(int argc, char** argv) { elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string())); node->shutdown(); delete node; - return 1; + return EXIT_FAILURE; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e57e3374a..014745822 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,6 +33,10 @@ file(GLOB BETTING_TESTS "betting/*.cpp") add_executable( betting_test ${BETTING_TESTS} ${COMMON_SOURCES} ) target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +file(GLOB PEERPLAYS_SIDECHAIN_TESTS "peerplays_sidechain/*.cpp") +add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ${COMMON_SOURCES} ) +target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) + file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} ) target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) @@ -46,7 +50,7 @@ add_executable( cli_test ${CLI_SOURCES} ) if(WIN32) list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32) endif() -target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_egenesis_none fc graphene_elasticsearch graphene_es_objects ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 623f760e0..28b8a1fc6 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -69,6 +69,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) boost::program_options::variables_map cfg; cfg.emplace("p2p-endpoint", boost::program_options::variable_value(string("127.0.0.1:0"), false)); + cfg.emplace("plugins", boost::program_options::variable_value(string(" "), false)); app1.initialize(app_dir.path(), cfg); cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); diff --git a/tests/cli/cli_fixture.cpp b/tests/cli/cli_fixture.cpp new file mode 100644 index 000000000..5b5fd7ad8 --- /dev/null +++ b/tests/cli/cli_fixture.cpp @@ -0,0 +1,277 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef _WIN32 +#ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 + #endif + #include + #include +#else +#include +#include +#include +#endif +#include + +#include +#include + +#include "cli_fixture.hpp" + +/***** + * Global Initialization for Windows + * ( sets up Winsock stuf ) + */ +#ifdef _WIN32 +int sockInit(void) +{ + WSADATA wsa_data; + return WSAStartup(MAKEWORD(1,1), &wsa_data); +} +int sockQuit(void) +{ + return WSACleanup(); +} +#endif + +/********************* + * Helper Methods + *********************/ + +#include "../common/genesis_file_util.hpp" + +////// +/// @brief attempt to find an available port on localhost +/// @returns an available port number, or -1 on error +///// +int get_available_port() +{ + struct sockaddr_in sin; + int socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd == -1) + return -1; + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1) + return -1; + socklen_t len = sizeof(sin); + if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1) + return -1; +#ifdef _WIN32 + closesocket(socket_fd); +#else + close(socket_fd); +#endif + return ntohs(sin.sin_port); +} + +/////////// +/// @brief Start the application +/// @param app_dir the temporary directory to use +/// @param server_port_number to be filled with the rpc endpoint port number +/// @returns the application object +////////// +std::shared_ptr start_application(fc::temp_directory& app_dir, int& server_port_number) { + std::shared_ptr app1(new graphene::app::application{}); + + app1->register_plugin(); + app1->register_plugin(); + app1->register_plugin(); + app1->register_plugin(); + app1->startup_plugins(); + boost::program_options::variables_map cfg; +#ifdef _WIN32 + sockInit(); +#endif + server_port_number = get_available_port(); + cfg.emplace( + "rpc-endpoint", + boost::program_options::variable_value(string("127.0.0.1:" + std::to_string(server_port_number)), false) + ); + cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); + cfg.emplace("seed-nodes", boost::program_options::variable_value(string("[]"), false)); + cfg.emplace("plugins", boost::program_options::variable_value(string("bookie account_history market_history"), false)); + + app1->initialize(app_dir.path(), cfg); + + app1->initialize_plugins(cfg); + app1->startup_plugins(); + + app1->startup(); + fc::usleep(fc::milliseconds(500)); + return app1; +} + +client_connection::client_connection( + std::shared_ptr app, + const fc::temp_directory& data_dir, + const int server_port_number +) +{ + wallet_data.chain_id = app->chain_database()->get_chain_id(); + wallet_data.ws_server = "ws://127.0.0.1:" + std::to_string(server_port_number); + wallet_data.ws_user = ""; + wallet_data.ws_password = ""; + websocket_connection = websocket_client.connect( wallet_data.ws_server ); + + api_connection = std::make_shared(websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); + + remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); + BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); + + wallet_api_ptr = std::make_shared(wallet_data, remote_login_api); + wallet_filename = data_dir.path().generic_string() + "/wallet.json"; + wallet_api_ptr->set_wallet_filename(wallet_filename); + + wallet_api = fc::api(wallet_api_ptr); + + wallet_cli = std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); + for( auto& name_formatter : wallet_api_ptr->get_result_formatters() ) + wallet_cli->format_result( name_formatter.first, name_formatter.second ); + + boost::signals2::scoped_connection closed_connection(websocket_connection->closed.connect([=]{ + cerr << "Server has disconnected us.\n"; + wallet_cli->stop(); + })); + (void)(closed_connection); +} + +client_connection::~client_connection() +{ + // wait for everything to finish up + fc::usleep(fc::milliseconds(500)); +} + +/////////////////////////////// +// Cli Wallet Fixture +/////////////////////////////// + +cli_fixture::cli_fixture() : + server_port_number(0), + app_dir( graphene::utilities::temp_directory_path() ), + app1( start_application(app_dir, server_port_number) ), + con( app1, app_dir, server_port_number ), + nathan_keys( {"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"} ) +{ + BOOST_TEST_MESSAGE("Setup cli_wallet::boost_fixture_test_case"); + + using namespace graphene::chain; + using namespace graphene::app; + + try + { + BOOST_TEST_MESSAGE("Setting wallet password"); + con.wallet_api_ptr->set_password("supersecret"); + con.wallet_api_ptr->unlock("supersecret"); + + // import Nathan account + BOOST_TEST_MESSAGE("Importing nathan key"); + BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); + BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + +cli_fixture::~cli_fixture() +{ + BOOST_TEST_MESSAGE("Cleanup cli_wallet::boost_fixture_test_case"); + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + + app1->shutdown(); +#ifdef _WIN32 + sockQuit(); +#endif +} + +bool cli_fixture::generate_maintenance_block() { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~database::skip_fork_db; + auto db = app1->chain_database(); + auto maint_time = db->get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db->get_slot_at_time(maint_time); + db->generate_block(db->get_slot_time(slots_to_miss), + db->get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (exception& e) + { + return false; + } +} + +bool cli_fixture::generate_block() +{ + graphene::chain::signed_block returned_block; + return generate_block(returned_block); +} + +bool cli_fixture::generate_block(graphene::chain::signed_block& returned_block) +{ + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + auto db = app1->chain_database(); + returned_block = db->generate_block( db->get_slot_time(1), + db->get_scheduled_witness(1), + committee_key, + database::skip_nothing ); + return true; + } catch (exception &e) { + return false; + } +} + +void cli_fixture::init_nathan() +{ + try + { + BOOST_TEST_MESSAGE("Upgrade Nathan's account"); + + account_object nathan_acct_before_upgrade, nathan_acct_after_upgrade; + std::vector import_txs; + signed_transaction upgrade_tx; + + BOOST_TEST_MESSAGE("Importing nathan's balance"); + import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true); + nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan"); + + BOOST_CHECK(generate_block()); + + // upgrade nathan + BOOST_TEST_MESSAGE("Upgrading Nathan to LTM"); + upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true); + + nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan"); + + // verify that the upgrade was successful + BOOST_CHECK_PREDICATE( + std::not_equal_to(), + (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) + (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) + ); + BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} diff --git a/tests/cli/cli_fixture.hpp b/tests/cli/cli_fixture.hpp new file mode 100644 index 000000000..ad3e9e33f --- /dev/null +++ b/tests/cli/cli_fixture.hpp @@ -0,0 +1,81 @@ +#include +#include + +#include +#include +#include +#include + +#define INVOKE(test) ((struct test*)this)->test_method(); + +int get_available_port(); + +std::shared_ptr start_application(fc::temp_directory& app_dir, int& server_port_number); + +/////////// +/// @brief a class to make connecting to the application server easier +/////////// +class client_connection +{ +public: + client_connection( + std::shared_ptr app, + const fc::temp_directory& data_dir, + const int server_port_number + ); + ~client_connection(); +public: + fc::http::websocket_client websocket_client; + graphene::wallet::wallet_data wallet_data; + fc::http::websocket_connection_ptr websocket_connection; + std::shared_ptr api_connection; + fc::api remote_login_api; + std::shared_ptr wallet_api_ptr; + fc::api wallet_api; + std::shared_ptr wallet_cli; + std::string wallet_filename; +}; + +/////////////////////////////// +// Cli Wallet Fixture +/////////////////////////////// +struct cli_fixture +{ + class dummy + { + public: + ~dummy() + { + // wait for everything to finish up + fc::usleep(fc::milliseconds(500)); + } + }; + dummy dmy; + int server_port_number; + fc::temp_directory app_dir; + std::shared_ptr app1; + client_connection con; + std::vector nathan_keys; + + cli_fixture(); + ~cli_fixture(); + + /////////// + /// Send a block to the db + /// @param returned_block the signed block + /// @returns true on success + /////////// + bool generate_block(graphene::chain::signed_block& returned_block); + bool generate_block(); + /////////// + /// @brief Skip intermediate blocks, and generate a maintenance block + /// @returns true on success + /////////// + bool generate_maintenance_block(); + + /////////// + // @brief init "nathan" account and make it LTM to use in tests + ////////// + void init_nathan(); +}; + diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 9e7a41193..36fb626cb 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -21,314 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include +#include "cli_fixture.hpp" #include #include -#ifdef _WIN32 -#ifndef _WIN32_WINNT - #define _WIN32_WINNT 0x0501 - #endif - #include - #include -#else -#include -#include -#include -#endif -#include - -#include +#include #define BOOST_TEST_MODULE Test Application #include -/***** - * Global Initialization for Windows - * ( sets up Winsock stuf ) - */ -#ifdef _WIN32 -int sockInit(void) -{ - WSADATA wsa_data; - return WSAStartup(MAKEWORD(1,1), &wsa_data); -} -int sockQuit(void) -{ - return WSACleanup(); -} -#endif - -/********************* - * Helper Methods - *********************/ - -#include "../common/genesis_file_util.hpp" - -using std::exception; -using std::cerr; - -#define INVOKE(test) ((struct test*)this)->test_method(); - -////// -/// @brief attempt to find an available port on localhost -/// @returns an available port number, or -1 on error -///// -int get_available_port() -{ - struct sockaddr_in sin; - int socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if (socket_fd == -1) - return -1; - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1) - return -1; - socklen_t len = sizeof(sin); - if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1) - return -1; -#ifdef _WIN32 - closesocket(socket_fd); -#else - close(socket_fd); -#endif - return ntohs(sin.sin_port); -} - -/////////// -/// @brief Start the application -/// @param app_dir the temporary directory to use -/// @param server_port_number to be filled with the rpc endpoint port number -/// @returns the application object -////////// -std::shared_ptr start_application(fc::temp_directory& app_dir, int& server_port_number) { - std::shared_ptr app1(new graphene::app::application{}); - - app1->register_plugin< graphene::bookie::bookie_plugin>(); - app1->register_plugin< graphene::account_history::account_history_plugin>(); - app1->register_plugin< graphene::market_history::market_history_plugin >(); - app1->register_plugin< graphene::witness_plugin::witness_plugin >(); - app1->register_plugin< graphene::accounts_list::accounts_list_plugin >(); - app1->register_plugin< graphene::affiliate_stats::affiliate_stats_plugin >(); - app1->startup_plugins(); - boost::program_options::variables_map cfg; -#ifdef _WIN32 - sockInit(); -#endif - server_port_number = get_available_port(); - cfg.emplace( - "rpc-endpoint", - boost::program_options::variable_value(string("127.0.0.1:" + std::to_string(server_port_number)), false) - ); - cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false)); - cfg.emplace("seed-nodes", boost::program_options::variable_value(string("[]"), false)); - - app1->initialize(app_dir.path(), cfg); - - app1->initialize_plugins(cfg); - app1->startup_plugins(); - - app1->startup(); - fc::usleep(fc::milliseconds(500)); - return app1; -} - -/////////// -/// Send a block to the db -/// @param app the application -/// @param returned_block the signed block -/// @returns true on success -/////////// -bool generate_block(std::shared_ptr app, graphene::chain::signed_block& returned_block) -{ - try { - fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); - auto db = app->chain_database(); - returned_block = db->generate_block( db->get_slot_time(1), - db->get_scheduled_witness(1), - committee_key, - database::skip_nothing ); - return true; - } catch (exception &e) { - return false; - } -} - -bool generate_block(std::shared_ptr app) -{ - graphene::chain::signed_block returned_block; - return generate_block(app, returned_block); -} - -/////////// -/// @brief Skip intermediate blocks, and generate a maintenance block -/// @param app the application -/// @returns true on success -/////////// -bool generate_maintenance_block(std::shared_ptr app) { - try { - fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); - uint32_t skip = ~0; - auto db = app->chain_database(); - auto maint_time = db->get_dynamic_global_properties().next_maintenance_time; - auto slots_to_miss = db->get_slot_at_time(maint_time); - db->generate_block(db->get_slot_time(slots_to_miss), - db->get_scheduled_witness(slots_to_miss), - committee_key, - skip); - return true; - } catch (exception& e) - { - return false; - } -} - -/////////// -/// @brief a class to make connecting to the application server easier -/////////// -class client_connection -{ -public: - ///////// - // constructor - ///////// - client_connection( - std::shared_ptr app, - const fc::temp_directory& data_dir, - const int server_port_number - ) - { - wallet_data.chain_id = app->chain_database()->get_chain_id(); - wallet_data.ws_server = "ws://127.0.0.1:" + std::to_string(server_port_number); - wallet_data.ws_user = ""; - wallet_data.ws_password = ""; - websocket_connection = websocket_client.connect( wallet_data.ws_server ); - - api_connection = std::make_shared(*websocket_connection, GRAPHENE_MAX_NESTED_OBJECTS); - - remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1); - BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) ); - - wallet_api_ptr = std::make_shared(wallet_data, remote_login_api); - wallet_filename = data_dir.path().generic_string() + "/wallet.json"; - wallet_api_ptr->set_wallet_filename(wallet_filename); - - wallet_api = fc::api(wallet_api_ptr); - - wallet_cli = std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); - for( auto& name_formatter : wallet_api_ptr->get_result_formatters() ) - wallet_cli->format_result( name_formatter.first, name_formatter.second ); - - boost::signals2::scoped_connection closed_connection(websocket_connection->closed.connect([=]{ - cerr << "Server has disconnected us.\n"; - wallet_cli->stop(); - })); - (void)(closed_connection); - } - ~client_connection() - { - // wait for everything to finish up - fc::usleep(fc::milliseconds(500)); - } -public: - fc::http::websocket_client websocket_client; - graphene::wallet::wallet_data wallet_data; - fc::http::websocket_connection_ptr websocket_connection; - std::shared_ptr api_connection; - fc::api remote_login_api; - std::shared_ptr wallet_api_ptr; - fc::api wallet_api; - std::shared_ptr wallet_cli; - std::string wallet_filename; -}; - - -/////////////////////////////// -// Cli Wallet Fixture -/////////////////////////////// - -struct cli_fixture -{ - class dummy - { - public: - ~dummy() - { - // wait for everything to finish up - fc::usleep(fc::milliseconds(500)); - } - }; - dummy dmy; - int server_port_number; - fc::temp_directory app_dir; - std::shared_ptr app1; - client_connection con; - std::vector nathan_keys; - - cli_fixture() : - server_port_number(0), - app_dir( graphene::utilities::temp_directory_path() ), - app1( start_application(app_dir, server_port_number) ), - con( app1, app_dir, server_port_number ), - nathan_keys( {"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"} ) - { - BOOST_TEST_MESSAGE("Setup cli_wallet::boost_fixture_test_case"); - - using namespace graphene::chain; - using namespace graphene::app; - - try - { - BOOST_TEST_MESSAGE("Setting wallet password"); - con.wallet_api_ptr->set_password("supersecret"); - con.wallet_api_ptr->unlock("supersecret"); - - // import Nathan account - BOOST_TEST_MESSAGE("Importing nathan key"); - BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); - BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } - } - - ~cli_fixture() - { - BOOST_TEST_MESSAGE("Cleanup cli_wallet::boost_fixture_test_case"); - - // wait for everything to finish up - fc::usleep(fc::seconds(1)); - - app1->shutdown(); -#ifdef _WIN32 - sockQuit(); -#endif - } -}; - /////////////////////////////// // Tests /////////////////////////////// @@ -336,7 +38,9 @@ struct cli_fixture //////////////// // Start a server and connect using the same calls as the CLI //////////////// -BOOST_FIXTURE_TEST_CASE( cli_connect, cli_fixture ) +BOOST_FIXTURE_TEST_SUITE( cli_common, cli_fixture ) + +BOOST_AUTO_TEST_CASE( cli_connect ) { BOOST_TEST_MESSAGE("Testing wallet connection."); } @@ -353,44 +57,14 @@ BOOST_FIXTURE_TEST_CASE( cli_quit, cli_fixture ) BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture ) { - try - { - BOOST_TEST_MESSAGE("Upgrade Nathan's account"); - - account_object nathan_acct_before_upgrade, nathan_acct_after_upgrade; - std::vector import_txs; - signed_transaction upgrade_tx; - - BOOST_TEST_MESSAGE("Importing nathan's balance"); - import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true); - nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan"); - - BOOST_CHECK(generate_block(app1)); - - // upgrade nathan - BOOST_TEST_MESSAGE("Upgrading Nathan to LTM"); - upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true); - - nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan"); - - // verify that the upgrade was successful - BOOST_CHECK_PREDICATE( - std::not_equal_to(), - (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) - (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) - ); - BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); - } catch( fc::exception& e ) { - edump((e.to_detail_string())); - throw; - } + init_nathan(); } -BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture ) +BOOST_AUTO_TEST_CASE( create_new_account ) { try { - INVOKE(upgrade_nathan_account); + init_nathan(); // create a new account graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); @@ -402,7 +76,7 @@ BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture ) BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - BOOST_CHECK(generate_block(app1)); + BOOST_CHECK(generate_block()); fc::usleep( fc::seconds(1) ); // attempt to give jmjatlanta some peerplays @@ -439,9 +113,9 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init1", true, true); // generate a block to get things started - BOOST_CHECK(generate_block(app1)); + BOOST_CHECK(generate_block()); // wait for a maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); + BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there init1_obj = con.wallet_api_ptr->get_witness("init1"); @@ -454,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture ) signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("jmjatlanta", "init2", true, true); // send another block to trigger maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); + BOOST_CHECK(generate_maintenance_block()); // Verify that both the first vote and the 2nd are there init2_obj = con.wallet_api_ptr->get_witness("init2"); @@ -510,7 +184,7 @@ BOOST_FIXTURE_TEST_CASE( cli_get_signed_transaction_signers, cli_fixture ) /////////////////////// // Check account history pagination /////////////////////// -BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) +BOOST_AUTO_TEST_CASE( account_history_pagination ) { try { @@ -524,7 +198,7 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) "1.3.0", "Here are some CORE token for your new account", true); } - BOOST_CHECK(generate_block(app1)); + BOOST_CHECK(generate_block()); // now get account history and make sure everything is there (and no duplicates) std::vector history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300); @@ -798,18 +472,16 @@ graphene::wallet::plain_keys decrypt_keys( const std::string& password, const ve return fc::raw::unpack( decrypted ); } -BOOST_AUTO_TEST_CASE( saving_keys_wallet_test ) +BOOST_FIXTURE_TEST_CASE( saving_keys_wallet_test, cli_fixture ) { - cli_fixture cli; - - cli.con.wallet_api_ptr->import_balance( "nathan", cli.nathan_keys, true ); - cli.con.wallet_api_ptr->upgrade_account( "nathan", true ); + con.wallet_api_ptr->import_balance( "nathan", nathan_keys, true ); + con.wallet_api_ptr->upgrade_account( "nathan", true ); std::string brain_key( "FICTIVE WEARY MINIBUS LENS HAWKIE MAIDISH MINTY GLYPH GYTE KNOT COCKSHY LENTIGO PROPS BIFORM KHUTBAH BRAZIL" ); - cli.con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true ); + con.wallet_api_ptr->create_account_with_brain_key( brain_key, "account1", "nathan", "nathan", true ); - BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) ); + BOOST_CHECK_NO_THROW( con.wallet_api_ptr->transfer( "nathan", "account1", "9000", "1.3.0", "", true ) ); - std::string path( cli.app_dir.path().generic_string() + "/wallet.json" ); + std::string path( app_dir.path().generic_string() + "/wallet.json" ); graphene::wallet::wallet_data wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); BOOST_CHECK( wallet.extra_keys.size() == 1 ); // nathan BOOST_CHECK( wallet.pending_account_registrations.size() == 1 ); // account1 @@ -818,14 +490,16 @@ BOOST_AUTO_TEST_CASE( saving_keys_wallet_test ) graphene::wallet::plain_keys pk = decrypt_keys( "supersecret", wallet.cipher_keys ); BOOST_CHECK( pk.keys.size() == 1 ); // nathan key - BOOST_CHECK( generate_block( cli.app1 ) ); + BOOST_CHECK( generate_block() ); fc::usleep( fc::seconds(1) ); wallet = fc::json::from_file( path ).as( 2 * GRAPHENE_MAX_NESTED_OBJECTS ); BOOST_CHECK( wallet.extra_keys.size() == 2 ); // nathan + account1 BOOST_CHECK( wallet.pending_account_registrations.empty() ); - BOOST_CHECK_NO_THROW( cli.con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) ); + BOOST_CHECK_NO_THROW( con.wallet_api_ptr->transfer( "account1", "nathan", "1000", "1.3.0", "", true ) ); pk = decrypt_keys( "supersecret", wallet.cipher_keys ); BOOST_CHECK( pk.keys.size() == 3 ); // nathan key + account1 active key + account1 memo key } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp new file mode 100644 index 000000000..87febbdde --- /dev/null +++ b/tests/cli/son.cpp @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2019 PBSA, 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 "cli_fixture.hpp" + +#include +#include + +#include + +class son_test_helper +{ + cli_fixture& fixture_; + +public: + son_test_helper(cli_fixture& fixture): + fixture_(fixture) + { + fixture_.init_nathan(); + } + + void create_son(const std::string& account_name, const std::string& son_url, + flat_map& sidechain_public_keys, + bool generate_maintenance = true) + { + graphene::wallet::brain_key_info bki; + signed_transaction create_tx; + signed_transaction transfer_tx; + signed_transaction upgrade_tx; + account_object son_account; + son_object son_obj; + + // create son account + bki = fixture_.con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + create_tx = fixture_.con.wallet_api_ptr->create_account_with_brain_key( + bki.brain_priv_key, account_name, "nathan", "nathan", true + ); + // save the private key for this new account in the wallet file + BOOST_CHECK(fixture_.con.wallet_api_ptr->import_key(account_name, bki.wif_priv_key)); + fixture_.con.wallet_api_ptr->save_wallet_file(fixture_.con.wallet_filename); + + // attempt to give son account some CORE tokens + BOOST_TEST_MESSAGE("Transferring CORE tokens from Nathan to son account"); + transfer_tx = fixture_.con.wallet_api_ptr->transfer( + "nathan", account_name, "65000", "1.3.0", "Here are some CORE token for your new account", true + ); + + BOOST_CHECK(fixture_.generate_block()); + + // upgrade son account + BOOST_TEST_MESSAGE("Upgrading son account to LTM"); + upgrade_tx = fixture_.con.wallet_api_ptr->upgrade_account(account_name, true); + son_account = fixture_.con.wallet_api_ptr->get_account(account_name); + + // verify that the upgrade was successful + BOOST_CHECK(son_account.is_lifetime_member()); + + BOOST_CHECK(fixture_.generate_block()); + + // create deposit vesting + fixture_.con.wallet_api_ptr->create_vesting_balance(account_name, + "50", "1.3.0", vesting_balance_type::son, true); + BOOST_CHECK(fixture_.generate_block()); + + // create pay_vb vesting + fixture_.con.wallet_api_ptr->create_vesting_balance(account_name, "1", "1.3.0", vesting_balance_type::normal, true); + BOOST_CHECK(fixture_.generate_block()); + + // check deposits are here + auto deposits = fixture_.con.wallet_api_ptr->get_vesting_balances(account_name); + BOOST_CHECK(deposits.size() >= 2); + + create_tx = fixture_.con.wallet_api_ptr->try_create_son(account_name, son_url, + sidechain_public_keys, + true); + + if (generate_maintenance) + BOOST_CHECK(fixture_.generate_maintenance_block()); + } + +}; + +/////////////////////// +// SON CLI +/////////////////////// +BOOST_FIXTURE_TEST_SUITE(son_cli, cli_fixture) + +BOOST_AUTO_TEST_CASE( create_sons ) +{ + BOOST_TEST_MESSAGE("SON cli wallet tests begin"); + try + { + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); + + auto son1_obj = con.wallet_api_ptr->get_son("son1account"); + BOOST_CHECK(son1_obj.son_account == con.wallet_api_ptr->get_account_id("son1account")); + BOOST_CHECK_EQUAL(son1_obj.url, "http://son1"); + + auto son2_obj = con.wallet_api_ptr->get_son("son2account"); + BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); + BOOST_CHECK_EQUAL(son2_obj.url, "http://son2"); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON cli wallet tests end"); +} + +BOOST_AUTO_TEST_CASE( cli_update_son ) +{ + try + { + BOOST_TEST_MESSAGE("Cli get_son and update_son Test"); + + flat_map sidechain_public_keys; + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + + son_test_helper sth(*this); + sth.create_son("sonmember", "http://sonmember", sidechain_public_keys); + + auto sonmember_acct = con.wallet_api_ptr->get_account("sonmember"); + + // get_son + auto son_data = con.wallet_api_ptr->get_son("sonmember"); + BOOST_CHECK(son_data.url == "http://sonmember"); + BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); + + // update SON + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + + con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); + son_data = con.wallet_api_ptr->get_son("sonmember"); + BOOST_CHECK(son_data.url == "http://sonmember_updated"); + + // update SON signing key + sidechain_public_keys.clear(); + con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated2", "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG", sidechain_public_keys, true); + son_data = con.wallet_api_ptr->get_son("sonmember"); + BOOST_CHECK(son_data.url == "http://sonmember_updated2"); + BOOST_CHECK(std::string(son_data.signing_key) == "TEST6Yaq5ZNTTkMM2kBBzV5jktr8ETsniCC3bnVD7eFmegRrLXfGGG"); + + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( son_voting ) +{ + BOOST_TEST_MESSAGE("SON Vote cli wallet tests begin"); + try + { + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); + + BOOST_TEST_MESSAGE("Voting for SONs"); + + son_object son1_obj; + son_object son2_obj; + signed_transaction vote_son1_tx; + signed_transaction vote_son2_tx; + uint64_t son1_start_votes, son1_end_votes; + uint64_t son2_start_votes, son2_end_votes; + + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_start_votes = son1_obj.total_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_start_votes = son2_obj.total_votes; + + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); + // Vote for a son1account + BOOST_TEST_MESSAGE("Voting for son1account"); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify that the vote is there + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes > son1_start_votes); + + // Vote for a son2account + BOOST_TEST_MESSAGE("Voting for son2account"); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", true, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify that the vote is there + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes > son2_start_votes); + + // Withdraw vote for a son1account + BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", false, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify that the vote is removed + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + + // Withdraw vote for a son2account + BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", false, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify that the vote is removed + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes == son2_start_votes); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON Vote cli wallet tests end"); +} + +BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON cli wallet tests begin"); + try + { + son_test_helper sth(*this); + + graphene::wallet::brain_key_info bki; + signed_transaction create_tx; + signed_transaction transfer_tx; + signed_transaction upgrade_tx; + signed_transaction vote_tx; + account_object acc_before_upgrade, acc_after_upgrade; + son_object son_obj; + global_property_object gpo; + + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count; + + flat_map sidechain_public_keys; + + // create son accounts + for(unsigned int i = 0; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + } + BOOST_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 0; i < son_number + 1; i++) + { + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + + std::string name = "sonaccount" + fc::to_pretty_string(i); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + + for(unsigned int i = 0; i < son_number; i++) + { + std::string name1 = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + } + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + + for(unsigned int i = 0; i < son_number - 1; i++) + { + std::string name1 = "sonaccount" + fc::to_pretty_string(i + 2); + std::string name2 = "sonaccount" + fc::to_pretty_string(i); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + } + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + + for(unsigned int i = 0; i < son_number - 2; i++) + { + std::string name1 = "sonaccount" + fc::to_pretty_string(i + 3); + std::string name2 = "sonaccount" + fc::to_pretty_string(i); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + } + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_CHECK(generate_maintenance_block()); + + BOOST_CHECK(gpo.active_sons.size() == 15); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON cli wallet tests end"); +} + +BOOST_AUTO_TEST_CASE( list_son ) +{ + BOOST_TEST_MESSAGE("List SONs cli wallet tests begin"); + try + { + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); + + auto res = con.wallet_api_ptr->list_sons("", 100); + BOOST_REQUIRE(res.size() == 2); + BOOST_CHECK(res.find("son1account") != res.end()); + BOOST_CHECK(res.find("son2account") != res.end()); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("List SONs cli wallet tests end"); +} + +BOOST_AUTO_TEST_CASE( update_son_votes_test ) +{ + BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests begin"); + try + { + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); + + BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); + + son_object son1_obj; + son_object son2_obj; + uint64_t son1_start_votes, son1_end_votes; + uint64_t son2_start_votes, son2_end_votes; + + // Get votes at start + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_start_votes = son1_obj.total_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_start_votes = son2_obj.total_votes; + + std::vector accepted; + std::vector rejected; + signed_transaction update_votes_tx; + + // Vote for both SONs + accepted.clear(); + rejected.clear(); + accepted.push_back("son1account"); + accepted.push_back("son2account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 2, true); + BOOST_CHECK(generate_block()); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes > son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes > son2_start_votes); + son2_start_votes = son2_end_votes; + + + // Withdraw vote for SON 1 + accepted.clear(); + rejected.clear(); + rejected.push_back("son1account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 1, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes < son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + // voice distribution changed, SON2 now has all voices + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power + son2_start_votes = son2_end_votes; + + // Try to reject incorrect SON + accepted.clear(); + rejected.clear(); + rejected.push_back("son1accnt"); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 1, true), fc::exception); + BOOST_CHECK(generate_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes == son2_start_votes); + son2_start_votes = son2_end_votes; + + // Reject SON2 + accepted.clear(); + rejected.clear(); + rejected.push_back("son2account"); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 0, true); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes < son2_start_votes); + son2_start_votes = son2_end_votes; + + // Try to accept and reject the same SON + accepted.clear(); + rejected.clear(); + rejected.push_back("son1accnt"); + accepted.push_back("son1accnt"); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 1, true), fc::exception); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes == son2_start_votes); + son2_start_votes = son2_end_votes; + + // Try to accept and reject empty lists + accepted.clear(); + rejected.clear(); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, + rejected, 1, true), fc::exception); + BOOST_CHECK(generate_maintenance_block()); + + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes == son1_start_votes); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes == son2_start_votes); + son2_start_votes = son2_end_votes; + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests end"); +} + +BOOST_AUTO_TEST_CASE( related_functions ) +{ + BOOST_TEST_MESSAGE("SON-related functions cli wallet tests begin"); + try + { + global_property_object gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons.size() == 0); + + flat_map sidechain_public_keys; + + son_test_helper sth(*this); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; + sth.create_son("son1account", "http://son1", sidechain_public_keys); + + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; + sth.create_son("son2account", "http://son2", sidechain_public_keys); + + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons.size() == 2); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON-related functions cli wallet tests end"); +} + +BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons begin"); + try + { + son_test_helper sth(*this); + + signed_transaction vote_tx; + global_property_object gpo; + + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count; + + flat_map sidechain_public_keys; + + // create son accounts + for(unsigned int i = 0; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + } + BOOST_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number + 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + + for(unsigned int i = 1; i < son_number; i++) + { + std::string name1 = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + + BOOST_CHECK(gpo.active_sons.size() == son_number); + + map active_sons = con.wallet_api_ptr->list_active_sons(); + BOOST_CHECK(active_sons.size() == son_number); + for(unsigned int i = 1; i < son_number + 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + BOOST_CHECK(active_sons.find(name) != active_sons.end()); + } + + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->list_active_sons()); + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons end"); +} + +BOOST_AUTO_TEST_CASE( maintenance_test ) +{ + BOOST_TEST_MESSAGE("SON maintenance cli wallet tests begin"); + try + { + son_test_helper sth(*this); + + std::string name("sonaccount1"); + + global_property_object gpo; + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count; + + flat_map sidechain_public_keys; + + // create son accounts + for(unsigned int i = 0; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + } + BOOST_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number + 1; i++) + { + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + + son_object son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::active); + + // put SON in maintenance mode + con.wallet_api_ptr->request_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is in request_maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::request_maintenance); + + // restore SON activity + con.wallet_api_ptr->cancel_request_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is active + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::active); + + // put SON in maintenance mode + con.wallet_api_ptr->request_son_maintenance(name, true); + BOOST_CHECK(generate_block()); + + // check SON is in request_maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::request_maintenance); + + // process maintenance + BOOST_CHECK(generate_maintenance_block()); + + // check SON is in maintenance + son_obj = con.wallet_api_ptr->get_son(name); + BOOST_CHECK(son_obj.status == son_status::in_maintenance); + + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON maintenance cli wallet tests end"); +} + +BOOST_AUTO_TEST_SUITE_END() + + + diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 3a3815857..b5ce1f559 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -50,6 +50,8 @@ #include #include +#include +#include #include #include "database_fixture.hpp" @@ -326,9 +328,10 @@ void database_fixture::verify_asset_supplies( const database& db ) uint64_t sweeps_vestings = 0; for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) sweeps_vestings += svbo.balance; - + total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; + total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget; for( const auto& item : total_debts ) { @@ -469,6 +472,23 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i generate_block(skip); } +bool database_fixture::generate_maintenance_block() { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~database::skip_fork_db; + auto maint_time = db.get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db.get_slot_at_time(maint_time); + db.generate_block(db.get_slot_time(slots_to_miss), + db.get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (std::exception& e) + { + return false; + } +} + account_create_operation database_fixture::make_account( const std::string& name /* = "nathan" */, public_key_type key /* = key_id_type() */ @@ -787,7 +807,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow witness_create_operation op; op.witness_account = owner.id; op.block_signing_key = signing_private_key.get_public_key(); - + secret_hash_type::encoder enc; fc::raw::pack(enc, signing_private_key); fc::raw::pack(enc, secret_hash_type()); @@ -1172,12 +1192,12 @@ int64_t database_fixture::get_balance( const account_object& account, const asse } int64_t database_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, - asset_id_type dividend_payout_asset_type) const + account_id_type dividend_holder_account_id, + asset_id_type dividend_payout_asset_type) const { - const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = + const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type(); - auto pending_payout_iter = + auto pending_payout_iter = pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id)); if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) return 0; @@ -1398,7 +1418,7 @@ void database_fixture::delete_sport(sport_id_type sport_id) sport_delete_op.sport_id = sport_id; process_operation_by_witnesses(sport_delete_op); } FC_CAPTURE_AND_RETHROW( (sport_id) ) } - + const event_group_object& database_fixture::create_event_group(internationalized_string_type name, sport_id_type sport_id) { try { event_group_create_operation event_group_create_op; @@ -1428,7 +1448,7 @@ void database_fixture::delete_event_group(event_group_id_type event_group_id) process_operation_by_witnesses(event_group_delete_op); } FC_CAPTURE_AND_RETHROW( (event_group_id) ) } - + void database_fixture::try_update_event_group(event_group_id_type event_group_id, fc::optional sport_id, fc::optional name, @@ -1468,7 +1488,7 @@ void database_fixture::update_event_impl(event_id_type event_id, fc::optional event_group_id, fc::optional name, fc::optional season, - fc::optional status, + fc::optional status, bool force) { try { event_update_operation event_update_op; @@ -1505,9 +1525,9 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type process_operation_by_witnesses(betting_market_rules_update_op); } FC_CAPTURE_AND_RETHROW( (name)(description) ) } -const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, +const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling) @@ -1577,7 +1597,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark bet_place_op.amount_to_bet = amount_to_bet; bet_place_op.backer_multiplier = backer_multiplier; bet_place_op.back_or_lay = back_or_lay; - + trx.operations.push_back(bet_place_op); trx.validate(); processed_transaction ptx = db.push_transaction(trx, ~0); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 200d1897a..a190b9c6a 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -199,6 +199,12 @@ struct database_fixture { */ void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); + /////////// + /// @brief Skip intermediate blocks, and generate a maintenance block + /// @returns true on success + /////////// + bool generate_maintenance_block(); + account_create_operation make_account( const std::string& name = "nathan", public_key_type = public_key_type() @@ -295,7 +301,7 @@ struct database_fixture { int64_t get_balance( account_id_type account, asset_id_type a )const; int64_t get_balance( const account_object& account, const asset_object& a )const; int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, + account_id_type dividend_holder_account_id, asset_id_type dividend_payout_asset_type) const; vector< operation_history_object > get_operation_history( account_id_type account_id )const; void process_operation_by_witnesses(operation op); @@ -321,7 +327,7 @@ struct database_fixture { fc::optional season, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, (required (event_id, (event_id_type))) (optional (event_group_id, (fc::optional), fc::optional()) (name, (fc::optional), fc::optional()) @@ -336,9 +342,9 @@ struct database_fixture { void update_betting_market_rules(betting_market_rules_id_type rules_id, fc::optional name, fc::optional description); - const betting_market_group_object& create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, + const betting_market_group_object& create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling); @@ -347,7 +353,7 @@ struct database_fixture { fc::optional rules_id, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, (required (betting_market_group_id, (betting_market_group_id_type))) (optional (description, (fc::optional), fc::optional()) (rules_id, (fc::optional), fc::optional()) diff --git a/tests/elasticsearch/main.cpp b/tests/elasticsearch/main.cpp index 28d3522ca..c948616f2 100644 --- a/tests/elasticsearch/main.cpp +++ b/tests/elasticsearch/main.cpp @@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_history_api) { create_bitasset("USD", account_id_type()); // create op 0 const account_object& dan = create_account("dan"); // create op 1 create_bitasset("CNY", dan.id); // create op 2 - create_bitasset("BTC", account_id_type()); // create op 3 + create_bitasset("BTCTEST", account_id_type()); // create op 3 create_bitasset("XMR", dan.id); // create op 4 create_bitasset("EUR", account_id_type()); // create op 5 create_bitasset("OIL", dan.id); // create op 6 diff --git a/tests/peerplays_sidechain/bitcoin_address_tests.cpp b/tests/peerplays_sidechain/bitcoin_address_tests.cpp new file mode 100644 index 000000000..a6909e249 --- /dev/null +++ b/tests/peerplays_sidechain/bitcoin_address_tests.cpp @@ -0,0 +1,316 @@ +#include +#include + +using namespace graphene::peerplays_sidechain::bitcoin; + +BOOST_AUTO_TEST_SUITE(bitcoin_address_tests) + +fc::ecc::public_key_data create_public_key_data(const std::vector &public_key) { + FC_ASSERT(public_key.size() == 33); + fc::ecc::public_key_data key; + for (size_t i = 0; i < 33; i++) { + key.at(i) = public_key[i]; + } + return key; +} + +BOOST_AUTO_TEST_CASE(addresses_type_test) { + // public_key + std::string compressed("03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22"); + BOOST_CHECK(bitcoin_address(compressed).get_type() == payment_type::P2PK); + + std::string uncompressed("04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904"); + BOOST_CHECK(bitcoin_address(uncompressed).get_type() == payment_type::NULLDATA); + + // segwit_address + std::string p2wpkh_mainnet("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"); + BOOST_CHECK(bitcoin_address(p2wpkh_mainnet).get_type() == payment_type::P2WPKH); + + std::string p2wpkh_testnet("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"); + BOOST_CHECK(bitcoin_address(p2wpkh_testnet).get_type() == payment_type::P2WPKH); + + std::string p2wsh("bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9"); + BOOST_CHECK(bitcoin_address(p2wsh).get_type() == payment_type::P2WSH); + + // base58 + std::string p2pkh_mainnet("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); + BOOST_CHECK(bitcoin_address(p2pkh_mainnet).get_type() == payment_type::P2PKH); + + std::string p2pkh_testnet("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"); + BOOST_CHECK(bitcoin_address(p2pkh_testnet).get_type() == payment_type::P2PKH); + + std::string p2sh_mainnet("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"); + BOOST_CHECK(bitcoin_address(p2sh_mainnet).get_type() == payment_type::P2SH); + + std::string p2sh_testnet("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc"); + BOOST_CHECK(bitcoin_address(p2sh_testnet).get_type() == payment_type::P2SH); + + std::string p2sh_regtest1("2NAL3YhMF4VcbRQdectN8XPMJipvATGefTZ"); + BOOST_CHECK(bitcoin_address(p2sh_regtest1).get_type() == payment_type::P2SH); +} + +BOOST_AUTO_TEST_CASE(addresses_raw_test) { + // public_key + std::string compressed("03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22"); + bytes standard_compressed(parse_hex(compressed)); + BOOST_CHECK(bitcoin_address(compressed).get_raw_address() == standard_compressed); + + std::string uncompressed("04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904"); + BOOST_CHECK(bitcoin_address(uncompressed).get_raw_address() == bytes()); + + // segwit_address + std::string p2wpkh_mainnet("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"); + bytes standard_p2wpkh_mainnet(parse_hex("751e76e8199196d454941c45d1b3a323f1433bd6")); + BOOST_CHECK(bitcoin_address(p2wpkh_mainnet).get_raw_address() == standard_p2wpkh_mainnet); + + std::string p2wpkh_testnet("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"); + bytes standard_p2wpkh_testnet(parse_hex("751e76e8199196d454941c45d1b3a323f1433bd6")); + BOOST_CHECK(bitcoin_address(p2wpkh_testnet).get_raw_address() == standard_p2wpkh_testnet); + + std::string p2wsh("bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9"); + bytes standard_p2wsh(parse_hex("c7a1f1a4d6b4c1802a59631966a18359de779e8a6a65973735a3ccdfdabc407d")); + BOOST_CHECK(bitcoin_address(p2wsh).get_raw_address() == standard_p2wsh); + + // base58 + std::string p2pkh_mainnet("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); + bytes standard_p2pkh_mainnet(parse_hex("47376c6f537d62177a2c41c4ca9b45829ab99083")); + BOOST_CHECK(bitcoin_address(p2pkh_mainnet).get_raw_address() == standard_p2pkh_mainnet); + + std::string p2pkh_testnet("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"); + bytes standard_p2pkh_testnet(parse_hex("243f1394f44554f4ce3fd68649c19adc483ce924")); + BOOST_CHECK(bitcoin_address(p2pkh_testnet).get_raw_address() == standard_p2pkh_testnet); + + std::string p2sh_mainnet("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"); + bytes standard_p2sh_mainnet(parse_hex("8f55563b9a19f321c211e9b9f38cdf686ea07845")); + BOOST_CHECK(bitcoin_address(p2sh_mainnet).get_raw_address() == standard_p2sh_mainnet); + + std::string p2sh_testnet("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc"); + bytes standard_p2sh_testnet(parse_hex("4e9f39ca4688ff102128ea4ccda34105324305b0")); + BOOST_CHECK(bitcoin_address(p2sh_testnet).get_raw_address() == standard_p2sh_testnet); +} + +BOOST_AUTO_TEST_CASE(create_multisig_address_test) { + + std::vector public_key1 = parse_hex("03db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010"); + std::vector public_key2 = parse_hex("0320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b4417983"); + std::vector public_key3 = parse_hex("033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f"); + + std::vector address = parse_hex("a91460cb986f0926e7c4ca1984ca9f56767da2af031e87"); + std::vector redeem_script = parse_hex("522103db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010210320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b441798321033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f53ae"); + + fc::ecc::public_key_data key1 = create_public_key_data(public_key1); + fc::ecc::public_key_data key2 = create_public_key_data(public_key2); + fc::ecc::public_key_data key3 = create_public_key_data(public_key3); + + btc_multisig_address cma(2, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}}); + + BOOST_CHECK(address == cma.raw_address); + BOOST_CHECK(redeem_script == cma.redeem_script); +} + +BOOST_AUTO_TEST_CASE(create_segwit_address_test) { + // https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa + std::vector public_key1 = parse_hex("03b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb"); + std::vector public_key2 = parse_hex("03dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba6"); + std::vector public_key3 = parse_hex("033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be1"); + + std::vector witness_script = parse_hex("0020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1"); + + fc::ecc::public_key_data key1 = create_public_key_data(public_key1); + fc::ecc::public_key_data key2 = create_public_key_data(public_key2); + fc::ecc::public_key_data key3 = create_public_key_data(public_key3); + + btc_multisig_segwit_address address(2, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}}); + BOOST_CHECK(address.get_witness_script() == witness_script); + BOOST_CHECK(address.get_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk"); +} + +BOOST_AUTO_TEST_CASE(create_segwit_address_10_of_14_test) { + std::vector public_key1 = parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275"); + std::vector public_key2 = parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4"); + std::vector public_key3 = parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61"); + std::vector public_key4 = parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866"); + std::vector public_key5 = parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666"); + std::vector public_key6 = parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf"); + std::vector public_key7 = parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f"); + std::vector public_key8 = parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84"); + std::vector public_key9 = parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf"); + std::vector public_key10 = parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5"); + std::vector public_key11 = parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da"); + std::vector public_key12 = parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1"); + std::vector public_key13 = parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8"); + std::vector public_key14 = parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8"); + + std::vector witness_script = parse_hex("0020b70a52b55974d3c8c1a2055bf23e2d6421942135c7be1f786ad8cbce2f532cef"); + std::vector redeem_script = parse_hex("5a2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae85eae"); + + fc::ecc::public_key_data key1 = create_public_key_data(public_key1); + fc::ecc::public_key_data key2 = create_public_key_data(public_key2); + fc::ecc::public_key_data key3 = create_public_key_data(public_key3); + fc::ecc::public_key_data key4 = create_public_key_data(public_key4); + fc::ecc::public_key_data key5 = create_public_key_data(public_key5); + fc::ecc::public_key_data key6 = create_public_key_data(public_key6); + fc::ecc::public_key_data key7 = create_public_key_data(public_key7); + fc::ecc::public_key_data key8 = create_public_key_data(public_key8); + fc::ecc::public_key_data key9 = create_public_key_data(public_key9); + fc::ecc::public_key_data key10 = create_public_key_data(public_key10); + fc::ecc::public_key_data key11 = create_public_key_data(public_key11); + fc::ecc::public_key_data key12 = create_public_key_data(public_key12); + fc::ecc::public_key_data key13 = create_public_key_data(public_key13); + fc::ecc::public_key_data key14 = create_public_key_data(public_key14); + + btc_multisig_segwit_address address(10, { + {son_id_type(1), public_key_type(key1)}, + {son_id_type(2), public_key_type(key2)}, + {son_id_type(3), public_key_type(key3)}, + {son_id_type(4), public_key_type(key4)}, + {son_id_type(5), public_key_type(key5)}, + {son_id_type(6), public_key_type(key6)}, + {son_id_type(7), public_key_type(key7)}, + {son_id_type(8), public_key_type(key8)}, + {son_id_type(9), public_key_type(key9)}, + {son_id_type(10), public_key_type(key10)}, + {son_id_type(11), public_key_type(key11)}, + {son_id_type(12), public_key_type(key12)}, + {son_id_type(13), public_key_type(key13)}, + {son_id_type(14), public_key_type(key14)}, + }); + BOOST_CHECK(address.get_witness_script() == witness_script); + BOOST_CHECK(address.get_redeem_script() == redeem_script); + BOOST_CHECK(address.get_address() == "2MwhYhBrZeb6mTf1kaWcYfLBCCQoMqQybFZ"); +} + +BOOST_AUTO_TEST_CASE(create_segwit_address_11_of_15_test) { + std::vector public_key1 = parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275"); + std::vector public_key2 = parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4"); + std::vector public_key3 = parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61"); + std::vector public_key4 = parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866"); + std::vector public_key5 = parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666"); + std::vector public_key6 = parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf"); + std::vector public_key7 = parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f"); + std::vector public_key8 = parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84"); + std::vector public_key9 = parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf"); + std::vector public_key10 = parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5"); + std::vector public_key11 = parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da"); + std::vector public_key12 = parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1"); + std::vector public_key13 = parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8"); + std::vector public_key14 = parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8"); + std::vector public_key15 = parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73"); + + fc::ecc::public_key_data key1 = create_public_key_data(public_key1); + fc::ecc::public_key_data key2 = create_public_key_data(public_key2); + fc::ecc::public_key_data key3 = create_public_key_data(public_key3); + fc::ecc::public_key_data key4 = create_public_key_data(public_key4); + fc::ecc::public_key_data key5 = create_public_key_data(public_key5); + fc::ecc::public_key_data key6 = create_public_key_data(public_key6); + fc::ecc::public_key_data key7 = create_public_key_data(public_key7); + fc::ecc::public_key_data key8 = create_public_key_data(public_key8); + fc::ecc::public_key_data key9 = create_public_key_data(public_key9); + fc::ecc::public_key_data key10 = create_public_key_data(public_key10); + fc::ecc::public_key_data key11 = create_public_key_data(public_key11); + fc::ecc::public_key_data key12 = create_public_key_data(public_key12); + fc::ecc::public_key_data key13 = create_public_key_data(public_key13); + fc::ecc::public_key_data key14 = create_public_key_data(public_key14); + fc::ecc::public_key_data key15 = create_public_key_data(public_key15); + + std::vector witness_script = parse_hex("00205fcdce3c62b477553b8dc957a935adda8305cbd47a408f07d2cb867d588279eb"); + std::vector redeem_script = parse_hex("5b2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae"); + + btc_multisig_segwit_address address(11, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}, {son_id_type(4), public_key_type(key4)}, {son_id_type(5), public_key_type(key5)}, {son_id_type(6), public_key_type(key6)}, {son_id_type(7), public_key_type(key7)}, {son_id_type(8), public_key_type(key8)}, {son_id_type(9), public_key_type(key9)}, {son_id_type(10), public_key_type(key10)}, {son_id_type(11), public_key_type(key11)}, {son_id_type(12), public_key_type(key12)}, {son_id_type(13), public_key_type(key13)}, {son_id_type(14), public_key_type(key14)}, {son_id_type(15), public_key_type(key15)}}); + + BOOST_CHECK(address.get_witness_script() == witness_script); + BOOST_CHECK(address.get_redeem_script() == redeem_script); + BOOST_CHECK(address.get_address() == "2NAL3YhMF4VcbRQdectN8XPMJipvATGefTZ"); +} + +BOOST_AUTO_TEST_CASE(btc_weighted_multisig_address_test) { + std::vector priv_old; + for (uint32_t i = 0; i < 15; ++i) { + const char *seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_old.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_old; + for (auto &key : priv_old) + pub_old.push_back(key.get_public_key()); + // key weights + std::vector> weights; + for (uint16_t i = 0; i < 15; ++i) + weights.push_back(std::make_pair(pub_old[i], i + 1)); + + btc_weighted_multisig_address addr(weights, btc_weighted_multisig_address::network::testnet); + BOOST_CHECK(addr.get_address() == "tb1qj4dwcpsqxhszc6ee5wwndqxe04j4vzs0qksrzgscfarzduu6th8qlyqaqp"); +} + +BOOST_AUTO_TEST_CASE(create_1_or_2_of_15_segwit_address_test) { + auto public_key1 = fc::ecc::public_key(create_public_key_data(parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275"))); + auto public_key2 = fc::ecc::public_key(create_public_key_data(parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4"))); + auto public_key3 = fc::ecc::public_key(create_public_key_data(parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61"))); + auto public_key4 = fc::ecc::public_key(create_public_key_data(parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866"))); + auto public_key5 = fc::ecc::public_key(create_public_key_data(parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666"))); + auto public_key6 = fc::ecc::public_key(create_public_key_data(parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf"))); + auto public_key7 = fc::ecc::public_key(create_public_key_data(parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f"))); + auto public_key8 = fc::ecc::public_key(create_public_key_data(parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84"))); + auto public_key9 = fc::ecc::public_key(create_public_key_data(parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf"))); + auto public_key10 = fc::ecc::public_key(create_public_key_data(parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5"))); + auto public_key11 = fc::ecc::public_key(create_public_key_data(parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da"))); + auto public_key12 = fc::ecc::public_key(create_public_key_data(parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1"))); + auto public_key13 = fc::ecc::public_key(create_public_key_data(parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8"))); + auto public_key14 = fc::ecc::public_key(create_public_key_data(parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8"))); + auto public_key15 = fc::ecc::public_key(create_public_key_data(parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73"))); + + auto user_key = fc::ecc::public_key(create_public_key_data(parse_hex("0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd"))); + + btc_one_or_m_of_n_multisig_address address(user_key, 2, {public_key1, public_key2, public_key3, public_key4, public_key5, public_key6, public_key7, public_key8, public_key9, public_key10, public_key11, public_key12, public_key13, public_key14, public_key15}); + + std::vector redeem_script = parse_hex("63210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac67522103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae68"); + + BOOST_CHECK(address.get_address() == "bcrt1qp8vplzrn7alzpjq8cw4ynd6xqzassmefrh48dzsj0krvkq85dywq9txkqr"); + BOOST_CHECK(address.get_redeem_script() == redeem_script); +} + +BOOST_AUTO_TEST_CASE(create_1_or_multiweighted_seggwit_address_test) { + auto public_key1 = fc::ecc::public_key(create_public_key_data(parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275"))); + auto public_key2 = fc::ecc::public_key(create_public_key_data(parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4"))); + auto public_key3 = fc::ecc::public_key(create_public_key_data(parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61"))); + auto public_key4 = fc::ecc::public_key(create_public_key_data(parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866"))); + auto public_key5 = fc::ecc::public_key(create_public_key_data(parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666"))); + auto public_key6 = fc::ecc::public_key(create_public_key_data(parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf"))); + auto public_key7 = fc::ecc::public_key(create_public_key_data(parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f"))); + auto public_key8 = fc::ecc::public_key(create_public_key_data(parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84"))); + auto public_key9 = fc::ecc::public_key(create_public_key_data(parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf"))); + auto public_key10 = fc::ecc::public_key(create_public_key_data(parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5"))); + auto public_key11 = fc::ecc::public_key(create_public_key_data(parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da"))); + auto public_key12 = fc::ecc::public_key(create_public_key_data(parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1"))); + auto public_key13 = fc::ecc::public_key(create_public_key_data(parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8"))); + auto public_key14 = fc::ecc::public_key(create_public_key_data(parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8"))); + auto public_key15 = fc::ecc::public_key(create_public_key_data(parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73"))); + + auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex("0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd"))); + + // key weights + std::vector> weights; + weights.push_back(std::make_pair(public_key1, 1)); + weights.push_back(std::make_pair(public_key2, 1)); + weights.push_back(std::make_pair(public_key3, 1)); + weights.push_back(std::make_pair(public_key4, 1)); + weights.push_back(std::make_pair(public_key5, 1)); + weights.push_back(std::make_pair(public_key6, 1)); + weights.push_back(std::make_pair(public_key7, 1)); + weights.push_back(std::make_pair(public_key8, 1)); + weights.push_back(std::make_pair(public_key9, 1)); + weights.push_back(std::make_pair(public_key10, 1)); + weights.push_back(std::make_pair(public_key11, 1)); + weights.push_back(std::make_pair(public_key12, 1)); + weights.push_back(std::make_pair(public_key13, 1)); + weights.push_back(std::make_pair(public_key14, 1)); + weights.push_back(std::make_pair(public_key15, 1)); + + btc_one_or_weighted_multisig_address addr(user_pub_key, weights); + auto redeem_script = parse_hex("210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac635167007c2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275ac635193687c2102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4ac635193687c21025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61ac635193687c210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866ac635193687c21037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666ac635193687c2102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccfac635193687c210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7fac635193687c210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84ac635193687c21023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccfac635193687c210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5ac635193687c21024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8daac635193687c2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1ac635193687c2102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8ac635193687c210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8ac635193687c2102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73ac635193685ba268"); + + BOOST_CHECK(addr.get_address() == "bcrt1qz2cykw980zns8uxpt7dsn4ku7ce24cq3z8mna0tttzhw2qcr4y0qxmgej2"); + BOOST_CHECK(addr.get_redeem_script() == redeem_script); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/peerplays_sidechain/bitcoin_sign_tests.cpp b/tests/peerplays_sidechain/bitcoin_sign_tests.cpp new file mode 100644 index 000000000..82a121c90 --- /dev/null +++ b/tests/peerplays_sidechain/bitcoin_sign_tests.cpp @@ -0,0 +1,449 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace graphene::peerplays_sidechain::bitcoin; +using namespace fc::ecc; + +BOOST_AUTO_TEST_SUITE(bitcoin_sign_tests) + +const secp256k1_context_t *btc_context() { + static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; +} + +inline bytes get_privkey_bytes(const std::string &privkey_base58) { + const auto privkey = fc::from_base58(privkey_base58); + // Remove version and hash + return bytes(privkey.cbegin() + 1, privkey.cbegin() + 1 + 32); +} + +BOOST_AUTO_TEST_CASE(btc_tx_witness_signature_test) { + bitcoin_transaction tx; + tx.nVersion = 1; + tx.vin.resize(1); + tx.vout.resize(1); + tx.nLockTime = 0; + + tx.vin[0].prevout.hash = fc::sha256("0a510f49749aaaa2638048132eafea959dd8e47e79332dbcb2a14189870e3145"); + tx.vin[0].prevout.n = 1; + tx.vin[0].scriptSig = parse_hex("220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1"); + tx.vin[0].nSequence = 4294967295; + + tx.vout[0].value = 20000000; + tx.vout[0].scriptPubKey = parse_hex("76a9143ebc40e411ed3c76f86711507ab952300890397288ac"); + + const auto privkey_1 = get_privkey_bytes("cQPUeypiYqp8J8Y8dGXUhvWGPHXTYYs3haryjdquwvMLAabXAnzF"); + const auto privkey_2 = get_privkey_bytes("cTG9AXoZjPbUmU2rx4ojeUBm3P88q3ZvRR6YWTUeVJHyke5KbVPM"); + const auto redeemScript = parse_hex("522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae"); + uint64_t amount = 20021300; + int32_t hash_type = 1; + + tx.vin[0].scriptWitness.push_back(sign_witness_transaction_part(tx, {redeemScript}, {amount}, privkey_1, btc_context(), hash_type)[0]); + tx.vin[0].scriptWitness.push_back(sign_witness_transaction_part(tx, {redeemScript}, {amount}, privkey_2, btc_context(), hash_type)[0]); + sign_witness_transaction_finalize(tx, {redeemScript}); + + BOOST_CHECK(fc::to_hex(pack(tx)) == "0100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000"); +} + +BOOST_AUTO_TEST_CASE(verify_sig_test) { + std::string hash("df074d23cbedea48308aa2161c5b0da3893cc898e143c285ae4d5d787b366f10"); + bytes vec_hash(parse_hex(hash)); + + std::string key1("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"); + bytes vec_key1(parse_hex(key1)); + + std::string sig("3044022051641c36fc6bc1e7ddd0022259c3f3a8dce0ac7fa4538c8b303c49e14b216b5302204e64c88a7f0279d902ccb338ffd42941ccdbbd7ddf8ba17d1ebf693f1f0b3ed901"); + bytes vec_sig(parse_hex(sig)); + + BOOST_CHECK(verify_sig(vec_sig, vec_key1, vec_hash, btc_context())); + + std::string key2("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435ca"); + bytes vec_key2(parse_hex(key2)); + + BOOST_CHECK(!verify_sig(vec_sig, vec_key2, vec_hash, btc_context())); +} + +BOOST_AUTO_TEST_CASE(get_pubkey_from_redeemScript_test) { + std::string script("5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf5bae"); + + std::vector keys = {parse_hex("025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf"), + parse_hex("02c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf")}; + + std::vector keys_from_script = get_pubkey_from_redeemScript(parse_hex(script)); + + BOOST_CHECK(keys.size() == keys_from_script.size()); + for (size_t i = 0; i < keys.size(); i++) { + BOOST_CHECK(keys[i] == keys_from_script[i]); + } + + std::string script2("5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e95bae"); + + std::vector keys_from_script2 = get_pubkey_from_redeemScript(parse_hex(script2)); + BOOST_CHECK(keys_from_script2.size() == 1); + BOOST_CHECK(keys_from_script2[0] == parse_hex("025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9")); +} + +BOOST_AUTO_TEST_CASE(sort_sig_test) { + bitcoin_transaction trx(fc::json::from_string("{\"nVersion\":1,\"vin\":[{\"prevout\":{\"hash\":\"e937fd2942f0f14dd46a122e138d00cfabd93572b4876da77ab57c2a76ee73af\",\"n\":0},\"scriptSig\":\"\",\"nSequence\":4294967295,\"scriptWitness\":[\"30440220772bd2e8afe8c39d28e0c08ea81281d14239033cb93c92edf52250da542fa7c2022059ca93f98b194c9bcc8017ba3de19893fad06c8ea74430dc5fbe7eb81844598d01\",\"30440220098d274e3de29da36577f88ff851d030051b417a0309b61ba1c90d1750eee432022013946f9434893e4d9cbc23b68ec8243ed935823948d701f007edb6ccc46ac29801\",\"3045022100c2cb782558909109d5971ff29e23011b8eb4cd99e86030ac81a15b3312d897530220690080ce113caf84373ac04cc16e7f62ee5eeaf47b26d8017d2509c5d5510c0201\",\"3045022100888c5c9b5d2a4f3a17713cf665c2c65f3b9e954c2bcf3506deb9e410a33f6ca50220604f2a37e3650aded4c5811826098f8fa18af62fe07273c138440c16bdae074401\",\"3045022100cd4e8db4154b100077a30063654c4fc8473c3856064264293b931968fff9cee9022028ab2c8694218853756cca3e5ef5857694037e56e559a0004b292c7122ab355401\"]},{\"prevout\":{\"hash\":\"ae34ad50ab112e6cc51e6e3a87c48798b67255f8c8a8af9d427cbf55207ecfd1\",\"n\":0},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"3045022100b93623da6ed9a3f75082dbd77fab5492e64ae96ad4cdbb70f5a9ff1b2b30aa2602205df026319f3f21ba6f69f0be2469155c62dcf54ddfaf5f7d489d969b8364a3e401\",\"3044022010a60381cdb91d1f45579cc1d06df44b57c5af98b475089c8b349ad96a9d84fd02200840cff73d4053521dc4e7b210d20114ca82926ae96eb74633284f03f9d9861c01\",\"304402205639d8b13a6d912a3fd086abd34eb7455320aeb6b7ff148452a469f90fe636c80220035aad331677b67590845a5c6e9f8a6805d7add98cbab7047362a91717934e0001\",\"304402202945a632fe13b14099c80eb29fe6144597ca33b6fe10995a4b8756725149b5d902202b2e6a2b7bb39c7441877feaa1be68d144859ac755099704bd49eac41e12c92e01\",\"304402200e4fd2d3001736fbdabc65d50a3a04b6f99a80dc7a50b7257d65d7ced844c2320220613d8704833c50c445f56769b27067ca68299f8a462e2b62fb9e55d7c3e7046701\"]},{\"prevout\":{\"hash\":\"e04ee70b6aa2180caa32aaa4ff00c80b62e5572c369e05986d3a0e0b6d9d7455\",\"n\":0},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"30440220762212bfd15454036502ecd635314f7f81be982ea16dddf892693815745b32c7022069846f5b22b0246737396834123439556c9f8cd640006ad1ef8c70d86ca70a3e01\",\"30450221009f1c1053f45450a9e20c7735b645eb3825587ecd9dcb39a0d6de35926dbb252802204bad14928faacca9481d69960d5add5acbf7072e5230a146a7ecb6d9193b7d5001\",\"3045022100f32581419b4b46b3aa3bab0ad80202ba559fa1b086b6b02d003a2aac19782d6b02204e3132c43a12411e52f0d8b3714cb77d82136752a1e939af87d865ccda75b0ec01\",\"3045022100e145ab07653d0b2d472ebc393b5e82eee725c65573dfb458c36c7717aba5994002201cd00d40dca3120db7b38239da801765f042e248785c8d44ce4a0b8b1d57b36901\",\"3045022100f7f48205bdbb5e1690e635bf02205e4790c57aadb3f65adabd4890eb4285cfd5022056a0013f35f59c73dc2048697e0330a439e7b35804b655dbda938bdfc77ced9301\"]},{\"prevout\":{\"hash\":\"6bc22ed725ba7c164df3a878113a11e4fbc3d1bbffee0083e75cb14e7bb5bd38\",\"n\":1},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"3045022100c8a830255c4ea9ca205126701fc435d39993eca2d7024817958beea76ad3785102201cb27c7613031a4f55bc3c43683aa57f04a4f73291ad9c1076c5281bc49dc4d101\",\"304402202c1bcbd436f95e42364122f9f552466122597050962524850434bfeb0b1a721e02200f5f7cfc4d7c43c550a59918d43ee52e76e04c8da381303558f4fc83cc64e19201\",\"304402201669a5580624132b2f1e8d2a51831816846c5f93505623dc03ea6a9f01f023ed022054c69ae28483cd40ac144b7d4af4ff29292813cda425373eabd8d14624c61aae01\",\"3045022100f1b787c0466e88bbc663df7f5584dfa68416c106ab806e0b9f959c6f51b7221b022042633dfc95cde470690a52d5cc468e2f9a745fc1db2fee692b8f31cfce28c0cb01\",\"304402206468ea767ad5aa2fbe837c29ae2fee4f87063d25b9e97fc6f7f679a036a892bf022055e78030476a78d8fc9177bf2e64ccece65005493a8fc6bd1352741153e7eea601\"]}],\"vout\":[{\"value\":\"9590365272\",\"scriptPubKey\":\"00206a19177b8e4d76408c574118681f204c1a7065040636d5288af41f67c25a85f0\"},{\"value\":240000,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":240000,\"scriptPubKey\":\"2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471bac\"},{\"value\":240000,\"scriptPubKey\":\"2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c80ac\"},{\"value\":240000,\"scriptPubKey\":\"21021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d99390091ac\"},{\"value\":240000,\"scriptPubKey\":\"2103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b0ac\"},{\"value\":240000,\"scriptPubKey\":\"21028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfaac\"},{\"value\":240000,\"scriptPubKey\":\"21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcdeac\"},{\"value\":240000,\"scriptPubKey\":\"2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de51ac\"},{\"value\":240000,\"scriptPubKey\":\"2103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6ac\"},{\"value\":240000,\"scriptPubKey\":\"210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d29ac\"},{\"value\":240000,\"scriptPubKey\":\"2103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232fac\"}],\"nLockTime\":0}").as(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector scripts(fc::json::from_string("[\"552102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d292103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232f5bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\"]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector amounts(fc::json::from_string("[5993981520,1200000000,1200000000,1200000000]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector> results(fc::json::from_string("[[\"30440220098d274e3de29da36577f88ff851d030051b417a0309b61ba1c90d1750eee432022013946f9434893e4d9cbc23b68ec8243ed935823948d701f007edb6ccc46ac29801\",\"3045022100c2cb782558909109d5971ff29e23011b8eb4cd99e86030ac81a15b3312d897530220690080ce113caf84373ac04cc16e7f62ee5eeaf47b26d8017d2509c5d5510c0201\",\"3045022100888c5c9b5d2a4f3a17713cf665c2c65f3b9e954c2bcf3506deb9e410a33f6ca50220604f2a37e3650aded4c5811826098f8fa18af62fe07273c138440c16bdae074401\",\"3045022100cd4e8db4154b100077a30063654c4fc8473c3856064264293b931968fff9cee9022028ab2c8694218853756cca3e5ef5857694037e56e559a0004b292c7122ab355401\",\"30440220772bd2e8afe8c39d28e0c08ea81281d14239033cb93c92edf52250da542fa7c2022059ca93f98b194c9bcc8017ba3de19893fad06c8ea74430dc5fbe7eb81844598d01\"],[\"3044022010a60381cdb91d1f45579cc1d06df44b57c5af98b475089c8b349ad96a9d84fd02200840cff73d4053521dc4e7b210d20114ca82926ae96eb74633284f03f9d9861c01\",\"304402205639d8b13a6d912a3fd086abd34eb7455320aeb6b7ff148452a469f90fe636c80220035aad331677b67590845a5c6e9f8a6805d7add98cbab7047362a91717934e0001\",\"304402202945a632fe13b14099c80eb29fe6144597ca33b6fe10995a4b8756725149b5d902202b2e6a2b7bb39c7441877feaa1be68d144859ac755099704bd49eac41e12c92e01\",\"304402200e4fd2d3001736fbdabc65d50a3a04b6f99a80dc7a50b7257d65d7ced844c2320220613d8704833c50c445f56769b27067ca68299f8a462e2b62fb9e55d7c3e7046701\",\"3045022100b93623da6ed9a3f75082dbd77fab5492e64ae96ad4cdbb70f5a9ff1b2b30aa2602205df026319f3f21ba6f69f0be2469155c62dcf54ddfaf5f7d489d969b8364a3e401\"],[\"30450221009f1c1053f45450a9e20c7735b645eb3825587ecd9dcb39a0d6de35926dbb252802204bad14928faacca9481d69960d5add5acbf7072e5230a146a7ecb6d9193b7d5001\",\"3045022100f32581419b4b46b3aa3bab0ad80202ba559fa1b086b6b02d003a2aac19782d6b02204e3132c43a12411e52f0d8b3714cb77d82136752a1e939af87d865ccda75b0ec01\",\"3045022100e145ab07653d0b2d472ebc393b5e82eee725c65573dfb458c36c7717aba5994002201cd00d40dca3120db7b38239da801765f042e248785c8d44ce4a0b8b1d57b36901\",\"3045022100f7f48205bdbb5e1690e635bf02205e4790c57aadb3f65adabd4890eb4285cfd5022056a0013f35f59c73dc2048697e0330a439e7b35804b655dbda938bdfc77ced9301\",\"30440220762212bfd15454036502ecd635314f7f81be982ea16dddf892693815745b32c7022069846f5b22b0246737396834123439556c9f8cd640006ad1ef8c70d86ca70a3e01\"],[\"304402202c1bcbd436f95e42364122f9f552466122597050962524850434bfeb0b1a721e02200f5f7cfc4d7c43c550a59918d43ee52e76e04c8da381303558f4fc83cc64e19201\",\"304402201669a5580624132b2f1e8d2a51831816846c5f93505623dc03ea6a9f01f023ed022054c69ae28483cd40ac144b7d4af4ff29292813cda425373eabd8d14624c61aae01\",\"3045022100f1b787c0466e88bbc663df7f5584dfa68416c106ab806e0b9f959c6f51b7221b022042633dfc95cde470690a52d5cc468e2f9a745fc1db2fee692b8f31cfce28c0cb01\",\"304402206468ea767ad5aa2fbe837c29ae2fee4f87063d25b9e97fc6f7f679a036a892bf022055e78030476a78d8fc9177bf2e64ccece65005493a8fc6bd1352741153e7eea601\",\"3045022100c8a830255c4ea9ca205126701fc435d39993eca2d7024817958beea76ad3785102201cb27c7613031a4f55bc3c43683aa57f04a4f73291ad9c1076c5281bc49dc4d101\"]]").as>>(GRAPHENE_MAX_NESTED_OBJECTS)); + + auto new_stacks = sort_sigs(trx, scripts, amounts, btc_context()); + for (size_t i = 0; i < trx.vin.size(); i++) { + BOOST_CHECK(new_stacks[i] == results[i]); + } +} + +BOOST_AUTO_TEST_CASE(already_sorted_sigs_test) { + bitcoin_transaction trx(fc::json::from_string("{\"nVersion\":1,\"vin\":[{\"prevout\":{\"hash\":\"e937fd2942f0f14dd46a122e138d00cfabd93572b4876da77ab57c2a76ee73af\",\"n\":0},\"scriptSig\":\"\",\"nSequence\":4294967295,\"scriptWitness\":[\"30440220098d274e3de29da36577f88ff851d030051b417a0309b61ba1c90d1750eee432022013946f9434893e4d9cbc23b68ec8243ed935823948d701f007edb6ccc46ac29801\",\"3045022100c2cb782558909109d5971ff29e23011b8eb4cd99e86030ac81a15b3312d897530220690080ce113caf84373ac04cc16e7f62ee5eeaf47b26d8017d2509c5d5510c0201\",\"3045022100888c5c9b5d2a4f3a17713cf665c2c65f3b9e954c2bcf3506deb9e410a33f6ca50220604f2a37e3650aded4c5811826098f8fa18af62fe07273c138440c16bdae074401\",\"3045022100cd4e8db4154b100077a30063654c4fc8473c3856064264293b931968fff9cee9022028ab2c8694218853756cca3e5ef5857694037e56e559a0004b292c7122ab355401\",\"30440220772bd2e8afe8c39d28e0c08ea81281d14239033cb93c92edf52250da542fa7c2022059ca93f98b194c9bcc8017ba3de19893fad06c8ea74430dc5fbe7eb81844598d01\"]},{\"prevout\":{\"hash\":\"ae34ad50ab112e6cc51e6e3a87c48798b67255f8c8a8af9d427cbf55207ecfd1\",\"n\":0},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"3044022010a60381cdb91d1f45579cc1d06df44b57c5af98b475089c8b349ad96a9d84fd02200840cff73d4053521dc4e7b210d20114ca82926ae96eb74633284f03f9d9861c01\",\"304402205639d8b13a6d912a3fd086abd34eb7455320aeb6b7ff148452a469f90fe636c80220035aad331677b67590845a5c6e9f8a6805d7add98cbab7047362a91717934e0001\",\"304402202945a632fe13b14099c80eb29fe6144597ca33b6fe10995a4b8756725149b5d902202b2e6a2b7bb39c7441877feaa1be68d144859ac755099704bd49eac41e12c92e01\",\"304402200e4fd2d3001736fbdabc65d50a3a04b6f99a80dc7a50b7257d65d7ced844c2320220613d8704833c50c445f56769b27067ca68299f8a462e2b62fb9e55d7c3e7046701\",\"3045022100b93623da6ed9a3f75082dbd77fab5492e64ae96ad4cdbb70f5a9ff1b2b30aa2602205df026319f3f21ba6f69f0be2469155c62dcf54ddfaf5f7d489d969b8364a3e401\"]},{\"prevout\":{\"hash\":\"e04ee70b6aa2180caa32aaa4ff00c80b62e5572c369e05986d3a0e0b6d9d7455\",\"n\":0},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"30450221009f1c1053f45450a9e20c7735b645eb3825587ecd9dcb39a0d6de35926dbb252802204bad14928faacca9481d69960d5add5acbf7072e5230a146a7ecb6d9193b7d5001\",\"3045022100f32581419b4b46b3aa3bab0ad80202ba559fa1b086b6b02d003a2aac19782d6b02204e3132c43a12411e52f0d8b3714cb77d82136752a1e939af87d865ccda75b0ec01\",\"3045022100e145ab07653d0b2d472ebc393b5e82eee725c65573dfb458c36c7717aba5994002201cd00d40dca3120db7b38239da801765f042e248785c8d44ce4a0b8b1d57b36901\",\"3045022100f7f48205bdbb5e1690e635bf02205e4790c57aadb3f65adabd4890eb4285cfd5022056a0013f35f59c73dc2048697e0330a439e7b35804b655dbda938bdfc77ced9301\",\"30440220762212bfd15454036502ecd635314f7f81be982ea16dddf892693815745b32c7022069846f5b22b0246737396834123439556c9f8cd640006ad1ef8c70d86ca70a3e01\"]},{\"prevout\":{\"hash\":\"6bc22ed725ba7c164df3a878113a11e4fbc3d1bbffee0083e75cb14e7bb5bd38\",\"n\":1},\"scriptSig\":\"220020d85971e91d6e46473104e3f7e5eb67d885304a08dd17b3e1a0eeebe5a15f54a6\",\"nSequence\":4294967295,\"scriptWitness\":[\"304402202c1bcbd436f95e42364122f9f552466122597050962524850434bfeb0b1a721e02200f5f7cfc4d7c43c550a59918d43ee52e76e04c8da381303558f4fc83cc64e19201\",\"304402201669a5580624132b2f1e8d2a51831816846c5f93505623dc03ea6a9f01f023ed022054c69ae28483cd40ac144b7d4af4ff29292813cda425373eabd8d14624c61aae01\",\"3045022100f1b787c0466e88bbc663df7f5584dfa68416c106ab806e0b9f959c6f51b7221b022042633dfc95cde470690a52d5cc468e2f9a745fc1db2fee692b8f31cfce28c0cb01\",\"304402206468ea767ad5aa2fbe837c29ae2fee4f87063d25b9e97fc6f7f679a036a892bf022055e78030476a78d8fc9177bf2e64ccece65005493a8fc6bd1352741153e7eea601\",\"3045022100c8a830255c4ea9ca205126701fc435d39993eca2d7024817958beea76ad3785102201cb27c7613031a4f55bc3c43683aa57f04a4f73291ad9c1076c5281bc49dc4d101\"]}],\"vout\":[{\"value\":\"9590365272\",\"scriptPubKey\":\"00206a19177b8e4d76408c574118681f204c1a7065040636d5288af41f67c25a85f0\"},{\"value\":240000,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":240000,\"scriptPubKey\":\"2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471bac\"},{\"value\":240000,\"scriptPubKey\":\"2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c80ac\"},{\"value\":240000,\"scriptPubKey\":\"21021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d99390091ac\"},{\"value\":240000,\"scriptPubKey\":\"2103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b0ac\"},{\"value\":240000,\"scriptPubKey\":\"21028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfaac\"},{\"value\":240000,\"scriptPubKey\":\"21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcdeac\"},{\"value\":240000,\"scriptPubKey\":\"2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de51ac\"},{\"value\":240000,\"scriptPubKey\":\"2103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6ac\"},{\"value\":240000,\"scriptPubKey\":\"210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d29ac\"},{\"value\":240000,\"scriptPubKey\":\"2103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232fac\"}],\"nLockTime\":0}").as(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector scripts(fc::json::from_string("[\"552102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d292103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232f5bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102ec74848d166af51b430f6d130606896e1436688e935dd8407c3aa15c38d4471b2102cd19bf004e5d533de24bcc55d8573fe5fada438860512a9bbe37118733b34c8021021d2559f259df45f16287d8f55ab41c1c2fb7099a75cc99a3f250611d993900912103e4857a5da1e9483ba14644421489790120555baccb9cf130848e0261464bb7b021028ba26c831fb21084c9bbb7059f76debbf442822adc286ee43ea3092fd666bcfa21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\"]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector amounts(fc::json::from_string("[5993981520,1200000000,1200000000,1200000000]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector> results(fc::json::from_string("[[\"30440220098d274e3de29da36577f88ff851d030051b417a0309b61ba1c90d1750eee432022013946f9434893e4d9cbc23b68ec8243ed935823948d701f007edb6ccc46ac29801\",\"3045022100c2cb782558909109d5971ff29e23011b8eb4cd99e86030ac81a15b3312d897530220690080ce113caf84373ac04cc16e7f62ee5eeaf47b26d8017d2509c5d5510c0201\",\"3045022100888c5c9b5d2a4f3a17713cf665c2c65f3b9e954c2bcf3506deb9e410a33f6ca50220604f2a37e3650aded4c5811826098f8fa18af62fe07273c138440c16bdae074401\",\"3045022100cd4e8db4154b100077a30063654c4fc8473c3856064264293b931968fff9cee9022028ab2c8694218853756cca3e5ef5857694037e56e559a0004b292c7122ab355401\",\"30440220772bd2e8afe8c39d28e0c08ea81281d14239033cb93c92edf52250da542fa7c2022059ca93f98b194c9bcc8017ba3de19893fad06c8ea74430dc5fbe7eb81844598d01\"],[\"3044022010a60381cdb91d1f45579cc1d06df44b57c5af98b475089c8b349ad96a9d84fd02200840cff73d4053521dc4e7b210d20114ca82926ae96eb74633284f03f9d9861c01\",\"304402205639d8b13a6d912a3fd086abd34eb7455320aeb6b7ff148452a469f90fe636c80220035aad331677b67590845a5c6e9f8a6805d7add98cbab7047362a91717934e0001\",\"304402202945a632fe13b14099c80eb29fe6144597ca33b6fe10995a4b8756725149b5d902202b2e6a2b7bb39c7441877feaa1be68d144859ac755099704bd49eac41e12c92e01\",\"304402200e4fd2d3001736fbdabc65d50a3a04b6f99a80dc7a50b7257d65d7ced844c2320220613d8704833c50c445f56769b27067ca68299f8a462e2b62fb9e55d7c3e7046701\",\"3045022100b93623da6ed9a3f75082dbd77fab5492e64ae96ad4cdbb70f5a9ff1b2b30aa2602205df026319f3f21ba6f69f0be2469155c62dcf54ddfaf5f7d489d969b8364a3e401\"],[\"30450221009f1c1053f45450a9e20c7735b645eb3825587ecd9dcb39a0d6de35926dbb252802204bad14928faacca9481d69960d5add5acbf7072e5230a146a7ecb6d9193b7d5001\",\"3045022100f32581419b4b46b3aa3bab0ad80202ba559fa1b086b6b02d003a2aac19782d6b02204e3132c43a12411e52f0d8b3714cb77d82136752a1e939af87d865ccda75b0ec01\",\"3045022100e145ab07653d0b2d472ebc393b5e82eee725c65573dfb458c36c7717aba5994002201cd00d40dca3120db7b38239da801765f042e248785c8d44ce4a0b8b1d57b36901\",\"3045022100f7f48205bdbb5e1690e635bf02205e4790c57aadb3f65adabd4890eb4285cfd5022056a0013f35f59c73dc2048697e0330a439e7b35804b655dbda938bdfc77ced9301\",\"30440220762212bfd15454036502ecd635314f7f81be982ea16dddf892693815745b32c7022069846f5b22b0246737396834123439556c9f8cd640006ad1ef8c70d86ca70a3e01\"],[\"304402202c1bcbd436f95e42364122f9f552466122597050962524850434bfeb0b1a721e02200f5f7cfc4d7c43c550a59918d43ee52e76e04c8da381303558f4fc83cc64e19201\",\"304402201669a5580624132b2f1e8d2a51831816846c5f93505623dc03ea6a9f01f023ed022054c69ae28483cd40ac144b7d4af4ff29292813cda425373eabd8d14624c61aae01\",\"3045022100f1b787c0466e88bbc663df7f5584dfa68416c106ab806e0b9f959c6f51b7221b022042633dfc95cde470690a52d5cc468e2f9a745fc1db2fee692b8f31cfce28c0cb01\",\"304402206468ea767ad5aa2fbe837c29ae2fee4f87063d25b9e97fc6f7f679a036a892bf022055e78030476a78d8fc9177bf2e64ccece65005493a8fc6bd1352741153e7eea601\",\"3045022100c8a830255c4ea9ca205126701fc435d39993eca2d7024817958beea76ad3785102201cb27c7613031a4f55bc3c43683aa57f04a4f73291ad9c1076c5281bc49dc4d101\"]]").as>>(GRAPHENE_MAX_NESTED_OBJECTS)); + + auto new_stacks = sort_sigs(trx, scripts, amounts, btc_context()); + for (size_t i = 0; i < trx.vin.size(); i++) { + BOOST_CHECK(new_stacks[i] == results[i]); + } +} + +BOOST_AUTO_TEST_CASE(all_signatures_are_same_test) { + bitcoin_transaction trx(fc::json::from_string("{\"nVersion\":1,\"vin\":[{\"prevout\":{\"hash\":\"e35635fce1cbe1e347a5f8a3c5dccd79db9d43a6fb4b27991894a8bce1a70e03\",\"n\":0},\"scriptSig\":\"\",\"nSequence\":4294967295,\"scriptWitness\":[\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\"]},{\"prevout\":{\"hash\":\"1826f1ba0ed5034806cf1cb3eaa5dc9abf04a319048ae1e73e3df75dcc9f0bca\",\"n\":1},\"scriptSig\":\"2200203b9e077c0043e8f394a273baffc0aed01d10d8c894ad39810257d63be9a315e0\",\"nSequence\":4294967295,\"scriptWitness\":[\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\"]}],\"vout\":[{\"value\":1997997990,\"scriptPubKey\":\"0020a40e801531fdca0fb550013a9140aaaf8d9eb106c427258fbc86d0e0c52c432d\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"}],\"nLockTime\":0}").as(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector scripts(fc::json::from_string("[\"552102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf5bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf5bae\"]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector amounts(fc::json::from_string("[998998995,1000000000]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector> results(fc::json::from_string("[[\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\",\"304402205370c8999e097e4018b04fa3be9c27e2ff16f0c21ef363c35dfd45b4290bf0740220775506660ece404703801a3f5a13fe24c96821c7d7eb42448abe35a1035cd8c801\"],[\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\",\"3045022100ced739a6c04cf3c5e5bc760272bb6f41ecb3fa3671aa78ac1bc47629e39bb7ba02207a8693778d3b5a0c045fddc1ab23fcd971640460f150252b39993587151ff27d01\"]]").as>>(GRAPHENE_MAX_NESTED_OBJECTS)); + + auto new_stacks = sort_sigs(trx, scripts, amounts, btc_context()); + for (size_t i = 0; i < trx.vin.size(); i++) { + BOOST_CHECK(new_stacks[i] == results[i]); + } +} + +BOOST_AUTO_TEST_CASE(same_signatures_test) { + bitcoin_transaction trx(fc::json::from_string("{\"nVersion\":1,\"vin\":[{\"prevout\":{\"hash\":\"f8d70d29817a78e160c53dcffcf6a783008f1cc43c64f7efb49e4de3a23ef016\",\"n\":0},\"scriptSig\":\"\",\"nSequence\":4294967295,\"scriptWitness\":[\"30440220228c930388a0420aa9a17acdf414763bec0f57f92ecf8db51f02e3f8d82428aa0220417f2d0fbc5fd00d5d158a2e7fe7188857119b8d16d11f82f513594a28dbcbfa01\",\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\",\"3045022100d95008906e848a8165fbc0d3ed6d11643bc4814d4f8ae84c8c84a97c8c14885002206a2d703ffcca22309b843b55b746994fb08c15fbbf0aadbf900398e8768c62dc01\",\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\",\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\"]},{\"prevout\":{\"hash\":\"dd54a8c470be54b02cd937a1758761863c2faf920b2645d2dd35bd0308ef0dfb\",\"n\":1},\"scriptSig\":\"220020f38dc1aecea9e28bea4410e6aa807be49cf6472b9a718750080b9703e80d9fe9\",\"nSequence\":4294967295,\"scriptWitness\":[\"3045022100bdc4d1151d0567bb4e377b473100eaf41544bb547bc6d82b0a0dae8e8e833a6d022013caa911c553558abe6fdf1a6853ca6bab6912d90676595cfd4d11afd4f7966301\",\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\",\"3045022100e1262b0e14df0f6f99d850651caa6b8881f7cbf811ad549cb0d6a1b1369beec902204232af72b6bfcb21a83d555374dc622275b7c2cac31b4f56d76b87d88fdc586e01\",\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\",\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\"]}],\"vout\":[{\"value\":2996996985,\"scriptPubKey\":\"0020a4d938999fff18a140d830009f8c9a2c5ab00d61cc3ffea10ee703b7d9b24b9a\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cfac\"},{\"value\":66667,\"scriptPubKey\":\"21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcdeac\"},{\"value\":66667,\"scriptPubKey\":\"2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de51ac\"},{\"value\":66667,\"scriptPubKey\":\"2103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6ac\"},{\"value\":66667,\"scriptPubKey\":\"210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d29ac\"},{\"value\":66667,\"scriptPubKey\":\"2103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232fac\"}],\"nLockTime\":0}").as(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector scripts(fc::json::from_string("[\"552102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d292103c6206bca3492f93b27a877362ffc25a57177fe0be4a7aaad661daead7703232f5bae\",\"5521025feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e92102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf2102c0ded2bc1f1305fb0faac5e6c03ee3a1924234985427b6167ca569d13df435cf21030d9f1f4be73391d5814bb00cdb6ae10b4a1182a32a77672b5b744efa2e88dcde2102096d78d32f51a1051c8e4f58ea99427ce335e76f6ea00c915cf6d7ac1270de512103b0184a0323802226dfaa767d0bc93e261762d3ae4457c04ca2613215365d2dc6210226d279da5bfd81f7ab9ab804a3d0b44a06dd883a1f29d4671d4da4793b9d0d295bae\"]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector amounts(fc::json::from_string("[1997997990,1000000000]").as>(GRAPHENE_MAX_NESTED_OBJECTS)); + std::vector> results(fc::json::from_string("[[\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\",\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\",\"304402204cc6d437f1f46263c36bc0605686b6072f0fb7c5991690e8ea06e8126f06a77f02205cfaa0f2e05ab9187fde9b10fbfee3ad06ae8b7120d455277186d1f8fd31b16c01\"\"30440220228c930388a0420aa9a17acdf414763bec0f57f92ecf8db51f02e3f8d82428aa0220417f2d0fbc5fd00d5d158a2e7fe7188857119b8d16d11f82f513594a28dbcbfa01\",\"3045022100d95008906e848a8165fbc0d3ed6d11643bc4814d4f8ae84c8c84a97c8c14885002206a2d703ffcca22309b843b55b746994fb08c15fbbf0aadbf900398e8768c62dc01\"],[\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\",\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\",\"3045022100c4d233c9183d91fd9f1821fb68e1180bbd6493eb66caf36438bccd8cb46a247302200d3a16ff3180fb9ffe8dd16c8ea71f461e7b940baea4c302c8d8e2ba9bf74b9201\",\"3045022100bdc4d1151d0567bb4e377b473100eaf41544bb547bc6d82b0a0dae8e8e833a6d022013caa911c553558abe6fdf1a6853ca6bab6912d90676595cfd4d11afd4f7966301\",\"3045022100e1262b0e14df0f6f99d850651caa6b8881f7cbf811ad549cb0d6a1b1369beec902204232af72b6bfcb21a83d555374dc622275b7c2cac31b4f56d76b87d88fdc586e01\"]]").as>>(GRAPHENE_MAX_NESTED_OBJECTS)); + + auto new_stacks = sort_sigs(trx, scripts, amounts, btc_context()); + for (size_t i = 0; i < trx.vin.size(); i++) { + BOOST_CHECK(new_stacks[i] == results[i]); + } +} + +BOOST_AUTO_TEST_CASE(weighted_multisig_spend_test) { + // create weighted multisig addess in regtest + std::vector priv_keys; + for (uint32_t i = 0; i < 15; ++i) { + const char *seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_keys.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_keys; + for (auto &key : priv_keys) { + pub_keys.push_back(key.get_public_key()); + } + // key weights + std::vector> weights; + for (uint16_t i = 0; i < 15; ++i) + weights.push_back(std::make_pair(pub_keys[i], i + 1)); + + btc_weighted_multisig_address addr(weights); + BOOST_CHECK(addr.get_address() == "bcrt1qj4dwcpsqxhszc6ee5wwndqxe04j4vzs0qksrzgscfarzduu6th8qja2m4m"); + + bytes redeem_script = addr.get_redeem_script(); + + // this address was filled with regtest transaction + // id 577c1abcbf87db230c499c3585a5f1e20a835fe2d39cade39c232554bd21d0b5 + // output 0, 10000 satoshis + + // now send it back to bcrt1qavkxpjkc30x0euepxup8qe2yfzpjyepzq0qctu + bitcoin_transaction tx; + tx.nVersion = 2; + tx.vin.resize(1); + tx.vout.resize(1); + tx.nLockTime = 0; + + tx.vin[0].prevout.hash = fc::sha256("577c1abcbf87db230c499c3585a5f1e20a835fe2d39cade39c232554bd21d0b5"); + tx.vin[0].prevout.n = 0; + tx.vin[0].nSequence = 0xffffffff; + + tx.vout[0].value = 9000; + bitcoin_address to_address("bcrt1qavkxpjkc30x0euepxup8qe2yfzpjyepzq0qctu"); + tx.vout[0].scriptPubKey = to_address.get_script(); + + uint64_t amount = 10000; + int32_t hash_type = 1; // implement SIGHASH_ALL scheme + + for (auto &key : priv_keys) { + bytes key_data(key.get_secret().data(), key.get_secret().data() + key.get_secret().data_size()); + std::vector sigs = sign_witness_transaction_part(tx, {redeem_script}, {amount}, key_data, btc_context(), hash_type); + // insert signatures in reverse order + tx.vin[0].scriptWitness.insert(tx.vin[0].scriptWitness.begin(), sigs[0]); + } + sign_witness_transaction_finalize(tx, {redeem_script}, false); + + BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000000101b5d021bd5425239ce3ad9cd3e25f830ae2f1a585359c490c23db87bfbc1a7c570000000000ffffffff012823000000000000160014eb2c60cad88bccfcf321370270654448832264221047304402204da1308f748ad89a268cf1c33fce2f88a39a7bcd8a60c5d2c95d7e2f2087d587022011ce8024743620c0388d4b998c8db28ff464928decbdb6114fab185033abbea00147304402206344d532aebe1ca501f6ae7191c5b7497d52fe582358c65dce9a20bd52b4b53402205dfc03292fdc5670dba1e73f9d418e7cbe1b18a8511eb9d7a99c7140f41b9bbc01483045022100a73580772db51b534673eebfc5c0d165a117fdafded7e4e5970a7452ff73cd2702200e616c6660011855c370b37b227c67ddcfda1dab08ff8c2027f313b82a00cbd501483045022100bfcf88627d42ac61ec8688292874d8326ad9015141c1ff203a07148f110f9dee022057d2c111ffc53b02bd462f88363563f4bbd10a9d317fe9ae02e129e4a8de69d901483045022100940eed87a3f4c359ad350fd8bd725fbb43db42c4f265a5e6e7c345b4b3d71f5c022046675535c070d3987af143a0daa5bd72b2ebe5e0057679681c04408aa7f3c382014830450221008d7d05e56253db6f99ca927583dfe3e4c1f9e94b8cc78498ccabf6afdd4c0bb702200717ef5e641cc02cfb048d4afca47e4a345756bbcd4dfcd6487e63c301ccb5930147304402204a7e44a6ec8ea1d0f20fa863e281ccf07be788d885c5149075f6009716e5d1d702201db6b8962fd2eed845e3df7979e3decc5a4acbc283b24df657d2ca3800ffb45501483045022100daf90eedd898e0b4f0270a1f5da300b0342f9ff45ea0b0882372afd1bff95b1c02207153a03fb5d4bf6a13c09eebebf9638d44968b04890c92d552511c96f62e55b70147304402205ebb11f079571c992d4b86a376369df6cea08a3d288264f8f0b942a22b28a900022060c7f9727c160c552fb6111a14253e27e8952e66cb491ee72c6b4eab46c1620501483045022100976cef7c51c5ad7a7ad55e748a5b499e2a9b8df613be8bd8f1c69b1f2a6facb902207b2505ebe9a4f853ce87c2a28945463a89fc2e6ce7104991f335cc80a7b2438c01483045022100e8b95ad09ae443303b3d0f84f5db1bb16ddca64f77d98d8c06347baa15831e8e02203a73c5f8b21d8aa339f1f652708db025c857b1086fadf9483ff34e41b9b59a8b0147304402203c949c4ecea198aaaa426185d904e7fdc0a8dd00caadf25d5ad30974a408513202205d75f601e6bd8289b899c60737947152f4a821e26ebb5b145303f3b494a5e310014730440220726943bffb5b8ca2bf487842de3918e0a5268ec0d6e99045853cacdbcbdc6b5902204347a8da6e05a353ec324cb37aa028cc3fcd4dc5939e6eea334ea7dc60c2144601473044022061d39cb49dc2cc27a48cb02d93332de59cd3134df4b7c23e375a1f0d90ffa5fc0220419cb88f1c2e961c33382278a85d5bf669ecb049ffce0b4e7116513a6621ff7e01473044022062e3daf133888df12f71ffb1ff62a37171dd6d9e7a5582a4d7928a281a3d666002201d83128b63483a901aebaeba80296cf8b99cc96de8be74ddfbc0f597d7cdc17401fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680151a200000000"); + // this transaction was published in regtest and was accepted, + // its id is 98666cd0f7e5e2040e4ad08477fc6f95df21724f2b0331a685ddbadad631b21b +} + +BOOST_AUTO_TEST_CASE(user_sig_one_or_2_of_15_multisig_spend_test) { + const auto user_pkey = get_privkey_bytes("cPzX5utDDBt2kfK4uz43e78zMxZSdfiGrV9wnkmqrqPnxfQCB2Rc"); + const auto son1_pkey = get_privkey_bytes("cSKyTeXidmj93dgbMFqgzD7yvxzA7QAYr5j9qDnY9seyhyv7gH2m"); + const auto son2_pkey = get_privkey_bytes("cQBBNyEw6P3pgc2NjPpKR2YoCpio9s3qEMkFkY7v9hByLAxeLQ3s"); + const auto son3_pkey = get_privkey_bytes("cQLKc4UgKyCdXY3PosndszEZTsB6mTrg4avZF6kDphrULKd2W6L4"); + const auto son4_pkey = get_privkey_bytes("cN43k9sqQimgzChZm9Qz1V1bdkjVwB3mcSHsEuj6bfUa4SP2AsTk"); + + auto public_key1 = fc::ecc::public_key(create_public_key_data(parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275"))); + auto public_key2 = fc::ecc::public_key(create_public_key_data(parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4"))); + auto public_key3 = fc::ecc::public_key(create_public_key_data(parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61"))); + auto public_key4 = fc::ecc::public_key(create_public_key_data(parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866"))); + auto public_key5 = fc::ecc::public_key(create_public_key_data(parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666"))); + auto public_key6 = fc::ecc::public_key(create_public_key_data(parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf"))); + auto public_key7 = fc::ecc::public_key(create_public_key_data(parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f"))); + auto public_key8 = fc::ecc::public_key(create_public_key_data(parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84"))); + auto public_key9 = fc::ecc::public_key(create_public_key_data(parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf"))); + auto public_key10 = fc::ecc::public_key(create_public_key_data(parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5"))); + auto public_key11 = fc::ecc::public_key(create_public_key_data(parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da"))); + auto public_key12 = fc::ecc::public_key(create_public_key_data(parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1"))); + auto public_key13 = fc::ecc::public_key(create_public_key_data(parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8"))); + auto public_key14 = fc::ecc::public_key(create_public_key_data(parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8"))); + auto public_key15 = fc::ecc::public_key(create_public_key_data(parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73"))); + + auto user_key = fc::ecc::public_key(create_public_key_data(parse_hex("0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd"))); + + btc_one_or_m_of_n_multisig_address addr(user_key, 2, {public_key1, public_key2, public_key3, public_key4, public_key5, public_key6, public_key7, public_key8, public_key9, public_key10, public_key11, public_key12, public_key13, public_key14, public_key15}); + + BOOST_CHECK(addr.get_address() == "bcrt1qp8vplzrn7alzpjq8cw4ynd6xqzassmefrh48dzsj0krvkq85dywq9txkqr"); + BOOST_CHECK(addr.get_redeem_script() == parse_hex("63210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac67522103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae68")); + + bytes redeem_script = addr.get_redeem_script(); + //User spend + bitcoin_transaction tx1; + tx1.nVersion = 2; + tx1.vin.resize(1); + tx1.vout.resize(1); + tx1.nLockTime = 0; + + tx1.vin[0].prevout.hash = fc::sha256("3cdb7e835006695b4f99fbad3a43ac9ce2ea63b67af0c3bb4c2af6cb54554f90"); + tx1.vin[0].prevout.n = 0; + tx1.vin[0].nSequence = 0xffffffff; + + tx1.vout[0].value = 9000; + bitcoin_address to_address("bcrt1qettl5u3jlh6e8s6hxpqsdpevxqmq947f0jnnfraeg4jsvhsme2uqplzjgt"); + tx1.vout[0].scriptPubKey = to_address.get_script(); + + uint64_t amount = 10000; + int32_t hash_type = 1; // implement SIGHASH_ALL scheme + + tx1.vin[0].scriptWitness.push_back(sign_witness_transaction_part(tx1, {redeem_script}, {amount}, user_pkey, btc_context(), hash_type)[0]); + bytes one = {0x01}; + tx1.vin[0].scriptWitness.push_back(one); + + sign_witness_transaction_finalize(tx1, {redeem_script}, false); + + BOOST_CHECK(fc::to_hex(pack(tx1)) == "02000000000101904f5554cbf62a4cbbc3f07ab663eae29cac433aadfb994f5b690650837edb3c0000000000ffffffff012823000000000000220020cad7fa7232fdf593c357304106872c303602d7c97ca7348fb94565065e1bcab80347304402206f7d3a4689fa1a8107bf5b4ab3f363db147e6eaee49c455cacaee434ac9e4786022054fdea815431dae2b82ce3f9a2151fd127e7dd2685ae5655938f22b5c38046d1010101fd270263210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac67522103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae6800000000"); + + //SON Spend + bitcoin_transaction tx2; + tx2.nVersion = 2; + tx2.vin.resize(1); + tx2.vout.resize(1); + tx2.nLockTime = 0; + + tx2.vin[0].prevout.hash = fc::sha256("00afbe7f3f31178c91c837ef160bef3d8108b80e368cbeda785e7a2f6e76f0a5"); + tx2.vin[0].prevout.n = 0; + tx2.vin[0].nSequence = 0xffffffff; + + tx2.vout[0].value = 9000; + tx2.vout[0].scriptPubKey = to_address.get_script(); + + tx2.vin[0].scriptWitness.push_back(bytes()); + tx2.vin[0].scriptWitness.push_back(sign_witness_transaction_part(tx2, {redeem_script}, {amount}, son2_pkey, btc_context(), hash_type)[0]); + tx2.vin[0].scriptWitness.push_back(sign_witness_transaction_part(tx2, {redeem_script}, {amount}, son4_pkey, btc_context(), hash_type)[0]); + tx2.vin[0].scriptWitness.push_back(bytes()); + + sign_witness_transaction_finalize(tx2, {redeem_script}, false); + + BOOST_CHECK(fc::to_hex(pack(tx2)) == "02000000000101a5f0766e2f7a5e78dabe8c360eb808813def0b16ef37c8918c17313f7fbeaf000000000000ffffffff012823000000000000220020cad7fa7232fdf593c357304106872c303602d7c97ca7348fb94565065e1bcab8050047304402204d6ed521e2dbe040f4139891b2dce21711757bcf5f4acf6dec9186157fe5bf3902206f0d1fa8e5c51c3f1c02568328ca92995136aee1a95fa1bc7739404b63f29c520147304402200999a6d7c657d729a6dceb1ed0e2cd441435e0d125dea99fbc117f14f267bf1e02202003c54c1cdf9d263c0a01a27fc53923c048a6230221765c3dc0e7fba4341d980100fd270263210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac67522103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae6800000000"); +} + +BOOST_AUTO_TEST_CASE(user_sig_one_or_weighted_multisig_spend_test) { + std::vector priv_keys; + for (uint32_t i = 0; i < 15; ++i) { + const char *seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_keys.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_keys; + for (auto &key : priv_keys) { + pub_keys.push_back(key.get_public_key()); + } + // key weights + std::vector> weights; + for (uint16_t i = 0; i < 15; ++i) + weights.push_back(std::make_pair(pub_keys[i], i + 1)); + + fc::sha256 h = fc::sha256::hash("user", 4); + fc::ecc::private_key user_key = fc::ecc::private_key::generate_from_seed(h); + fc::ecc::public_key user_pub_key = user_key.get_public_key(); + + btc_one_or_weighted_multisig_address addr(user_pub_key, weights); + BOOST_CHECK(addr.get_address() == "bcrt1q6e323wwh3nw5875kztgna6z7ygvec2mvt48e630gmlsdh3t8ed8s63qmm5"); + + bytes redeem_script = addr.get_redeem_script(); + + { + // this address was filled with regtest transaction + // id 11f5a6d6917c49eb46dd1e19bf1d76c09b58acc35cd92f6911695df44bddf4a0 + // output 0, 10000 satoshis + + // now send it to 2MtH9U8fEZbRmco3GYVMjSg9NfUyPn5RDN1 + // with single user signature + bitcoin_transaction tx; + tx.nVersion = 2; + tx.vin.resize(1); + tx.vout.resize(1); + tx.nLockTime = 0; + + tx.vin[0].prevout.hash = fc::sha256("11f5a6d6917c49eb46dd1e19bf1d76c09b58acc35cd92f6911695df44bddf4a0"); + tx.vin[0].prevout.n = 1; + tx.vin[0].nSequence = 0xffffffff; + + tx.vout[0].value = 9000; + bitcoin_address to_address("2MtH9U8fEZbRmco3GYVMjSg9NfUyPn5RDN1"); + tx.vout[0].scriptPubKey = to_address.get_script(); + + uint64_t amount = 10000; + int32_t hash_type = 1; // implement SIGHASH_ALL scheme + + bytes key_data(user_key.get_secret().data(), user_key.get_secret().data() + user_key.get_secret().data_size()); + std::vector sigs = sign_witness_transaction_part(tx, {redeem_script}, {amount}, key_data, btc_context(), hash_type); + tx.vin[0].scriptWitness.push_back(sigs[0]); + sign_witness_transaction_finalize(tx, {redeem_script}, false); + + // this transaction was published in regtest and was accepted, + // its id is 9969642bf24f3d25f025f91dbf87dd5e5cf8508597aedd56af6ef1f97658f9d9 + BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000000101a0f4dd4bf45d6911692fd95cc3ac589bc0761dbf191edd46eb497c91d6a6f5110100000000ffffffff01282300000000000017a9140b552f4a72cb614717878b20743d9e38e618130a87024730440220083ae11a94bc0c0a5783fa1d81e470cf906fbd5a8b3b8cadf89d78ba4b721e7a022045b5469576dc075f27f3ea510e07cf5037af3b462a2111d0f1d6e6da7d06728501fd83022102d2c1cb1575d323b6120b6e5bcc9ce5ad373e88e73e675030f1c2c5261b4dbc86ac635167007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680151a26800000000"); + } + + { + // this address was filled again with regtest transaction + // id 57df37df31895807d6a5255e24691c58c028d6f60b11b3f5504fbdd22df90872 + // output 0, 10000 satoshis + + // now send it to the primary wallet with sons signatures + bitcoin_transaction tx; + tx.nVersion = 2; + tx.vin.resize(1); + tx.vout.resize(1); + tx.nLockTime = 0; + + tx.vin[0].prevout.hash = fc::sha256("57df37df31895807d6a5255e24691c58c028d6f60b11b3f5504fbdd22df90872"); + tx.vin[0].prevout.n = 0; + tx.vin[0].nSequence = 0xffffffff; + + tx.vout[0].value = 9000; + btc_weighted_multisig_address to_address(weights); + tx.vout[0].scriptPubKey = to_address.get_script(); + + uint64_t amount = 10000; + int32_t hash_type = 1; // implement SIGHASH_ALL scheme + + for (auto &key : priv_keys) { + bytes key_data(key.get_secret().data(), key.get_secret().data() + key.get_secret().data_size()); + std::vector sigs = sign_witness_transaction_part(tx, {redeem_script}, {amount}, key_data, btc_context(), hash_type); + // insert signatures in reverse order + tx.vin[0].scriptWitness.insert(tx.vin[0].scriptWitness.begin(), sigs[0]); + } + // add empty sig for user signature + tx.vin[0].scriptWitness.push_back(bytes()); + + sign_witness_transaction_finalize(tx, {redeem_script}, false); + + // this transaction was published in regtest and was accepted, + // its id is d1c1f75a276fed52a230f57120fafb5af10fb5d9b666d37e78722e1b811fd6dd + BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000001017208f92dd2bd4f50f5b3110bf6d628c0581c69245e25a5d607588931df37df570000000000ffffffff012823000000000000220020fd2b172e2f825a9cba582cdabf5ba178c5ac3b49cd63970192cb2511fc59e17211483045022100b2401c5081bea13c87678ee1a11b09a937be7cab4ac0f295eded00f3d9920fbc022010d84be64051ec09f5d0682f59539e23df77dc5ada73a0ba0ac4e1ae32d7d0620147304402203196944571e652275eb5b055ea48de8d62a8321d6ba5c0c52f01bfd3d187dc7802207062762016d8b26946823b30470f720919f8312024bda8799984d55a784ef20301483045022100c2a183ca75915b4d2e8dc1bb94a2be536f1c8b26effed9360ca5913128b7dee102203fa40e103cb6e711acd84b1b94eb46279ad52a6affc95bc78359bb5ceb706126014830450221009e08aa12474c67c07dc90d94ce1181b1b9f347a13142c7bb9eba30b45b89b52c022031a4313250b01e4fb3edc63cb7cb3a5b0fd3362f8624ec9e7c63722fc6245b8c0147304402207e044ffed8c5d9a2458c728a0b0381571db4cadf83c0cc7d07930836ed712d5e0220786c41038b605f0fb7926395f869f756ff0018c73f934e73f9a3162903c0f2c201483045022100cd48b237d227af046b721e323a21cefbb47cbe6956893b0b3294e8d57cfc49c502202197209d0ed63998ad9fe5e4b448f472e7a0009c0d764b6855fd3bfcb65d258d0147304402205319cae42865486e4e4cfe040129f5057cc52768a8c11310e226171b7655b8d402203f521314eeb716aa5a1911e3663a27844ecf747fddbcfb201217abbeb236693a01483045022100f35e680293c1e3c97cd7a8b24f05c4f45862b8d93485100a7b934668467cc2bb02203827853d1efb1ae98266a622216fccd72acd5bbc1681ccd9e5c4e29c7daf3bc601483045022100bae57bb8eee66938c20b231bd71b84f5e09b582c005015f8ccd163c9afb6b08d02204deaf19b266ae3548b75235ab5c36c3334beb6fd93b936ef3eca7b0d39f9e03a01473044022044848ec4188a00695a45c4ae3e977654f8a819e9931501ae36b9c500a7e72a5e02207e0b27b855fe72a46a6d75c6286d9b47068bfc29fe54f86f5bf2761db7ecaf7701483045022100edd042c70e727d3956d4e08a1f9ae789f43ce44f5d90b5d19f32a78c20db57140220337aafb7441bcc29ad20a9d813db0cf3d9528102afa5191654feedd1d3b3fd2d0147304402205fa41757a0d1b25c8acf413700e90268d7f17d4ae296388d619bfbdc863154b70220529d33e4b6a34e8de2b821257097bbe16b5d0040c8ae4d443c61daa7d80f2c86014730440220623f7039e9e9bd652e3370a125b82919092d9eb5144b2835a208d59de3887c3d022041d274f9dabe3fb00db6deef0edf2e0671dc49f1fe7b4bcc6c589f697c02a44a01483045022100847dc557a81e06efe330c7a50d40e3aa7a4c97ea64dc93d809571aab5258ab6002203da85fc76c01d4a50a44e9de4c21e53a88444df573f2f000e9b3dddc7063bbf501473044022052bebca16b1629c121c23ce14e6d8f0f12603f96d54f07c33f4ccdbad57887a502206df9cb44417f513c97cf03bd8cd7a05d3b19294880158ef695c1ff5269f59bb30100fd83022102d2c1cb1575d323b6120b6e5bcc9ce5ad373e88e73e675030f1c2c5261b4dbc86ac635167007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680151a26800000000"); + } +} + +BOOST_AUTO_TEST_CASE(user_sig_timelocked_one_or_weighted_multisig_spend_test) { + std::vector priv_keys; + for (uint32_t i = 0; i < 15; ++i) { + const char *seed = reinterpret_cast(&i); + fc::sha256 h = fc::sha256::hash(seed, sizeof(i)); + priv_keys.push_back(fc::ecc::private_key::generate_from_seed(h)); + } + std::vector pub_keys; + for (auto &key : priv_keys) { + pub_keys.push_back(key.get_public_key()); + } + // key weights + std::vector> weights; + for (uint16_t i = 0; i < 15; ++i) + weights.push_back(std::make_pair(pub_keys[i], i + 1)); + + fc::sha256 h = fc::sha256::hash("user", 4); + fc::ecc::private_key user_key = fc::ecc::private_key::generate_from_seed(h); + fc::ecc::public_key user_pub_key = user_key.get_public_key(); + + uint32_t latency = 1; + btc_timelocked_one_or_weighted_multisig_address addr(user_pub_key, latency, weights); + BOOST_CHECK(addr.get_address() == "bcrt1qk9ef5eh4lsrc6uenchd4fpfvgd8avulnf7daqxhrt7d8arxyp8kqsnzyc4"); + + bytes redeem_script = addr.get_redeem_script(); + + { + // this address was filled with regtest transaction + // id e6f6bc21f4034ad222820162717574e7d2488b34412dccecec19e2bbd3889dee + // output 1, 10000 satoshis + + // now send it to 2MtH9U8fEZbRmco3GYVMjSg9NfUyPn5RDN1 + // with single user signature + bitcoin_transaction tx; + tx.nVersion = 2; + tx.vin.resize(1); + tx.vout.resize(1); + tx.nLockTime = 0; + + tx.vin[0].prevout.hash = fc::sha256("e6f6bc21f4034ad222820162717574e7d2488b34412dccecec19e2bbd3889dee"); + tx.vin[0].prevout.n = 1; + tx.vin[0].nSequence = 0x1; + + tx.vout[0].value = 9000; + bitcoin_address to_address("2MtH9U8fEZbRmco3GYVMjSg9NfUyPn5RDN1"); + tx.vout[0].scriptPubKey = to_address.get_script(); + + uint64_t amount = 10000; + int32_t hash_type = 1; // implement SIGHASH_ALL scheme + + bytes key_data(user_key.get_secret().data(), user_key.get_secret().data() + user_key.get_secret().data_size()); + std::vector sigs = sign_witness_transaction_part(tx, {redeem_script}, {amount}, key_data, btc_context(), hash_type); + tx.vin[0].scriptWitness.push_back(sigs[0]); + sign_witness_transaction_finalize(tx, {redeem_script}, false); + + // this transaction was published in regtest and was accepted only 1 block later then source tx + // its id is 32831d5c6c979166c4fdc7871e28d67858c9f66fbb0fdb8284eb16e42554d6c9 + BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000000101ee9d88d3bbe219ececcc2d41348b48d2e774757162018222d24a03f421bcf6e601000000000100000001282300000000000017a9140b552f4a72cb614717878b20743d9e38e618130a8702483045022100d3a5aa67227cd67f2a2f851b3762343ae407cba2e0e2fa8e6abdf3a6143681fd022033ba7e4cdcc2185c4b80021d7c0f90aa839c08c1477f54d4cfd5208ff4ea222801fd86022102d2c1cb1575d323b6120b6e5bcc9ce5ad373e88e73e675030f1c2c5261b4dbc86ac6351b2755167007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680151a26800000000"); + } + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/peerplays_sidechain/bitcoin_transaction_tests.cpp b/tests/peerplays_sidechain/bitcoin_transaction_tests.cpp new file mode 100644 index 000000000..20fe878c3 --- /dev/null +++ b/tests/peerplays_sidechain/bitcoin_transaction_tests.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +using namespace graphene::peerplays_sidechain::bitcoin; + +BOOST_AUTO_TEST_SUITE(bitcoin_transaction_tests) + +BOOST_AUTO_TEST_CASE(serialize_bitcoin_transaction_test) { + out_point prevout; + prevout.hash = fc::sha256("89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a"); + prevout.n = 0; + + tx_in in; + in.prevout = prevout; + in.scriptSig = parse_hex("473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101"); + in.nSequence = 4294967294; + + tx_out out1; + out1.value = 3500000000; + out1.scriptPubKey = parse_hex("76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac"); + + tx_out out2; + out2.value = 1499996160; + out2.scriptPubKey = parse_hex("76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac"); + + bitcoin_transaction tx; + tx.nVersion = 2; + tx.vin = {in}; + tx.vout = {out1, out2}; + tx.nLockTime = 101; + + const auto serialized = pack(tx); + + const auto expected = parse_hex("02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000"); + + BOOST_CHECK_EQUAL_COLLECTIONS(serialized.cbegin(), serialized.cend(), expected.cbegin(), expected.cend()); +} + +BOOST_AUTO_TEST_CASE(deserialize_bitcoin_transaction_test) { + bitcoin_transaction tx = unpack(parse_hex("02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000")); + + BOOST_CHECK_EQUAL(tx.nVersion, 2); + BOOST_CHECK_EQUAL(tx.nLockTime, 101); + + BOOST_REQUIRE_EQUAL(tx.vin.size(), 1); + BOOST_CHECK_EQUAL(tx.vin[0].prevout.hash.str(), "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a"); + BOOST_CHECK_EQUAL(tx.vin[0].prevout.n, 0); + + BOOST_CHECK(fc::to_hex(tx.vin[0].scriptSig) == "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101"); + BOOST_CHECK_EQUAL(tx.vin[0].nSequence, 4294967294); + + BOOST_REQUIRE_EQUAL(tx.vout.size(), 2); + BOOST_CHECK_EQUAL(tx.vout[0].value, 3500000000); + BOOST_CHECK(fc::to_hex(tx.vout[0].scriptPubKey) == "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac"); + BOOST_CHECK_EQUAL(tx.vout[1].value, 1499996160); + BOOST_CHECK(fc::to_hex(tx.vout[1].scriptPubKey) == "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac"); +} + +BOOST_AUTO_TEST_CASE(btc_tx_methods_test) { + const auto tx = unpack(parse_hex("0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f856040000002322002001d5d92effa6ffba3efa379f9830d0f75618b13393827152d26e4309000e88b1ffffffff0188b3f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02473044022038421164c6468c63dc7bf724aa9d48d8e5abe3935564d38182addf733ad4cd81022076362326b22dd7bfaf211d5b17220723659e4fe3359740ced5762d0e497b7dcc012321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac00000000")); + + BOOST_CHECK(tx.get_hash().str() == "a5947589e2762107ff650958ba0e3a3cf341f53281d15593530bf9762c4edab1"); + BOOST_CHECK(tx.get_txid().str() == "954f43dbb30ad8024981c07d1f5eb6c9fd461e2cf1760dd1283f052af746fc88"); + BOOST_CHECK(tx.get_vsize() == 148); +} + +BOOST_AUTO_TEST_CASE(bitcoin_transaction_builder_test) { + // All tests are only to verefy the compilation of transactions, the transactions are not real(not valid) + { // P2PKH to P2PKH + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2PKH, fc::sha256("5d42b45d5a3ddcf2421b208885871121551acf6ea5cc1c1b4e666537ab6fcbef"), 0, bytes()); + tb.add_out(payment_type::P2PKH, 4999990000, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000001efcb6fab3765664e1b1ccca56ecf1a552111878588201b42f2dc3d5a5db4425d0000000000ffffffff01f0ca052a010000001976a9143307bf6f98832e53a48b144d65c6a95700a93ffb88ac00000000"); + } /* + { // coinbase to P2PK + const auto pubkey = fc::raw::unpack( parse_hex( "02028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00" ) ); + out_point prevout; + prevout.hash = fc::sha256( "0000000000000000000000000000000000000000000000000000000000000000" ); + prevout.n = 0xffffffff; + + tx_in txin; + txin.prevout = prevout; + + bitcoin_transaction_builder tb; + tb.set_version( 2 ); + tb.add_in( payment_type::P2SH, txin, parse_hex( "022a020101" ) ); + tb.add_out( payment_type::P2PK, 625000000, pubkey); + tb.add_out( payment_type::NULLDATA, 0, parse_hex( "21030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac" ) ); + + const auto tx = tb.get_transaction(); + BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05022a020101ffffffff0240be402500000000232102028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00ac0000000000000000256a2321030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac00000000" ); + }*/ + { // P2SH to P2SH + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2SH, fc::sha256("40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8"), 0, bytes()); + tb.add_out(payment_type::P2SH, 0xffffffff, "3P14159f73E4gFr7JterCCQh9QjiTjiZrG"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee400000000000ffffffff01ffffffff0000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a8700000000"); + } + { // P2PK to NULLDATA + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2PK, fc::sha256("fa897a4a2b8bc507db6cf4425e81ca7ebde89a369e07d608ac7f7c311cb13b4f"), 0, bytes()); + tb.add_out(payment_type::NULLDATA, 0, parse_hex("ffffffff")); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000014f3bb11c317c7fac08d6079e369ae8bd7eca815e42f46cdb07c58b2b4a7a89fa0000000000ffffffff010000000000000000066a04ffffffff00000000"); + } + { // P2PK+P2PKH to P2PKH,P2PKH + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2PK, fc::sha256("13d29149e08b6ca63263f3dddd303b32f5aab646ebc6b7db84756d80a227f6d9"), 0, bytes()); + tb.add_in(payment_type::P2PKH, fc::sha256("a8e7f661925cdd2c0e37fc93c03540c113aa6bcea02b35de09377127f76d0da3"), 0, bytes()); + tb.add_out(payment_type::P2PKH, 4999990000, "mzg9RZ1p29uNXu4uTWoMdMERdVXZpunJhW"); + tb.add_out(payment_type::P2PKH, 4999990000, "n2SPW6abRxUnnTSSHp73VGahbPW4WT9GaK"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000002d9f627a2806d7584dbb7c6eb46b6aaf5323b30ddddf36332a66c8be04991d2130000000000ffffffffa30d6df727713709de352ba0ce6baa13c14035c093fc370e2cdd5c9261f6e7a80000000000ffffffff02f0ca052a010000001976a914d2276c7ed7af07f697175cc2cbcbbf32da81caba88acf0ca052a010000001976a914e57d9a9af070998bedce991c4d8e39f9c51eb93a88ac00000000"); + } + { // P2WPKH to P2WPKH + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2WPKH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 1, bytes()); + tb.add_out(payment_type::P2WPKH, 99988480, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001600143307bf6f98832e53a48b144d65c6a95700a93ffb00000000"); + } + { // P2WSH to P2WSH + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2WSH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 2, bytes()); + tb.add_out(payment_type::P2WSH, 99988360, "p2xtZoXeX5X8BP8JfFhQK2nD3emtjch7UeFm"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560200000000ffffffff0188b3f505000000001600140000010966776006953d5567439e5e39f86a0d2700000000"); + } + { // P2SH(WPKH) to P2SH(WPKH) + bitcoin_transaction_builder tb; + tb.set_version(2); + tb.add_in(payment_type::P2SH_WPKH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 3, parse_hex("ab68025513c3dbd2f7b92a94e0581f5d50f654e7")); + tb.add_out(payment_type::P2SH_WPKH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560300000014ab68025513c3dbd2f7b92a94e0581f5d50f654e7ffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000"); + } + { // P2SH(WSH) to P2SH(WSH) + bitcoin_transaction_builder tb; + tb.set_version(2); + const auto redeem_script = parse_hex("21038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac"); + tb.add_in(payment_type::P2SH_WSH, fc::sha256("fca01bd539623013f6f945dc6173c395394621ffaa53a9eb6da6e9a2e7c9400e"), 0, redeem_script); + tb.add_out(payment_type::P2SH_WSH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd"); + + const auto tx = tb.get_transaction(); + BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000010e40c9e7a2e9a66deba953aaff21463995c37361dc45f9f613306239d51ba0fc000000002321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acacffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000"); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/peerplays_sidechain/peerplays_sidechain_tests.cpp b/tests/peerplays_sidechain/peerplays_sidechain_tests.cpp new file mode 100644 index 000000000..84577384f --- /dev/null +++ b/tests/peerplays_sidechain/peerplays_sidechain_tests.cpp @@ -0,0 +1,17 @@ +#include + +#define BOOST_TEST_MODULE Peerplays SON Tests + +BOOST_AUTO_TEST_CASE(peerplays_sidechain) { +} + +#include +#include +#include + +boost::unit_test::test_suite *init_unit_test_suite(int argc, char *argv[]) { + std::srand(time(NULL)); + std::cout << "Random number generator seeded to " << time(NULL) << std::endl; + + return nullptr; +} diff --git a/tests/tests/account_role_tests.cpp b/tests/tests/account_role_tests.cpp new file mode 100644 index 000000000..73824678a --- /dev/null +++ b/tests/tests/account_role_tests.cpp @@ -0,0 +1,379 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE(account_role_tests, database_fixture) + +BOOST_AUTO_TEST_CASE(account_role_create_test) +{ + try + { + BOOST_TEST_MESSAGE("account_role_create_test"); + generate_blocks(HARDFORK_NFT_TIME); + generate_block(); + generate_block(); + set_expiration(db, trx); + ACTORS((resourceowner)(alice)(bob)(charlie)); + upgrade_to_lifetime_member(resourceowner); + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + upgrade_to_lifetime_member(charlie); + transfer(committee_account, resourceowner_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, alice_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, bob_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, charlie_id, asset(100000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + { + BOOST_TEST_MESSAGE("Send account_role_create_operation"); + + account_role_create_operation op; + op.owner = resourceowner_id; + op.name = "Test Account Role"; + op.metadata = "{\"country\": \"earth\", \"race\": \"human\" }"; + + int ops[] = {operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value}; + op.allowed_operations.insert(ops, ops + 6); + op.whitelisted_accounts.emplace(alice_id); + op.whitelisted_accounts.emplace(bob_id); + op.valid_to = db.head_block_time() + 1000; + + trx.operations.push_back(op); + sign(trx, resourceowner_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.begin(); + BOOST_REQUIRE(obj != idx.end()); + BOOST_CHECK(obj->owner == resourceowner_id); + BOOST_CHECK(obj->name == "Test Account Role"); + BOOST_CHECK(obj->metadata == "{\"country\": \"earth\", \"race\": \"human\" }"); + flat_set expected_allowed_operations = {operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value}; + BOOST_CHECK(obj->allowed_operations == expected_allowed_operations); + flat_set expected_whitelisted_accounts = {alice_id, bob_id}; + BOOST_CHECK(obj->whitelisted_accounts == expected_whitelisted_accounts); + BOOST_CHECK(obj->valid_to == db.head_block_time() + 1000); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_role_update_test) +{ + try + { + BOOST_TEST_MESSAGE("account_role_create_test"); + INVOKE(account_role_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + GET_ACTOR(resourceowner); + set_expiration(db, trx); + { + BOOST_TEST_MESSAGE("Send account_role_update_operation"); + + account_role_update_operation op; + op.owner = resourceowner_id; + op.account_role_id = account_role_id_type(0); + op.name = "Test Account Role Update"; + op.metadata = "{\"country\": \"earth\", \"race\": \"human\", \"op\":\"update\" }"; + + int ops_add[] = {operation::tag::value}; + int ops_delete[] = {operation::tag::value, operation::tag::value}; + op.allowed_operations_to_add.insert(ops_add, ops_add + 1); + op.allowed_operations_to_remove.insert(ops_delete, ops_delete + 2); + op.valid_to = db.head_block_time() + 10000; + + trx.operations.push_back(op); + sign(trx, resourceowner_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.begin(); + BOOST_REQUIRE(obj != idx.end()); + BOOST_CHECK(obj->owner == resourceowner_id); + BOOST_CHECK(obj->name == "Test Account Role Update"); + BOOST_CHECK(obj->metadata == "{\"country\": \"earth\", \"race\": \"human\", \"op\":\"update\" }"); + flat_set expected_allowed_operations = {operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value, + operation::tag::value}; + BOOST_CHECK(obj->allowed_operations == expected_allowed_operations); + flat_set expected_whitelisted_accounts = {alice_id, bob_id}; + BOOST_CHECK(obj->whitelisted_accounts == expected_whitelisted_accounts); + BOOST_CHECK(obj->valid_to == db.head_block_time() + 10000); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(account_role_delete_test) +{ + try + { + BOOST_TEST_MESSAGE("account_role_delete_test"); + INVOKE(account_role_create_test); + GET_ACTOR(resourceowner); + set_expiration(db, trx); + { + BOOST_TEST_MESSAGE("Send account_role_delete_operation"); + + account_role_delete_operation op; + op.owner = resourceowner_id; + op.account_role_id = account_role_id_type(0); + + trx.operations.push_back(op); + sign(trx, resourceowner_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 0); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(nft_metadata_create_mint_with_account_role_test) +{ + try + { + BOOST_TEST_MESSAGE("nft_metadata_create_mint_with_account_role_test"); + INVOKE(account_role_create_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(resourceowner); + + { + BOOST_TEST_MESSAGE("Send nft_metadata_create_operation"); + + nft_metadata_create_operation op; + op.owner = resourceowner_id; + op.name = "NFT Test"; + op.symbol = "NFT"; + op.base_uri = "http://nft.example.com"; + op.revenue_partner = resourceowner_id; + op.revenue_split = 1000; + op.is_transferable = true; + op.is_sellable = true; + op.account_role = account_role_id_type(0); + + trx.operations.push_back(op); + sign(trx, resourceowner_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results"); + + const auto &nftmd_idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(nftmd_idx.size() == 1); + auto nftmd_obj = nftmd_idx.begin(); + BOOST_REQUIRE(nftmd_obj != nftmd_idx.end()); + BOOST_CHECK(nftmd_obj->owner == resourceowner_id); + BOOST_CHECK(nftmd_obj->name == "NFT Test"); + BOOST_CHECK(nftmd_obj->symbol == "NFT"); + BOOST_CHECK(nftmd_obj->base_uri == "http://nft.example.com"); + BOOST_CHECK(nftmd_obj->account_role == account_role_id_type(0)); + + { + BOOST_TEST_MESSAGE("Send nft_mint_operation"); + + nft_mint_operation op; + op.payer = resourceowner_id; + op.nft_metadata_id = nftmd_obj->id; + op.owner = alice_id; + op.approved = alice_id; + + trx.operations.push_back(op); + sign(trx, resourceowner_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + + BOOST_TEST_MESSAGE("Check nft_mint_operation results"); + + const auto &nft_idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(nft_idx.size() == 1); + auto nft_obj = nft_idx.begin(); + BOOST_REQUIRE(nft_obj != nft_idx.end()); + BOOST_CHECK(nft_obj->owner == alice_id); + BOOST_CHECK(nft_obj->approved == alice_id); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(nft_safe_transfer_account_role_test) +{ + try + { + BOOST_TEST_MESSAGE("nft_safe_transfer_account_role_test"); + INVOKE(nft_metadata_create_mint_with_account_role_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + + { + BOOST_TEST_MESSAGE("Send nft_safe_transfer_from_operation"); + + nft_safe_transfer_from_operation op; + op.operator_ = alice_id; + op.from = alice_id; + op.to = bob_id; + op.token_id = nft_id_type(0); + op.data = "data"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check nft_safe_transfer_from_operation results"); + + const auto &nft_idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(nft_idx.size() == 1); + auto nft_obj = nft_idx.begin(); + BOOST_REQUIRE(nft_obj != nft_idx.end()); + BOOST_CHECK(nft_obj->owner == bob_id); + + { + BOOST_TEST_MESSAGE("Send nft_safe_transfer_from_operation"); + + nft_safe_transfer_from_operation op; + op.operator_ = bob_id; + op.from = bob_id; + op.to = charlie_id; + op.token_id = nft_id_type(0); + op.data = "data"; + + trx.operations.push_back(op); + sign(trx, bob_private_key); + // Charlie is not whitelisted by resource creator + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(nft_offer_bid_account_role_test) +{ + try + { + BOOST_TEST_MESSAGE("nft_offer_bid_account_role_test"); + INVOKE(nft_metadata_create_mint_with_account_role_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(charlie); + + { + BOOST_TEST_MESSAGE("Send create_offer"); + + offer_operation offer_op; + offer_op.item_ids.emplace(nft_id_type(0)); + offer_op.issuer = alice_id; + offer_op.buying_item = false; + offer_op.maximum_price = asset(10000); + offer_op.minimum_price = asset(10); + offer_op.offer_expiration_date = db.head_block_time() + fc::seconds(15); + + trx.operations.push_back(offer_op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + } + // Charlie tries to bid but fails. + { + BOOST_TEST_MESSAGE("Send create_bid by charlie"); + bid_operation bid_op; + bid_op.offer_id = offer_id_type(0); + // Buy it now price + bid_op.bid_price = asset(10000); + bid_op.bidder = charlie_id; + trx.operations.push_back(bid_op); + sign(trx, charlie_private_key); + // Charlie is not whitelisting to perform bid_operation by resource/metadata owner + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + } + // Bob succeeds in bidding. + { + BOOST_TEST_MESSAGE("Send create_bid by bob"); + bid_operation bid_op; + bid_op.offer_id = offer_id_type(0); + // Buy it now price + bid_op.bid_price = asset(10000); + bid_op.bidder = bob_id; + trx.operations.push_back(bid_op); + sign(trx, bob_private_key); + // Bob is whitelisted in account role created by resource/metadata owner + PUSH_TX(db, trx); + trx.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check offer results"); + + const auto &nft_idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(nft_idx.size() == 1); + auto nft_obj = nft_idx.begin(); + BOOST_REQUIRE(nft_obj != nft_idx.end()); + BOOST_CHECK(nft_obj->owner == bob_id); + // Charlie tries to bid (buy offer) but fails + { + BOOST_TEST_MESSAGE("Send create_offer"); + + offer_operation offer_op; + offer_op.item_ids.emplace(nft_id_type(0)); + offer_op.issuer = charlie_id; + offer_op.buying_item = true; + offer_op.maximum_price = asset(10000); + offer_op.minimum_price = asset(10); + offer_op.offer_expiration_date = db.head_block_time() + fc::seconds(15); + + trx.operations.push_back(offer_op); + sign(trx, charlie_private_key); + // Charlie is not whitelisting to perform offer_operation by resource/metadata owner + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/tests/affiliate_tests.cpp b/tests/tests/affiliate_tests.cpp index 72482a0a9..804d9e8a8 100644 --- a/tests/tests/affiliate_tests.cpp +++ b/tests/tests/affiliate_tests.cpp @@ -289,7 +289,7 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test ) { ACTORS( (irene) ); - const asset_id_type btc_id = create_user_issued_asset( "BTC", irene, 0 ).id; + const asset_id_type btc_id = create_user_issued_asset( "BTCTEST", irene, 0 ).id; issue_uia( irene, asset( 100000, btc_id ) ); affiliate_test_helper ath( *this ); @@ -517,7 +517,7 @@ BOOST_AUTO_TEST_CASE( bookie_payout_test ) { try { ACTORS( (irene) ); - const asset_id_type btc_id = create_user_issued_asset( "BTC", irene, 0 ).id; + const asset_id_type btc_id = create_user_issued_asset( "BTCTEST", irene, 0 ).id; affiliate_test_helper ath( *this ); @@ -616,7 +616,7 @@ BOOST_AUTO_TEST_CASE( statistics_test ) INVOKE(bookie_payout_test); - const asset_id_type btc_id = get_asset( "BTC" ).id; + const asset_id_type btc_id = get_asset( "BTCTEST" ).id; transfer( ath.alice_id, ath.ann_id, asset( 100, btc_id ), asset(0) ); transfer( ath.alice_id, ath.audrey_id, asset( 100, btc_id ), asset(0) ); diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index a6169489a..387036df2 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) { vector other; flat_set active_set, owner_set; - operation_get_required_authorities(op,active_set,owner_set,other); + operation_get_required_authorities(op, active_set, owner_set, other, false); BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu); @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account ) active_set.clear(); other.clear(); - operation_get_required_authorities(op.proposed_ops.front().op,active_set,owner_set,other); + operation_get_required_authorities(op.proposed_ops.front().op, active_set, owner_set, other, false); BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu); @@ -1055,7 +1055,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) flat_set active_set, owner_set; vector others; - trx.get_required_authorities( active_set, owner_set, others ); + trx.get_required_authorities(active_set, owner_set, others, false); PUSH_TX( db, trx, skip ); @@ -1204,9 +1204,12 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set = tx.get_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, false); + set result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, true); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) ); @@ -1326,9 +1329,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set = tx.get_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, false); + set result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, true); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; auto chk_min = [&]( @@ -1338,9 +1344,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set = tx.minimize_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, false); + set result_set2 = tx.minimize_required_signatures(db.get_chain_id(), available_keys, + get_active, get_owner, get_custom, true); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) ); @@ -1357,9 +1366,13 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) ); BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ), fc::exception ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, false ), + fc::exception ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true ), + fc::exception ); sign( tx, alice_private_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true ); } catch(fc::exception& e) { diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index b7ed69fe7..9885b5483 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -733,7 +733,7 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) fc::time_point_sec maintenence_time = db.get_dynamic_global_properties().next_maintenance_time; BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch()); auto initial_properties = db.get_global_properties(); - const account_object& nathan = create_account("nathan"); + auto nathan = create_account("nathan"); upgrade_to_lifetime_member(nathan); const committee_member_object nathans_committee_member = create_committee_member(nathan); { @@ -747,6 +747,7 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture ) } generate_block(); + nathan = get_account("nathan"); transfer(account_id_type()(db), nathan, asset(5000)); generate_blocks(maintenence_time - initial_properties.parameters.block_interval); diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp index 943b8265d..8b8a1a517 100644 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(get_account_history_additional) { const account_object& dan = create_account("dan"); // create op 1 create_bitasset("CNY", dan.id); // create op 2 - create_bitasset("BTC", account_id_type()); // create op 3 + create_bitasset("BTCTEST", account_id_type()); // create op 3 create_bitasset("XMR", dan.id); // create op 4 create_bitasset("EUR", account_id_type()); // create op 5 create_bitasset("OIL", dan.id); // create op 6 @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(track_account) { BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u); // create more ops, starting with an untracked account - create_bitasset( "BTC", account_id_type() ); + create_bitasset( "BTCTEST", account_id_type() ); create_bitasset( "GBP", dan_id ); generate_block( ~database::skip_fork_db ); @@ -468,7 +468,7 @@ BOOST_AUTO_TEST_CASE(track_account) { db.pop_block(); // Try again, should result in same object IDs - create_bitasset( "BTC", account_id_type() ); + create_bitasset( "BTCTEST", account_id_type() ); create_bitasset( "GBP", dan_id ); generate_block(); diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp new file mode 100644 index 000000000..755ad37f9 --- /dev/null +++ b/tests/tests/sidechain_addresses_test.cpp @@ -0,0 +1,170 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( sidechain_addresses_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) { + + BOOST_TEST_MESSAGE("sidechain_address_add_test"); + + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)); + + generate_block(); + set_expiration(db, trx); + + { + BOOST_TEST_MESSAGE("Send sidechain_address_add_operation"); + + sidechain_address_add_operation op; + op.payer = alice_id; + op.sidechain_address_account = alice_id; + op.sidechain = sidechain_type::bitcoin; + op.deposit_public_key = "deposit_public_key"; + op.withdraw_public_key = "withdraw_public_key"; + op.withdraw_address = "withdraw_address"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check sidechain_address_add_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->sidechain_address_account == alice_id ); + BOOST_CHECK( obj->sidechain == sidechain_type::bitcoin ); + BOOST_CHECK( obj->deposit_public_key == "deposit_public_key" ); + BOOST_CHECK( obj->deposit_address == "" ); + BOOST_CHECK( obj->deposit_address_data == "" ); + BOOST_CHECK( obj->withdraw_public_key == "withdraw_public_key" ); + BOOST_CHECK( obj->withdraw_address == "withdraw_address" ); +} + +BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { + BOOST_TEST_MESSAGE("sidechain_address_update_test"); + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + + INVOKE(sidechain_address_add_test); + + GET_ACTOR(alice); + ACTORS((bob)); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + BOOST_REQUIRE( obj != idx.end() ); + std::string new_deposit_public_key = "deposit_public_key"; + std::string new_deposit_address = "new_deposit_address"; + std::string new_deposit_address_data = "new_deposit_address_data"; + std::string new_withdraw_public_key = "withdraw_public_key"; + std::string new_withdraw_address = "withdraw_address"; + + generate_block(); + auto& son = db.create( [&]( son_object& sobj ) + { + sobj.son_account = bob_id; + sobj.statistics = db.create([&](son_statistics_object& s){s.owner = sobj.id;}).id; + }); + generate_block(); + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + son_info sinfo; + sinfo.son_id = son.id; + _gpo.active_sons.push_back(sinfo); + }); + generate_block(); + { + BOOST_TEST_MESSAGE("Send sidechain_address_update_operation"); + trx.clear(); + sidechain_address_update_operation op; + op.payer = bob_id; + op.sidechain_address_id = sidechain_address_id_type(0); + op.sidechain_address_account = obj->sidechain_address_account; + op.sidechain = obj->sidechain; + op.deposit_public_key = new_deposit_public_key; + op.deposit_address = new_deposit_address; + op.deposit_address_data = new_deposit_address_data; + op.withdraw_public_key = new_withdraw_public_key; + op.withdraw_address = new_withdraw_address; + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + { + BOOST_TEST_MESSAGE("Check sidechain_address_update_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->sidechain_address_account == obj->sidechain_address_account ); + BOOST_CHECK( obj->sidechain == obj->sidechain ); + BOOST_CHECK( obj->deposit_public_key == new_deposit_public_key ); + BOOST_CHECK( obj->deposit_address == new_deposit_address ); + BOOST_CHECK( obj->deposit_address_data == new_deposit_address_data ); + BOOST_CHECK( obj->withdraw_public_key == new_withdraw_public_key ); + BOOST_CHECK( obj->withdraw_address == new_withdraw_address ); + } +} + +BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) { + + BOOST_TEST_MESSAGE("sidechain_address_delete_test"); + + INVOKE(sidechain_address_add_test); + + GET_ACTOR(alice); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + BOOST_REQUIRE( obj != idx.end() ); + + { + BOOST_TEST_MESSAGE("Send sidechain_address_delete_operation"); + + sidechain_address_delete_operation op; + op.payer = alice_id; + op.sidechain_address_id = sidechain_address_id_type(0); + op.sidechain_address_account = alice_id; + op.sidechain = obj->sidechain; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + time_point_sec now = db.head_block_time(); + generate_block(); + + { + BOOST_TEST_MESSAGE("Check sidechain_address_delete_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + BOOST_REQUIRE( obj == idx.end() ); + auto expired_obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, now ) ); + BOOST_REQUIRE( expired_obj != idx.end() ); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp new file mode 100644 index 000000000..8029c58a3 --- /dev/null +++ b/tests/tests/son_operations_tests.cpp @@ -0,0 +1,788 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( create_son_test ) { + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)(bob)); + + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, alice_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + set_expiration(db, trx); + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + + // amount in the son balance need to be at least 50 + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); + + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); + trx.clear(); + + trx.operations.push_back(op); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + deposit = ptx.operation_results[0].get(); + + auto deposit_vesting = db.get(ptx.operation_results[0].get()); + + BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50*GRAPHENE_BLOCKCHAIN_PRECISION); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50*GRAPHENE_BLOCKCHAIN_PRECISION)), false); // cant withdraw + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment ; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + + op.validate(); + + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // alice became son + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url; + op.deposit = deposit; + op.pay_vb = payment; + op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->url == test_url ); + BOOST_CHECK( obj->signing_key == alice_public_key ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" ); + BOOST_CHECK( obj->deposit.instance == deposit.instance.value ); + BOOST_CHECK( obj->pay_vb.instance == payment.instance.value ); +} + +BOOST_AUTO_TEST_CASE( update_son_test ) { + + INVOKE(create_son_test); + GET_ACTOR(alice); + + std::string new_url = "https://anewurl.com"; + + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "new bitcoin address"; + + son_update_operation op; + op.son_id = son_id_type(0); + op.owner_account = alice_id; + op.new_url = new_url; + op.new_sidechain_public_keys = sidechain_public_keys; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->url == new_url ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "new bitcoin address" ); +} + +BOOST_AUTO_TEST_CASE( deregister_son_test ) { +try { + INVOKE(create_son_test); + GET_ACTOR(alice); + + auto deposit_vesting = db.get(vesting_balance_id_type(0)); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw + + { + son_deregister_operation op; + op.son_id = son_id_type(0); + op.payer = alice_id; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + } + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1); +} +catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; +} } + +BOOST_AUTO_TEST_CASE( deregister_son_test_with_consensus_account ) { +try { + INVOKE(create_son_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + // Modify SON's status to active + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + db.modify( *son_stats_obj, [&]( son_statistics_object& _s) + { + _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + }); + + auto deposit_vesting = db.get(vesting_balance_id_type(0)); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw + + { + trx.clear(); + son_deregister_operation op; + op.son_id = son_id_type(0); + op.payer = db.get_global_properties().parameters.son_account(); + + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_REQUIRE( idx.size() == 1 ); + BOOST_REQUIRE( obj->status == son_status::deregistered ); + BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now ); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + BOOST_CHECK_EQUAL(deposit_vesting.policy.get().vesting_cliff_seconds, + db.get_global_properties().parameters.son_vesting_period()); // in linear policy + + now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw + + generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period())); + generate_block(); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed +} +catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; +} } + +BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner +try { + + INVOKE(create_son_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + + // bob tries to update a son object he dont own + { + son_update_operation op; + op.owner_account = bob_id; + op.new_url = "whatever"; + op.son_id = son_id_type(0); + + trx.operations.push_back(op); + sign(trx, bob_private_key); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); + } + generate_block(); + + set_expiration(db, trx); + trx.clear(); + + const auto& idx = db.get_index_type().indices().get(); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + // not changing + BOOST_CHECK( obj->url == "https://create_son_test" ); + + // bob tries to deregister a son object he dont own + { + son_deregister_operation op; + op.son_id = son_id_type(0); + op.payer = bob_id; + + trx.operations.push_back(op); + sign(trx, bob_private_key); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception); + + } + generate_block(); + + obj = idx.find( alice_id ); + // not deleting + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->son_account.instance == alice_id.instance); +} +catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; +} +} + +BOOST_AUTO_TEST_CASE( son_pay_test ) +{ + try + { + const dynamic_global_property_object& dpo = db.get_dynamic_global_properties(); + const auto block_interval = db.get_global_properties().parameters.block_interval; + BOOST_CHECK( dpo.son_budget.value == 0); + generate_blocks(HARDFORK_SON_TIME); + while (db.head_block_time() <= HARDFORK_SON_TIME) { + generate_block(); + } + generate_block(); + set_expiration(db, trx); + + { + ACTORS((alice)(bob)); + // Send some core to the actors + transfer( committee_account, alice_id, asset( 20000 * 100000) ); + transfer( committee_account, bob_id, asset( 20000 * 100000) ); + } + + generate_block(); + // Enable default fee schedule to collect fees + enable_fees(); + // Make SON Budget small for testing purposes + // Make witness budget zero so that amount can be allocated to SON + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + _gpo.parameters.extensions.value.son_pay_max = 200; + _gpo.parameters.witness_pay_per_block = 0; + } ); + + GET_ACTOR(alice); + GET_ACTOR(bob); + // Upgrades pay fee and this goes to reserve + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + // Note payment time just to generate enough blocks to make budget + auto pay_fee_time = db.head_block_time().sec_since_epoch(); + generate_block(); + // Do maintenance from the upcoming block + auto schedule_maint = [&]() + { + db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo ) + { + _dpo.next_maintenance_time = db.head_block_time() + 1; + } ); + }; + + // Generate enough blocks to make budget + while( db.head_block_time().sec_since_epoch() - pay_fee_time < 100 * block_interval ) + { + generate_block(); + } + + // Enough blocks generated schedule maintenance now + schedule_maint(); + // This block triggers maintenance + generate_block(); + + // Check that the SON Budget is allocated and Witness budget is zero + BOOST_CHECK( dpo.son_budget.value == 200); + BOOST_CHECK( dpo.witness_budget.value == 0); + + // Now create SONs + std::string test_url1 = "https://create_son_test1"; + std::string test_url2 = "https://create_son_test2"; + + // create deposit vesting + vesting_balance_id_type deposit1; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + set_expiration(db, trx); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit1 = ptx.operation_results[0].get(); + } + + // create payment vesting + vesting_balance_id_type payment1; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + set_expiration(db, trx); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment1 = ptx.operation_results[0].get(); + } + + // create deposit vesting + vesting_balance_id_type deposit2; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + set_expiration(db, trx); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit2 = ptx.operation_results[0].get(); + } + + // create payment vesting + vesting_balance_id_type payment2; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + set_expiration(db, trx); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment2 = ptx.operation_results[0].get(); + } + + // alice becomes son + { + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url1; + op.deposit = deposit1; + op.pay_vb = payment1; + op.fee = asset(0); + op.signing_key = alice_private_key.get_public_key(); + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + + // bob becomes son + { + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url2; + op.deposit = deposit2; + op.pay_vb = payment2; + op.fee = asset(0); + op.signing_key = bob_private_key.get_public_key(); + trx.operations.push_back(op); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + + generate_block(); + // Check if SONs are created properly + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 2 ); + // Alice's SON + auto obj1 = idx.find( alice_id ); + BOOST_REQUIRE( obj1 != idx.end() ); + BOOST_CHECK( obj1->url == test_url1 ); + BOOST_CHECK( obj1->signing_key == alice_private_key.get_public_key() ); + BOOST_CHECK( obj1->deposit.instance == deposit1.instance.value ); + BOOST_CHECK( obj1->pay_vb.instance == payment1.instance.value ); + // Bob's SON + auto obj2 = idx.find( bob_id ); + BOOST_REQUIRE( obj2 != idx.end() ); + BOOST_CHECK( obj2->url == test_url2 ); + BOOST_CHECK( obj2->signing_key == bob_private_key.get_public_key() ); + BOOST_CHECK( obj2->deposit.instance == deposit2.instance.value ); + BOOST_CHECK( obj2->pay_vb.instance == payment2.instance.value ); + // Get the statistics object for the SONs + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 2 ); + auto son_stats_obj1 = sidx.find( obj1->statistics ); + auto son_stats_obj2 = sidx.find( obj2->statistics ); + BOOST_REQUIRE( son_stats_obj1 != sidx.end() ); + BOOST_REQUIRE( son_stats_obj2 != sidx.end() ); + // Modify the transaction signed statistics of Alice's SON + db.modify( *son_stats_obj1, [&]( son_statistics_object& _s) + { + _s.txs_signed = 2; + }); + // Modify the transaction signed statistics of Bob's SON + db.modify( *son_stats_obj2, [&]( son_statistics_object& _s) + { + _s.txs_signed = 3; + }); + + // Note the balances before the maintenance + int64_t obj1_balance = db.get_balance(obj1->son_account, asset_id_type()).amount.value; + int64_t obj2_balance = db.get_balance(obj2->son_account, asset_id_type()).amount.value; + // Next maintenance triggerred + generate_blocks(dpo.next_maintenance_time); + generate_block(); + // Check if the signed transaction statistics are reset for both SONs + BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed, 0); + BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed, 0); + + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed, 2); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed, 3); + // Check that Alice and Bob are paid for signing the transactions in the previous day/cycle + BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance); + BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance); + // Check the SON Budget is again allocated after maintenance + BOOST_CHECK( dpo.son_budget.value == 200); + BOOST_CHECK( dpo.witness_budget.value == 0); + }FC_LOG_AND_RETHROW() + + } + +BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { + + try + { + INVOKE(create_son_test); + GET_ACTOR(alice); + + { + // Send Heartbeat for an inactive SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = fc::time_point::now(); + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + + { + // Try to go in maintenance for an inactive SON + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::request_maintenance; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + // Modify SON's status to active + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::active; + }); + + db.modify( *son_stats_obj, [&]( son_statistics_object& _s) + { + _s.last_down_timestamp = fc::time_point_sec(db.head_block_time()); + }); + + { + generate_block(); + // Request SON Maintenance + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::request_maintenance; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_CHECK( obj->status == son_status::request_maintenance); + } + + { + generate_block(); + // Cancel SON Maintenance request + son_maintenance_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.request_type = son_maintenance_request_type::cancel_request_maintenance; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_CHECK( obj->status == son_status::active); + } + + // Modify SON's status to in_maintenance + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + uint64_t downtime = 0; + + { + generate_block(); + // Send Heartbeat for an in_maintenance SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); + downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); + BOOST_CHECK( obj->status == son_status::inactive); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + + // Modify SON's status to in_maintenance + db.modify( *obj, [&]( son_object& _s) + { + _s.status = son_status::in_maintenance; + }); + + // SON is selected as one of the active SONs + db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + { + son_info son_inf; + son_inf.son_id = son_id_type(0); + _gpo.active_sons.push_back(son_inf); + }); + + { + generate_block(); + // Send Heartbeat for an in_maintenance SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); + downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); + BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + + { + generate_block(); + // Send Heartbeat for an active SON + son_heartbeat_operation op; + op.owner_account = alice_id; + op.son_id = son_id_type(0); + op.ts = (db.head_block_time()+fc::seconds(2*db.block_interval())); + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime); + BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + } + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( son_report_down_test ) { + + try + { + INVOKE(son_heartbeat_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + + generate_block(); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find( alice_id ); + BOOST_REQUIRE( obj != idx.end() ); + + const auto& sidx = db.get_index_type().indices().get(); + BOOST_REQUIRE( sidx.size() == 1 ); + auto son_stats_obj = sidx.find( obj->statistics ); + BOOST_REQUIRE( son_stats_obj != sidx.end() ); + + BOOST_CHECK( obj->status == son_status::active); + + { + // Check that transaction fails if down_ts < last_active_timestamp + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.son_account(); + op.son_id = son_id_type(0); + op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + + { + // Check that transaction fails if payer is not db.get_global_properties().parameters.son_account(). + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = alice_id; + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, alice_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + + { + // Check that transaction succeeds after getting enough approvals on db.get_global_properties().parameters.son_account(). + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.son_account(); + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + PUSH_TX( db, trx, ~0); + generate_block(); + trx.clear(); + + BOOST_CHECK( obj->status == son_status::in_maintenance); + BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts); + } + + { + // Check that transaction fails if report down sent for an in_maintenance SON. + generate_block(); + // Send Report Down Operation for an active status SON + son_report_down_operation op; + op.payer = db.get_global_properties().parameters.son_account(); + op.son_id = son_id_type(0); + op.down_ts = son_stats_obj->last_active_timestamp; + + trx.operations.push_back(op); + set_expiration(db, trx); + sign(trx, bob_private_key); + // Expect an exception + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0), fc::exception); + trx.clear(); + } + } FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp new file mode 100644 index 000000000..cef29b546 --- /dev/null +++ b/tests/tests/son_wallet_tests.cpp @@ -0,0 +1,225 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include + +using namespace graphene; +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( son_wallet_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { + + BOOST_TEST_MESSAGE("son_wallet_recreate_test"); + + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)(bob)); + + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, alice_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, bob_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + generate_block(); + set_expiration(db, trx); + + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_alice = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_alice = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // alice becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url; + op.deposit = deposit_alice; + op.pay_vb = payment_alice; + op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + // create deposit vesting + vesting_balance_id_type deposit_bob; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_bob ; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // bob becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit_bob; + op.pay_vb = payment_bob; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + generate_blocks(60); + set_expiration(db, trx); + + { + BOOST_TEST_MESSAGE("Send son_wallet_recreate_operation"); + + son_wallet_recreate_operation op; + + op.payer = db.get_global_properties().parameters.son_account(); + + { + son_info si; + si.son_id = son_id_type(0); + si.weight = 1000; + si.signing_key = alice_public_key; + si.sidechain_public_keys[sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + { + son_info si; + si.son_id = son_id_type(1); + si.weight = 1000; + si.signing_key = bob_public_key; + si.sidechain_public_keys[sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check son_wallet_recreate_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->expires == time_point_sec::maximum() ); +} + +BOOST_AUTO_TEST_CASE( son_wallet_update_test ) { + + BOOST_TEST_MESSAGE("son_wallet_update_test"); + + INVOKE(son_wallet_recreate_test); + GET_ACTOR(alice); + + { + BOOST_TEST_MESSAGE("Send son_wallet_update_operation"); + + son_wallet_update_operation op; + + op.payer = db.get_global_properties().parameters.son_account(); + op.son_wallet_id = son_wallet_id_type(0); + op.sidechain = sidechain_type::bitcoin; + op.address = "bitcoin address"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + { + BOOST_TEST_MESSAGE("Check son_wallet_update_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->addresses.at(sidechain_type::bitcoin) == "bitcoin address" ); + } + +} + +BOOST_AUTO_TEST_SUITE_END() From 3e8a64f1b7c91172376ad6a2e48c1fc193f888c4 Mon Sep 17 00:00:00 2001 From: Serki Date: Wed, 7 Oct 2020 14:58:03 +0200 Subject: [PATCH 02/15] SON hardfork time to Saturday, October 17, 2020 00:00:00 UTC --- libraries/chain/hardfork.d/SON.hf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf index b3947a750..d3ecce127 100644 --- a/libraries/chain/hardfork.d/SON.hf +++ b/libraries/chain/hardfork.d/SON.hf @@ -1,7 +1,5 @@ -// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800 -// SON HARDFORK Monday, March 30, 2020 3:00:00 PM - 1585573200 -// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791 +// SON HARDFORK Saturday, October 17, 2020 00:00:00 UTC #ifndef HARDFORK_SON_TIME #include -#define HARDFORK_SON_TIME (fc::time_point_sec( 1585573200 )) +#define HARDFORK_SON_TIME (fc::time_point_sec( 1602892800 )) #endif From 9642e5d7af869bdbc13f379cedb96160b10708ab Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Fri, 9 Oct 2020 23:58:25 +1100 Subject: [PATCH 03/15] hotfix - chain params variable overflow, rbac hf check (#387) --- .../chain/protocol/chain_parameters.hpp | 12 +++++----- .../graphene/chain/rbac_hardfork_visitor.hpp | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 88d5dccf9..0dbf04bd0 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -59,7 +59,7 @@ namespace graphene { namespace chain { optional < uint32_t > son_vesting_amount = SON_VESTING_AMOUNT; optional < uint32_t > son_vesting_period = SON_VESTING_PERIOD; - optional < uint32_t > son_pay_max = SON_PAY_MAX; + optional < uint64_t > son_pay_max = SON_PAY_MAX; optional < uint32_t > son_pay_time = SON_PAY_TIME; optional < uint32_t > son_deregister_time = SON_DEREGISTER_TIME; optional < uint32_t > son_heartbeat_frequency = SON_HEARTBEAT_FREQUENCY; @@ -181,19 +181,19 @@ namespace graphene { namespace chain { inline uint32_t son_vesting_period()const { return extensions.value.son_vesting_period.valid() ? *extensions.value.son_vesting_period : SON_VESTING_PERIOD; /// current period start date } - inline uint16_t son_pay_max()const { + inline uint64_t son_pay_max()const { return extensions.value.son_pay_max.valid() ? *extensions.value.son_pay_max : SON_PAY_MAX; } - inline uint16_t son_pay_time()const { + inline uint32_t son_pay_time()const { return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME; } - inline uint16_t son_deregister_time()const { + inline uint32_t son_deregister_time()const { return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME; } - inline uint16_t son_heartbeat_frequency()const { + inline uint32_t son_heartbeat_frequency()const { return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY; } - inline uint16_t son_down_time()const { + inline uint32_t son_down_time()const { return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME; } inline uint16_t son_bitcoin_min_tx_confirmations()const { diff --git a/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp b/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp index 6851c4e76..21cf492fe 100644 --- a/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp +++ b/libraries/chain/include/graphene/chain/rbac_hardfork_visitor.hpp @@ -17,6 +17,27 @@ namespace graphene int first_allowed_op = operation::tag::value; switch (op_type) { + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + case operation::tag::value: + FC_ASSERT(block_time >= HARDFORK_SON_TIME, "Custom permissions and roles not allowed on this operation yet!"); + break; case operation::tag::value: case operation::tag::value: case operation::tag::value: @@ -45,4 +66,4 @@ namespace graphene }; } // namespace chain -} // namespace graphene \ No newline at end of file +} // namespace graphene From f0437308cba52164183e089a37cc63269effdfb7 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Sun, 11 Oct 2020 10:55:49 +0000 Subject: [PATCH 04/15] hotfix - son max count fix --- libraries/chain/db_maint.cpp | 12 +++++++++--- .../include/graphene/chain/budget_record_object.hpp | 2 ++ .../graphene/chain/protocol/chain_parameters.hpp | 7 +++++-- .../graphene/chain/witness_schedule_object.hpp | 4 ++-- programs/witness_node/genesis.json | 4 ++-- tests/cli/son.cpp | 6 +++--- 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index d23b0fe28..b8908f113 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -653,7 +653,7 @@ void database::update_active_sons() const global_property_object& gpo = get_global_properties(); const chain_parameters& cp = gpo.parameters; - auto sons = sort_votable_objects(cp.maximum_son_count); + auto sons = sort_votable_objects(cp.maximum_son_count()); const auto& all_sons = get_index_type().indices(); @@ -1981,7 +1981,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g d._vote_tally_buffer.resize(props.next_available_vote_id); d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1); d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1); - d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1); + d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count() / 2 + 1); d._total_voting_stake = 0; auto balance_type = vesting_balance_type::normal; @@ -2093,7 +2093,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // same rationale as for witnesses d._committee_count_histogram_buffer[offset] += voting_stake; } - if( opinion_account.options.num_son <= props.parameters.maximum_son_count ) + if( opinion_account.options.num_son <= props.parameters.maximum_son_count() ) { uint16_t offset = std::min(size_t(opinion_account.options.num_son/2), d._son_count_histogram_buffer.size() - 1); @@ -2183,6 +2183,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.son_down_time = p.parameters.extensions.value.son_down_time; if( !p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations.valid() ) p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations = p.parameters.extensions.value.son_bitcoin_min_tx_confirmations; + if( !p.pending_parameters->extensions.value.son_account.valid() ) + p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; + if( !p.pending_parameters->extensions.value.btc_asset.valid() ) + p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; + if( !p.pending_parameters->extensions.value.maximum_son_count.valid() ) + p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); } diff --git a/libraries/chain/include/graphene/chain/budget_record_object.hpp b/libraries/chain/include/graphene/chain/budget_record_object.hpp index 007d46a75..0da71ca5d 100644 --- a/libraries/chain/include/graphene/chain/budget_record_object.hpp +++ b/libraries/chain/include/graphene/chain/budget_record_object.hpp @@ -76,7 +76,9 @@ FC_REFLECT(graphene::chain::budget_record, (total_budget) (witness_budget) (worker_budget) + (son_budget) (leftover_worker_funds) + (leftover_son_funds) (supply_delta) ) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 0dbf04bd0..405704edb 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -68,6 +68,7 @@ namespace graphene { namespace chain { optional < account_id_type > son_account; optional < asset_id_type > btc_asset; + optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS }; struct chain_parameters @@ -86,7 +87,6 @@ namespace graphene { namespace chain { uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members - uint16_t maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have uint16_t reserve_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network @@ -205,6 +205,9 @@ namespace graphene { namespace chain { inline asset_id_type btc_asset() const { return extensions.value.btc_asset.valid() ? *extensions.value.btc_asset : asset_id_type(); } + inline uint16_t maximum_son_count()const { + return extensions.value.maximum_son_count.valid() ? *extensions.value.maximum_son_count : GRAPHENE_DEFAULT_MAX_SONS; + } }; } } // graphene::chain @@ -237,6 +240,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (son_bitcoin_min_tx_confirmations) (son_account) (btc_asset) + (maximum_son_count) ) FC_REFLECT( graphene::chain::chain_parameters, @@ -253,7 +257,6 @@ FC_REFLECT( graphene::chain::chain_parameters, (maximum_asset_feed_publishers) (maximum_witness_count) (maximum_committee_count) - (maximum_son_count) (maximum_authority_membership) (reserve_percent_of_fee) (network_percent_of_fee) diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index b934fd01b..2eff563f9 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -57,7 +57,7 @@ typedef generic_far_future_witness_scheduler< typedef generic_witness_scheduler< /* WitnessID = */ son_id_type, /* RNG = */ witness_scheduler_rng, - /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* CountType = */ decltype( chain_parameters::extensions.value.maximum_son_count )::value_type, /* OffsetType = */ uint32_t, /* debug = */ true > son_scheduler; @@ -65,7 +65,7 @@ typedef generic_witness_scheduler< typedef generic_far_future_witness_scheduler< /* WitnessID = */ son_id_type, /* RNG = */ witness_scheduler_rng, - /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* CountType = */ decltype( chain_parameters::extensions.value.maximum_son_count )::value_type, /* OffsetType = */ uint32_t, /* debug = */ true > far_future_son_scheduler; diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index 192a38794..1e0610f44 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -344,7 +344,6 @@ "maximum_asset_feed_publishers": 10, "maximum_witness_count": 1001, "maximum_committee_count": 1001, - "maximum_son_count": 15, "maximum_authority_membership": 10, "reserve_percent_of_fee": 2000, "network_percent_of_fee": 2000, @@ -392,7 +391,8 @@ "son_pay_time": 86400, "son_deregister_time": 43200, "son_heartbeat_frequency": 180, - "son_down_time": 360 + "son_down_time": 360, + "maximum_son_count": 15 } }, "initial_bts_accounts": [], diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 87febbdde..7b6259d13 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -278,7 +278,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - unsigned int son_number = gpo.parameters.maximum_son_count; + unsigned int son_number = gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; @@ -578,7 +578,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - unsigned int son_number = gpo.parameters.maximum_son_count; + unsigned int son_number = gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; @@ -645,7 +645,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - unsigned int son_number = gpo.parameters.maximum_son_count; + unsigned int son_number = gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; From 71090ca6780bf345ea955d504857211938803d92 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Sun, 11 Oct 2020 15:18:47 +0000 Subject: [PATCH 05/15] init variables --- .../include/graphene/chain/protocol/chain_parameters.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 405704edb..de67aa286 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -65,9 +65,8 @@ namespace graphene { namespace chain { optional < uint32_t > son_heartbeat_frequency = SON_HEARTBEAT_FREQUENCY; optional < uint32_t > son_down_time = SON_DOWN_TIME; optional < uint16_t > son_bitcoin_min_tx_confirmations = SON_BITCOIN_MIN_TX_CONFIRMATIONS; - - optional < account_id_type > son_account; - optional < asset_id_type > btc_asset; + optional < account_id_type > son_account = GRAPHENE_NULL_ACCOUNT; + optional < asset_id_type > btc_asset = asset_id_type(); optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS }; From 2d28c13e7a8e0bbeff7db8375e75c6612efe7202 Mon Sep 17 00:00:00 2001 From: serkixenos <70147861+serkixenos@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:04:35 +0200 Subject: [PATCH 06/15] Update SON HF time to October 20th 2020, 00:00:00 --- libraries/chain/hardfork.d/SON.hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf index d3ecce127..40c99784b 100644 --- a/libraries/chain/hardfork.d/SON.hf +++ b/libraries/chain/hardfork.d/SON.hf @@ -1,5 +1,5 @@ -// SON HARDFORK Saturday, October 17, 2020 00:00:00 UTC +// SON HARDFORK Saturday, October 20, 2020 00:00:00 UTC #ifndef HARDFORK_SON_TIME #include -#define HARDFORK_SON_TIME (fc::time_point_sec( 1602892800 )) +#define HARDFORK_SON_TIME (fc::time_point_sec( 1603152000 )) #endif From fc1a81525012fd03837578ce0e0ba0025bb79ad8 Mon Sep 17 00:00:00 2001 From: serkixenos <70147861+serkixenos@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:14:59 +0200 Subject: [PATCH 07/15] Update SON HF time to October 20th 2020, 00:00:00 --- libraries/chain/hardfork.d/SON.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf index 40c99784b..4bc7496f4 100644 --- a/libraries/chain/hardfork.d/SON.hf +++ b/libraries/chain/hardfork.d/SON.hf @@ -1,4 +1,4 @@ -// SON HARDFORK Saturday, October 20, 2020 00:00:00 UTC +// SON HARDFORK Tuesday, October 20, 2020 00:00:00 UTC #ifndef HARDFORK_SON_TIME #include #define HARDFORK_SON_TIME (fc::time_point_sec( 1603152000 )) From 959474610f1631a001179bd0a258dbe488f88c3d Mon Sep 17 00:00:00 2001 From: serkixenos <70147861+serkixenos@users.noreply.github.com> Date: Fri, 16 Oct 2020 15:12:14 +0200 Subject: [PATCH 08/15] Release build fix, missing includes (#391) fixes https://github.com/peerplays-network/peerplays/issues/390 --- .../bitcoin/bitcoin_address.cpp | 15 ++++++++------- .../peerplays_sidechain_plugin.cpp | 8 ++++---- .../peerplays_sidechain/sidechain_net_handler.cpp | 12 ++++++------ .../sidechain_net_handler_bitcoin.cpp | 10 +++++----- .../sidechain_net_handler_peerplays.cpp | 8 +++++--- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp index 4c10899f7..69b28ee18 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -99,7 +100,7 @@ bool bitcoin_address::is_p2pk() const { parse_hex(address); return true; } - } catch (fc::exception e) { + } catch (fc::exception &e) { return false; } return false; @@ -121,7 +122,7 @@ bool bitcoin_address::is_p2pkh() const { return true; } return false; - } catch (fc::exception e) { + } catch (fc::exception &e) { return false; } } @@ -134,7 +135,7 @@ bool bitcoin_address::is_p2sh() const { return true; } return false; - } catch (fc::exception e) { + } catch (fc::exception &e) { return false; } } @@ -269,7 +270,7 @@ void btc_weighted_multisig_address::create_redeem_script(const std::vectorbroadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending heartbeat failed with exception ${e}", ("e", e.what())); return false; } @@ -468,7 +468,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending approval failed with exception ${e}", ("e", e.what())); return false; } @@ -544,7 +544,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending son down proposal failed with exception ${e}", ("e", e.what())); return false; } @@ -579,7 +579,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what())); return false; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index ed404efe3..c02f8c5dc 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -138,7 +138,7 @@ bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}", ("son_id", son_id)("proposal_id", proposal_id)("e", e.what())); return false; @@ -205,7 +205,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } @@ -247,7 +247,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } @@ -460,7 +460,7 @@ void sidechain_net_handler::process_sidechain_transactions() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what())); } }); @@ -495,7 +495,7 @@ void sidechain_net_handler::send_sidechain_transactions() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what())); } }); @@ -560,7 +560,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); } }); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 48f641c3d..dc5e88afb 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -940,7 +940,7 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain fc::http::connection conn; try { conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip)("port", rpc_port)); FC_ASSERT(false); } @@ -1324,7 +1324,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; } @@ -1375,7 +1375,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); return false; } @@ -1420,7 +1420,7 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); return false; } @@ -1464,7 +1464,7 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); return false; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 3388527a1..ecf3cc00c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -149,7 +151,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending transaction for update deposit address operation failed with exception ${e}", ("e", e.what())); return false; } @@ -203,7 +205,7 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); return false; } @@ -254,7 +256,7 @@ std::string sidechain_net_handler_peerplays::send_sidechain_transaction(const si if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return trx.id().str(); - } catch (fc::exception e) { + } catch (fc::exception &e) { elog("Sidechain transaction failed with exception ${e}", ("e", e.what())); return ""; } From 088b07de66d3a0833512d79aae216aac35140d04 Mon Sep 17 00:00:00 2001 From: Serki Date: Sun, 18 Oct 2020 16:59:59 +0200 Subject: [PATCH 09/15] Fix release build on 18.04, fc::smart_ref_* removed --- libraries/app/api.cpp | 1 - libraries/app/application.cpp | 4 +--- libraries/app/database_api.cpp | 1 - libraries/chain/CMakeLists.txt | 1 + libraries/chain/account_evaluator.cpp | 2 -- libraries/chain/betting_market_evaluator.cpp | 1 - libraries/chain/block_database.cpp | 1 - .../chain/committee_member_evaluator.cpp | 2 -- libraries/chain/confidential_evaluator.cpp | 2 -- libraries/chain/database.cpp | 1 - libraries/chain/db_block.cpp | 1 - libraries/chain/db_getter.cpp | 4 +--- libraries/chain/db_init.cpp | 1 - libraries/chain/db_maint.cpp | 1 - libraries/chain/fork_database.cpp | 1 - libraries/chain/genesis_state.cpp | 1 - .../chain/protocol/chain_parameters.hpp | 5 +++-- .../graphene/chain/protocol/fee_schedule.hpp | 5 ++++- .../include/graphene/chain/protocol/types.hpp | 1 - libraries/chain/market_evaluator.cpp | 1 - libraries/chain/proposal_evaluator.cpp | 2 -- libraries/chain/protocol/chain_parameters.cpp | 8 ++++++++ libraries/chain/protocol/committee_member.cpp | 1 - libraries/chain/protocol/fee_schedule.cpp | 15 -------------- libraries/chain/protocol/operations.cpp | 1 - libraries/chain/protocol/proposal.cpp | 1 - libraries/chain/protocol/small_ops.cpp | 1 - libraries/chain/protocol/transaction.cpp | 1 - libraries/chain/small_objects.cpp | 1 - .../db/include/graphene/db/generic_index.hpp | 2 +- libraries/egenesis/embed_genesis.cpp | 1 - libraries/net/node.cpp | 1 - .../account_history_plugin.cpp | 1 - .../accounts_list/accounts_list_plugin.cpp | 1 - .../affiliate_stats/affiliate_stats_api.cpp | 1 - .../affiliate_stats_plugin.cpp | 1 - libraries/plugins/bookie/bookie_api.cpp | 1 - libraries/plugins/bookie/bookie_plugin.cpp | 1 - libraries/plugins/debug_witness/debug_api.cpp | 1 - .../plugins/debug_witness/debug_witness.cpp | 1 - .../delayed_node/delayed_node_plugin.cpp | 2 -- libraries/plugins/es_objects/es_objects.cpp | 2 -- .../generate_genesis/generate_genesis.cpp | 1 - .../generate_uia_sharedrop_genesis.cpp | 1 - .../market_history/market_history_plugin.cpp | 1 - .../peerplays_sidechain_plugin.cpp | 2 -- .../sidechain_net_handler.cpp | 1 - .../sidechain_net_handler_peerplays.cpp | 1 - libraries/plugins/witness/witness.cpp | 1 - libraries/wallet/wallet.cpp | 10 ++++------ programs/build_helpers/member_enumerator.cpp | 1 - programs/cli_wallet/main.cpp | 1 - programs/genesis_util/genesis_update.cpp | 1 - programs/js_operation_serializer/main.cpp | 10 ---------- programs/size_checker/main.cpp | 1 - tests/app/main.cpp | 1 - tests/benchmarks/genesis_allocation.cpp | 1 - tests/cli/cli_fixture.cpp | 1 - tests/cli/main.cpp | 1 - tests/cli/son.cpp | 1 - tests/common/database_fixture.cpp | 5 ++--- tests/common/database_fixture.hpp | 1 - tests/generate_empty_blocks/main.cpp | 1 - tests/tests/fee_tests.cpp | 20 +++++++++---------- tests/tests/history_api_tests.cpp | 1 - 65 files changed, 35 insertions(+), 114 deletions(-) create mode 100644 libraries/chain/protocol/chain_parameters.cpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 11d39f693..e1baf30f8 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -38,7 +38,6 @@ #include #include -#include #include #include diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index db73124f2..17a254d4d 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -37,8 +37,6 @@ #include #include -#include - #include #include #include @@ -80,7 +78,7 @@ namespace detail { auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key))); genesis_state_type initial_state; - initial_state.initial_parameters.current_fees = fee_schedule::get_default();//->set_all_fees(GRAPHENE_BLOCKCHAIN_PRECISION); + initial_state.initial_parameters.current_fees = std::make_shared(fee_schedule::get_default()); initial_state.initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT; initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() / initial_state.initial_parameters.block_interval * diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index a2c56c594..fbc98aa4b 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 73024f692..0a0e9c56b 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -43,6 +43,7 @@ add_library( graphene_chain protocol/assert.cpp protocol/account.cpp protocol/transfer.cpp + protocol/chain_parameters.cpp protocol/committee_member.cpp protocol/witness.cpp protocol/market.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index a166e7e5c..aa199c84f 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -22,8 +22,6 @@ * THE SOFTWARE. */ -#include - #include #include #include diff --git a/libraries/chain/betting_market_evaluator.cpp b/libraries/chain/betting_market_evaluator.cpp index b40f276a9..1670ef788 100644 --- a/libraries/chain/betting_market_evaluator.cpp +++ b/libraries/chain/betting_market_evaluator.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #define DEFAULT_LOGGER "betting" -#include #include #include diff --git a/libraries/chain/block_database.cpp b/libraries/chain/block_database.cpp index 3dcdcba42..2dd9b7a2f 100644 --- a/libraries/chain/block_database.cpp +++ b/libraries/chain/block_database.cpp @@ -24,7 +24,6 @@ #include #include #include -#include namespace graphene { namespace chain { diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 73d7703b3..01614f994 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -30,8 +30,6 @@ #include #include -#include - namespace graphene { namespace chain { void_result committee_member_create_evaluator::do_evaluate( const committee_member_create_operation& op ) diff --git a/libraries/chain/confidential_evaluator.cpp b/libraries/chain/confidential_evaluator.cpp index 9323d2d9b..9946b492d 100644 --- a/libraries/chain/confidential_evaluator.cpp +++ b/libraries/chain/confidential_evaluator.cpp @@ -29,8 +29,6 @@ #include #include -#include - namespace graphene { namespace chain { void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o ) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 7711f5439..1e124902d 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include "db_balance.cpp" #include "db_bet.cpp" #include "db_block.cpp" diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e8a49199d..c7881906e 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -42,7 +42,6 @@ #include #include -#include namespace { diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 07b96fea6..7588c9fea 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -34,8 +34,6 @@ #include #include -#include - #include #include @@ -68,7 +66,7 @@ const dynamic_global_property_object& database::get_dynamic_global_properties() const fee_schedule& database::current_fee_schedule()const { - return get_global_properties().parameters.current_fees; + return std::ref( *get_global_properties().parameters.current_fees ); } time_point_sec database::head_block_time()const diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index e857ed94a..43ba381d4 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -103,7 +103,6 @@ #include -#include #include #include diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index b8908f113..a64b6d653 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -24,7 +24,6 @@ #include -#include #include #include diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 83d4880c4..1cb4f77bc 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -24,7 +24,6 @@ #include #include #include -#include namespace graphene { namespace chain { fork_database::fork_database() diff --git a/libraries/chain/genesis_state.cpp b/libraries/chain/genesis_state.cpp index 533110125..7907c79e8 100644 --- a/libraries/chain/genesis_state.cpp +++ b/libraries/chain/genesis_state.cpp @@ -25,7 +25,6 @@ #include // these are required to serialize a genesis_state -#include // required for gcc in release mode #include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index de67aa286..9ab9c2dbe 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -22,10 +22,10 @@ * THE SOFTWARE. */ #pragma once +#include #include #include #include -#include #include #include <../hardfork.d/GPOS.hf> @@ -72,8 +72,9 @@ namespace graphene { namespace chain { struct chain_parameters { + chain_parameters(); /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ - smart_ref current_fees; ///< current schedule of fees + std::shared_ptr current_fees; ///< current schedule of fees uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks uint32_t maintenance_interval = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events uint8_t maintenance_skip_slots = GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS; ///< number of block_intervals to skip at maintenance time diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 9baaffc7f..28a7e0528 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #pragma once -#include #include namespace graphene { namespace chain { @@ -84,6 +83,10 @@ namespace graphene { namespace chain { } } // graphene::chain +namespace fc { + template<> struct get_typename> { static const char* name() { return "shared_ptr"; } }; +} + FC_REFLECT_TYPENAME( graphene::chain::fee_parameters ) FC_REFLECT( graphene::chain::fee_schedule, (parameters)(scale) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 436197a72..56a4bb1cd 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 60e076694..4f219565c 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -35,7 +35,6 @@ #include #include -#include namespace graphene { namespace chain { void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index e7d216efb..3ea4a836a 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -32,8 +32,6 @@ #include #include -#include - namespace graphene { namespace chain { struct proposal_operation_hardfork_visitor diff --git a/libraries/chain/protocol/chain_parameters.cpp b/libraries/chain/protocol/chain_parameters.cpp new file mode 100644 index 000000000..5f30edf73 --- /dev/null +++ b/libraries/chain/protocol/chain_parameters.cpp @@ -0,0 +1,8 @@ +#include +#include + +namespace graphene { namespace chain { + chain_parameters::chain_parameters() { + current_fees = std::make_shared(); + } +}} diff --git a/libraries/chain/protocol/committee_member.cpp b/libraries/chain/protocol/committee_member.cpp index 1824870a9..3ce62783a 100644 --- a/libraries/chain/protocol/committee_member.cpp +++ b/libraries/chain/protocol/committee_member.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include #include diff --git a/libraries/chain/protocol/fee_schedule.cpp b/libraries/chain/protocol/fee_schedule.cpp index 6d494e37d..122136eae 100644 --- a/libraries/chain/protocol/fee_schedule.cpp +++ b/libraries/chain/protocol/fee_schedule.cpp @@ -23,17 +23,6 @@ */ #include #include -#include - -namespace fc -{ - // explicitly instantiate the smart_ref, gcc fails to instantiate it in some release builds - //template graphene::chain::fee_schedule& smart_ref::operator=(smart_ref&&); - //template graphene::chain::fee_schedule& smart_ref::operator=(U&&); - //template graphene::chain::fee_schedule& smart_ref::operator=(const smart_ref&); - //template smart_ref::smart_ref(); - //template const graphene::chain::fee_schedule& smart_ref::operator*() const; -} #include @@ -41,10 +30,6 @@ namespace fc namespace graphene { namespace chain { - typedef fc::smart_ref smart_fee_schedule; - - static smart_fee_schedule tmp; - fee_schedule::fee_schedule() { } diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 385e14dcc..99c8a3761 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include #include #include diff --git a/libraries/chain/protocol/proposal.cpp b/libraries/chain/protocol/proposal.cpp index c77e71e4b..f14f057a9 100644 --- a/libraries/chain/protocol/proposal.cpp +++ b/libraries/chain/protocol/proposal.cpp @@ -23,7 +23,6 @@ */ #include #include -#include #include diff --git a/libraries/chain/protocol/small_ops.cpp b/libraries/chain/protocol/small_ops.cpp index 8ab7cf436..cba4b1b73 100644 --- a/libraries/chain/protocol/small_ops.cpp +++ b/libraries/chain/protocol/small_ops.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ -#include #include #include #include diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 98e172380..db1992783 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/libraries/chain/small_objects.cpp b/libraries/chain/small_objects.cpp index a74fa116e..6b8af3d94 100644 --- a/libraries/chain/small_objects.cpp +++ b/libraries/chain/small_objects.cpp @@ -44,7 +44,6 @@ #include #include -#include GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::balance_object ) GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::block_summary_object ) diff --git a/libraries/db/include/graphene/db/generic_index.hpp b/libraries/db/include/graphene/db/generic_index.hpp index 10e5d19ee..fb11d44a3 100644 --- a/libraries/db/include/graphene/db/generic_index.hpp +++ b/libraries/db/include/graphene/db/generic_index.hpp @@ -73,7 +73,7 @@ namespace graphene { namespace chain { [&m, &exc](ObjectType& o) mutable { try { m(o); - } catch (fc::exception e) { + } catch (fc::exception& e) { exc = std::current_exception(); elog("Exception while modifying object: ${e} -- object may be corrupted", ("e", e)); diff --git a/libraries/egenesis/embed_genesis.cpp b/libraries/egenesis/embed_genesis.cpp index 262830801..c2ffd6864 100644 --- a/libraries/egenesis/embed_genesis.cpp +++ b/libraries/egenesis/embed_genesis.cpp @@ -30,7 +30,6 @@ #include #include -#include // required for gcc in release mode #include #include #include diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 0fc61dde9..14e32306d 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -70,7 +70,6 @@ #include #include #include -#include #include #include diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index d1aa2f71d..6eb3ff0c0 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -35,7 +35,6 @@ #include #include -#include #include namespace graphene { namespace account_history { diff --git a/libraries/plugins/accounts_list/accounts_list_plugin.cpp b/libraries/plugins/accounts_list/accounts_list_plugin.cpp index 757891ea9..5a57cc63d 100644 --- a/libraries/plugins/accounts_list/accounts_list_plugin.cpp +++ b/libraries/plugins/accounts_list/accounts_list_plugin.cpp @@ -34,7 +34,6 @@ #include #include -#include #include namespace graphene { namespace accounts_list { diff --git a/libraries/plugins/affiliate_stats/affiliate_stats_api.cpp b/libraries/plugins/affiliate_stats/affiliate_stats_api.cpp index d37a03600..159f66999 100644 --- a/libraries/plugins/affiliate_stats/affiliate_stats_api.cpp +++ b/libraries/plugins/affiliate_stats/affiliate_stats_api.cpp @@ -24,7 +24,6 @@ #include #include -#include #include diff --git a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp index da9c8a04b..03b546593 100644 --- a/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp +++ b/libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp @@ -35,7 +35,6 @@ #include #include -#include #include namespace graphene { namespace affiliate_stats { diff --git a/libraries/plugins/bookie/bookie_api.cpp b/libraries/plugins/bookie/bookie_api.cpp index 838c038c8..9caa9c625 100644 --- a/libraries/plugins/bookie/bookie_api.cpp +++ b/libraries/plugins/bookie/bookie_api.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index 261de241d..5bc31f14c 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -36,7 +36,6 @@ #include -#include #include #include diff --git a/libraries/plugins/debug_witness/debug_api.cpp b/libraries/plugins/debug_witness/debug_api.cpp index 823755f59..43ffd6cd3 100644 --- a/libraries/plugins/debug_witness/debug_api.cpp +++ b/libraries/plugins/debug_witness/debug_api.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include diff --git a/libraries/plugins/debug_witness/debug_witness.cpp b/libraries/plugins/debug_witness/debug_witness.cpp index 66ef2f58e..7268006d3 100644 --- a/libraries/plugins/debug_witness/debug_witness.cpp +++ b/libraries/plugins/debug_witness/debug_witness.cpp @@ -28,7 +28,6 @@ #include -#include #include #include diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 3eadda344..71de7db5a 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -30,8 +30,6 @@ #include #include #include -#include - namespace graphene { namespace delayed_node { namespace bpo = boost::program_options; diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index b9083cbc5..5fd891070 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -24,8 +24,6 @@ #include -#include - #include #include #include diff --git a/libraries/plugins/generate_genesis/generate_genesis.cpp b/libraries/plugins/generate_genesis/generate_genesis.cpp index 337b42553..88da427e1 100644 --- a/libraries/plugins/generate_genesis/generate_genesis.cpp +++ b/libraries/plugins/generate_genesis/generate_genesis.cpp @@ -29,7 +29,6 @@ #include -#include #include #include diff --git a/libraries/plugins/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.cpp b/libraries/plugins/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.cpp index d6db850ac..05f7c9b34 100644 --- a/libraries/plugins/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.cpp +++ b/libraries/plugins/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.cpp @@ -29,7 +29,6 @@ #include -#include #include #include diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index 80876a48f..48c215f0c 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -34,7 +34,6 @@ #include #include -#include namespace graphene { namespace market_history { diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 25234aedf..dcd5e401f 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include #include #include diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index c02f8c5dc..49c5f0224 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index ecf3cc00c..6037fabd1 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 6ef7798b9..1e7a9d6ed 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -30,7 +30,6 @@ #include -#include #include #include diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 4b44ad6e2..98dc122c6 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -84,7 +84,6 @@ #include #include #include -#include #ifndef WIN32 # include @@ -662,10 +661,10 @@ class wallet_api_impl return ob.template as( GRAPHENE_MAX_NESTED_OBJECTS ); } - void set_operation_fees( signed_transaction& tx, const fee_schedule& s ) + void set_operation_fees( signed_transaction& tx, std::shared_ptr s ) { for( auto& op : tx.operations ) - s.set_fee(op); + s->set_fee(op); } variant info() const @@ -1307,8 +1306,7 @@ class wallet_api_impl tx.operations.push_back( account_create_op ); - auto current_fees = _remote_db->get_global_properties().parameters.current_fees; - set_operation_fees( tx, current_fees ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); vector paying_keys = registrar_account_object.active.get_keys(); @@ -3496,7 +3494,7 @@ class wallet_api_impl new_fees.scale = scale; chain_parameters new_params = current_params; - new_params.current_fees = new_fees; + new_params.current_fees = std::make_shared(new_fees); committee_member_update_global_parameters_operation update_op; update_op.new_parameters = new_params; diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp index 915d7edf4..d956af388 100644 --- a/programs/build_helpers/member_enumerator.cpp +++ b/programs/build_helpers/member_enumerator.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include using namespace graphene::chain; diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index fda7f22dd..bb7afa1a9 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/programs/genesis_util/genesis_update.cpp b/programs/genesis_util/genesis_update.cpp index e753b8b71..4df7091f3 100644 --- a/programs/genesis_util/genesis_update.cpp +++ b/programs/genesis_util/genesis_update.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 34b861b18..797a32a1e 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -55,7 +55,6 @@ #include #include -#include #include using namespace graphene::chain; @@ -128,7 +127,6 @@ struct js_name> template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; -template struct js_name< fc::smart_ref > { static std::string name(){ return js_name::name(); } }; template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } }; template struct js_name< fc::flat_set > { static std::string name(){ return "set " + js_name::name(); } }; template struct js_name< std::vector > { static std::string name(){ return "array " + js_name::name(); } }; @@ -256,14 +254,6 @@ struct serializer,false> static void generate() {} }; -template -struct serializer,false> -{ - static void init() { - serializer::init(); } - static void generate() {} -}; - template<> struct serializer,false> { diff --git a/programs/size_checker/main.cpp b/programs/size_checker/main.cpp index de071cfcf..188e88e40 100644 --- a/programs/size_checker/main.cpp +++ b/programs/size_checker/main.cpp @@ -23,7 +23,6 @@ */ #include -#include #include #include diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 28b8a1fc6..bd58a518b 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include diff --git a/tests/benchmarks/genesis_allocation.cpp b/tests/benchmarks/genesis_allocation.cpp index a17a16fa8..63e75db56 100644 --- a/tests/benchmarks/genesis_allocation.cpp +++ b/tests/benchmarks/genesis_allocation.cpp @@ -26,7 +26,6 @@ #include #include -#include #include diff --git a/tests/cli/cli_fixture.cpp b/tests/cli/cli_fixture.cpp index 5b5fd7ad8..6148ccaab 100644 --- a/tests/cli/cli_fixture.cpp +++ b/tests/cli/cli_fixture.cpp @@ -12,7 +12,6 @@ #include #include -#include #ifdef _WIN32 #ifndef _WIN32_WINNT diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 36fb626cb..c6b587e82 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -24,7 +24,6 @@ #include "cli_fixture.hpp" #include -#include #include diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 7b6259d13..e5f09faae 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -23,7 +23,6 @@ */ #include "cli_fixture.hpp" -#include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index b5ce1f559..d1c8a2b62 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -48,7 +48,6 @@ #include #include -#include #include #include @@ -712,7 +711,7 @@ void database_fixture::change_fees( new_fees.scale = new_scale; chain_parameters new_chain_params = current_chain_params; - new_chain_params.current_fees = new_fees; + new_chain_params.current_fees = std::make_shared(new_fees); db.modify(db.get_global_properties(), [&](global_property_object& p) { p.parameters = new_chain_params; @@ -1039,7 +1038,7 @@ void database_fixture::enable_fees() { db.modify(global_property_id_type()(db), [](global_property_object& gpo) { - gpo.parameters.current_fees = fee_schedule::get_default(); + gpo.parameters.current_fees = std::make_shared(fee_schedule::get_default()); }); } diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index a190b9c6a..366e707ec 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include diff --git a/tests/generate_empty_blocks/main.cpp b/tests/generate_empty_blocks/main.cpp index 1960a1514..298b03cd6 100644 --- a/tests/generate_empty_blocks/main.cpp +++ b/tests/generate_empty_blocks/main.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 964948991..74ae896cb 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ -#include #include #include @@ -334,7 +333,7 @@ BOOST_AUTO_TEST_CASE( cashback_test ) upgrade_to_lifetime_member(rog_id); BOOST_TEST_MESSAGE("Enable fees"); - const auto& fees = db.get_global_properties().parameters.current_fees; + const auto& fees = *db.get_global_properties().parameters.current_fees; #define CustomRegisterActor(actor_name, registrar_name, referrer_name, referrer_rate) \ { \ @@ -346,7 +345,7 @@ BOOST_AUTO_TEST_CASE( cashback_test ) op.options.memo_key = actor_name ## _private_key.get_public_key(); \ op.active = authority(1, public_key_type(actor_name ## _private_key.get_public_key()), 1); \ op.owner = op.active; \ - op.fee = fees->calculate_fee(op); \ + op.fee = fees.calculate_fee(op); \ trx.operations = {op}; \ sign( trx, registrar_name ## _private_key ); \ actor_name ## _id = PUSH_TX( db, trx ).operation_results.front().get(); \ @@ -372,10 +371,10 @@ BOOST_AUTO_TEST_CASE( cashback_test ) CustomAuditActor( pleb ); \ } - int64_t reg_fee = fees->get< account_create_operation >().premium_fee; - int64_t xfer_fee = fees->get< transfer_operation >().fee; - int64_t upg_an_fee = fees->get< account_upgrade_operation >().membership_annual_fee; - int64_t upg_lt_fee = fees->get< account_upgrade_operation >().membership_lifetime_fee; + int64_t reg_fee = fees.get< account_create_operation >().premium_fee; + int64_t xfer_fee = fees.get< transfer_operation >().fee; + int64_t upg_an_fee = fees.get< account_upgrade_operation >().membership_annual_fee; + int64_t upg_lt_fee = fees.get< account_upgrade_operation >().membership_lifetime_fee; // all percentages here are cut from whole pie! uint64_t network_pct = 20 * P1; uint64_t lt_pct = 375 * P100 / 1000; @@ -582,7 +581,7 @@ BOOST_AUTO_TEST_CASE( account_create_fee_scaling ) auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale; db.modify(global_property_id_type()(db), [](global_property_object& gpo) { - gpo.parameters.current_fees = fee_schedule::get_default(); + gpo.parameters.current_fees = std::make_shared(fee_schedule::get_default()); gpo.parameters.current_fees->get().basic_fee = 1; }); @@ -1004,8 +1003,9 @@ BOOST_AUTO_TEST_CASE( issue_429_test ) // make sure the database requires our fee to be nonzero enable_fees(); - auto fees_to_pay = db.get_global_properties().parameters.current_fees->get(); - + const auto& fees = *db.get_global_properties().parameters.current_fees; + auto fees_to_pay = fees.get(); + { signed_transaction tx; asset_create_operation op; diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp index 8b8a1a517..f3b46fe70 100644 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -30,7 +30,6 @@ #include "../common/database_fixture.hpp" -#include #include using namespace graphene::app; From 7a721f8094e264417aa8519ca90e68f2b23aeebc Mon Sep 17 00:00:00 2001 From: serkixenos <70147861+serkixenos@users.noreply.github.com> Date: Sun, 18 Oct 2020 19:18:59 +0200 Subject: [PATCH 10/15] Gitlab will build Debug and Release versions --- .gitlab-ci.yml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1403ba24e..2d27e0919 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,15 +17,24 @@ build: - git submodule update --init --recursive - rm -rf build - mkdir build - - cd build - - cmake .. + - mkdir build/debug + - mkdir build/release + - cd build/debug + - cmake -DCMAKE_BUILD_TYPE=Debug ../.. + - make -j$(nproc) + - cd ../.. + - cd build/release + - cmake -DCMAKE_BUILD_TYPE=Release ../.. - make -j$(nproc) artifacts: untracked: true paths: - - build/libraries/ - - build/programs/ - - build/tests/ + - build/debug/libraries/ + - build/debug/programs/ + - build/debug/tests/ + - build/release/libraries/ + - build/release/programs/ + - build/release/tests/ tags: - builder @@ -34,8 +43,11 @@ test: dependencies: - build script: - - ./build/tests/betting_test --log_level=message - - ./build/tests/chain_test --log_level=message - - ./build/tests/cli_test --log_level=message + - ./build/debug/tests/betting_test --log_level=message + - ./build/debug/tests/chain_test --log_level=message + - ./build/debug/tests/cli_test --log_level=message + - ./build/release/tests/betting_test --log_level=message + - ./build/release/tests/chain_test --log_level=message + - ./build/release/tests/cli_test --log_level=message tags: - builder From 854cc412b3584e57466a390dec075c21c621adc9 Mon Sep 17 00:00:00 2001 From: Serki Date: Sun, 18 Oct 2020 23:11:26 +0200 Subject: [PATCH 11/15] Revert "Gitlab will build Debug and Release versions" This reverts commit 7a721f8094e264417aa8519ca90e68f2b23aeebc. --- .gitlab-ci.yml | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d27e0919..1403ba24e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,24 +17,15 @@ build: - git submodule update --init --recursive - rm -rf build - mkdir build - - mkdir build/debug - - mkdir build/release - - cd build/debug - - cmake -DCMAKE_BUILD_TYPE=Debug ../.. - - make -j$(nproc) - - cd ../.. - - cd build/release - - cmake -DCMAKE_BUILD_TYPE=Release ../.. + - cd build + - cmake .. - make -j$(nproc) artifacts: untracked: true paths: - - build/debug/libraries/ - - build/debug/programs/ - - build/debug/tests/ - - build/release/libraries/ - - build/release/programs/ - - build/release/tests/ + - build/libraries/ + - build/programs/ + - build/tests/ tags: - builder @@ -43,11 +34,8 @@ test: dependencies: - build script: - - ./build/debug/tests/betting_test --log_level=message - - ./build/debug/tests/chain_test --log_level=message - - ./build/debug/tests/cli_test --log_level=message - - ./build/release/tests/betting_test --log_level=message - - ./build/release/tests/chain_test --log_level=message - - ./build/release/tests/cli_test --log_level=message + - ./build/tests/betting_test --log_level=message + - ./build/tests/chain_test --log_level=message + - ./build/tests/cli_test --log_level=message tags: - builder From a9afe474a21c35b80bc7fb522778cd5a37747667 Mon Sep 17 00:00:00 2001 From: Serki Date: Sun, 18 Oct 2020 23:12:39 +0200 Subject: [PATCH 12/15] Gitlab will build Release version only --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1403ba24e..da6baf86e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,7 +18,7 @@ build: - rm -rf build - mkdir build - cd build - - cmake .. + - cmake -DCMAKE_BUILD_TYPE=Release .. - make -j$(nproc) artifacts: untracked: true From f394b54cdd929a6ebcac9c2f0d1d751cc86b537c Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Tue, 20 Oct 2020 06:16:17 +0000 Subject: [PATCH 13/15] add safe copy --- .../chain/protocol/chain_parameters.hpp | 6 ++ libraries/chain/protocol/chain_parameters.cpp | 84 +++++++++++++++++++ tests/betting/betting_tests.cpp | 5 +- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 9ab9c2dbe..c24a05760 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -73,6 +73,10 @@ namespace graphene { namespace chain { struct chain_parameters { chain_parameters(); + chain_parameters(const chain_parameters& other); + chain_parameters(chain_parameters&& other); + chain_parameters& operator=(const chain_parameters& other); + chain_parameters& operator=(chain_parameters&& other); /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ std::shared_ptr current_fees; ///< current schedule of fees uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks @@ -208,6 +212,8 @@ namespace graphene { namespace chain { inline uint16_t maximum_son_count()const { return extensions.value.maximum_son_count.valid() ? *extensions.value.maximum_son_count : GRAPHENE_DEFAULT_MAX_SONS; } + private: + static void safe_copy(chain_parameters& to, const chain_parameters& from); }; } } // graphene::chain diff --git a/libraries/chain/protocol/chain_parameters.cpp b/libraries/chain/protocol/chain_parameters.cpp index 5f30edf73..a904b0e31 100644 --- a/libraries/chain/protocol/chain_parameters.cpp +++ b/libraries/chain/protocol/chain_parameters.cpp @@ -5,4 +5,88 @@ namespace graphene { namespace chain { chain_parameters::chain_parameters() { current_fees = std::make_shared(); } + + // copy constructor + chain_parameters::chain_parameters(const chain_parameters& other) + { + current_fees = std::make_shared(*other.current_fees); + safe_copy(*this, other); + } + + // copy assignment + chain_parameters& chain_parameters::operator=(const chain_parameters& other) + { + if (&other != this) + { + current_fees = std::make_shared(*other.current_fees); + safe_copy(*this, other); + } + return *this; + } + + // copies the easy stuff + void chain_parameters::safe_copy(chain_parameters& to, const chain_parameters& from) + { + to.block_interval = from.block_interval; + to.maintenance_interval = from.maintenance_interval; + to.maintenance_skip_slots = from.maintenance_skip_slots; + to.committee_proposal_review_period = from.committee_proposal_review_period; + to.maximum_transaction_size = from.maximum_transaction_size; + to.maximum_block_size = from.maximum_block_size; + to.maximum_time_until_expiration = from.maximum_time_until_expiration; + to.maximum_proposal_lifetime = from.maximum_proposal_lifetime; + to.maximum_asset_whitelist_authorities = from.maximum_asset_whitelist_authorities; + to.maximum_asset_feed_publishers = from.maximum_asset_feed_publishers; + to.maximum_witness_count = from.maximum_witness_count; + to.maximum_committee_count = from.maximum_committee_count; + to.maximum_authority_membership = from.maximum_authority_membership; + to.reserve_percent_of_fee = from.reserve_percent_of_fee; + to.network_percent_of_fee = from.network_percent_of_fee; + to.lifetime_referrer_percent_of_fee = from.lifetime_referrer_percent_of_fee; + to.cashback_vesting_period_seconds = from.cashback_vesting_period_seconds; + to.cashback_vesting_threshold = from.cashback_vesting_threshold; + to.count_non_member_votes = from.count_non_member_votes; + to.allow_non_member_whitelists = from.allow_non_member_whitelists; + to.witness_pay_per_block = from.witness_pay_per_block; + to.witness_pay_vesting_seconds = from.witness_pay_vesting_seconds; + to.worker_budget_per_day = from.worker_budget_per_day; + to.max_predicate_opcode = from.max_predicate_opcode; + to.fee_liquidation_threshold = from.fee_liquidation_threshold; + to.accounts_per_fee_scale = from.accounts_per_fee_scale; + to.account_fee_scale_bitshifts = from.account_fee_scale_bitshifts; + to.max_authority_depth = from.max_authority_depth; + to.witness_schedule_algorithm= from.witness_schedule_algorithm; + to.min_round_delay = from.min_round_delay; + to.max_round_delay = from.max_round_delay; + to.min_time_per_commit_move = from.min_time_per_commit_move; + to.max_time_per_commit_move = from.max_time_per_commit_move; + to.min_time_per_reveal_move = from.min_time_per_reveal_move; + to.max_time_per_reveal_move = from.max_time_per_reveal_move; + to.rake_fee_percentage = from.rake_fee_percentage; + to.maximum_registration_deadline = from.maximum_registration_deadline; + to.maximum_players_in_tournament = from.maximum_players_in_tournament; + to.maximum_tournament_whitelist_length = from.maximum_tournament_whitelist_length; + to.maximum_tournament_start_time_in_future= from.maximum_tournament_start_time_in_future; + to.maximum_tournament_start_delay = from.maximum_tournament_start_delay; + to.maximum_tournament_number_of_wins = from.maximum_tournament_number_of_wins; + to.extensions = from.extensions; + } + + // move constructor + chain_parameters::chain_parameters(chain_parameters&& other) + { + current_fees = std::move(other.current_fees); + safe_copy(*this, other); + } + + // move assignment + chain_parameters& chain_parameters::operator=(chain_parameters&& other) + { + if (&other != this) + { + current_fees = std::move(other.current_fees); + safe_copy(*this, other); + } + return *this; + } }} diff --git a/tests/betting/betting_tests.cpp b/tests/betting/betting_tests.cpp index 4befe25ca..7dff8b823 100644 --- a/tests/betting/betting_tests.cpp +++ b/tests/betting/betting_tests.cpp @@ -1877,10 +1877,11 @@ BOOST_AUTO_TEST_CASE(event_group_delete_test_not_existed_event_group) try { CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0); + event_group_id_type nhl_id = nhl.id; + delete_event_group(nhl_id); - delete_event_group(nhl.id); - BOOST_CHECK_THROW(delete_event_group(nhl.id), fc::exception); + BOOST_CHECK_THROW(delete_event_group(nhl_id), fc::exception); } FC_LOG_AND_RETHROW() } From e23637a01f18d5fa62f713a41393be4bc82710af Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Thu, 22 Oct 2020 12:02:46 +0000 Subject: [PATCH 14/15] verify_transaction proper value --- libraries/chain/protocol/transaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index db1992783..340ef86eb 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -445,7 +445,7 @@ void signed_transaction::verify_authority( bool ignore_custom_operation_required_auths, uint32_t max_recursion )const { try { - graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion ); + graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, ignore_custom_operation_required_auths, max_recursion ); } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // graphene::chain From 00a6fc8d23af14415dc6e206f3d429200ab68dfb Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Thu, 22 Oct 2020 14:55:37 +0000 Subject: [PATCH 15/15] proper init of publick_key --- libraries/chain/protocol/transaction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 340ef86eb..4c0684bd6 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -105,6 +105,7 @@ void transaction::get_required_authorities( flat_set& active, +const flat_set empty_keyset; struct sign_state { @@ -230,7 +231,7 @@ struct sign_state sign_state( const flat_set& sigs, const std::function& a, - const flat_set& keys = flat_set() ) + const flat_set& keys = empty_keyset ) :get_active(a),available_keys(keys) { for( const auto& key : sigs )