diff --git a/genesis.json b/genesis.json deleted file mode 100644 index 13a96210..00000000 --- a/genesis.json +++ /dev/null @@ -1,646 +0,0 @@ -{ - "initial_timestamp": "2017-05-24T08:09:05", - "max_core_supply": "1446053259451", - "initial_parameters": { - "current_fees": { - "parameters": [[ - 0,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 1,{ - "fee": 50 - } - ],[ - 2,{ - "fee": 0 - } - ],[ - 3,{ - "fee": 500000000000 - } - ],[ - 4,{} - ],[ - 5,{ - "basic_fee": 1000, - "premium_fee": 1000000, - "price_per_kbyte": 1000 - } - ],[ - 6,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 7,{ - "fee": 3000 - } - ],[ - 8,{ - "membership_annual_fee": 500000000000, - "membership_lifetime_fee": 500000 - } - ],[ - 9,{ - "fee": 200000 - } - ],[ - 10,{ - "symbol3": "500000000000", - "symbol4": "500000000000", - "long_symbol": 5000000, - "price_per_kbyte": 1000 - } - ],[ - 11,{ - "fee": 100000, - "price_per_kbyte": 1000 - } - ],[ - 12,{ - "fee": 500000000000 - } - ],[ - 13,{ - "fee": 500000000000 - } - ],[ - 14,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 15,{ - "fee": 1000 - } - ],[ - 16,{ - "fee": 3000 - } - ],[ - 17,{ - "fee": 500000000000 - } - ],[ - 18,{ - "fee": 500000000000 - } - ],[ - 19,{ - "fee": 500000000000 - } - ],[ - 20,{ - "fee": 1000000 - } - ],[ - 21,{ - "fee": 2000 - } - ],[ - 22,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 23,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 24,{ - "fee": 0 - } - ],[ - 25,{ - "fee": 3000 - } - ],[ - 26,{ - "fee": 200 - } - ],[ - 27,{ - "fee": 200, - "price_per_kbyte": 1000 - } - ],[ - 28,{ - "fee": 0 - } - ],[ - 29,{ - "fee": 100000 - } - ],[ - 30,{ - "fee": 2000 - } - ],[ - 31,{ - "fee": 0 - } - ],[ - 32,{ - "fee": 100000 - } - ],[ - 33,{ - "fee": 50000 - } - ],[ - 34,{ - "fee": 500000000000 - } - ],[ - 35,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 36,{ - "fee": 1000 - } - ],[ - 37,{} - ],[ - 38,{ - "fee": 20000, - "price_per_kbyte": 1000 - } - ],[ - 39,{ - "fee": 500000000000, - "price_per_output": 500000000000 - } - ],[ - 40,{ - "fee": 500000000000, - "price_per_output": 500000000000 - } - ],[ - 41,{ - "fee": 500000000000 - } - ],[ - 42,{} - ],[ - 43,{ - "fee": 3000 - } - ],[ - 44,{} - ],[ - 45,{ - "fee": 1000 - } - ],[ - 46,{ - "fee": 5000 - } - ],[ - 47,{ - "fee": 0 - } - ],[ - 48,{ - "fee": 1000 - } - ],[ - 49,{} - ] - ], - "scale": 10000 - }, - "block_interval": 3, - "maintenance_interval": 600, - "maintenance_skip_slots": 3, - "committee_proposal_review_period": 3600, - "maximum_transaction_size": 98304, - "maximum_block_size": 2097192, - "maximum_time_until_expiration": 86400, - "maximum_proposal_lifetime": 2419200, - "maximum_asset_whitelist_authorities": 10, - "maximum_asset_feed_publishers": 10, - "maximum_witness_count": 1001, - "maximum_committee_count": 1001, - "maximum_authority_membership": 10, - "reserve_percent_of_fee": 10000, - "network_percent_of_fee": 10000, - "lifetime_referrer_percent_of_fee": 0, - "cashback_vesting_period_seconds": 9999999, - "cashback_vesting_threshold": 500000000000, - "count_non_member_votes": true, - "allow_non_member_whitelists": true, - "witness_pay_per_block": 700, - "worker_budget_per_day": "0", - "max_predicate_opcode": 1, - "fee_liquidation_threshold": 500000000000, - "accounts_per_fee_scale": 1000, - "account_fee_scale_bitshifts": 0, - "max_authority_depth": 2, - "witness_schedule_algorithm": 0, - "min_round_delay": 1, - "max_round_delay": 300, - "min_time_per_commit_move": 15, - "max_time_per_commit_move": 15, - "min_time_per_reveal_move": 6, - "max_time_per_reveal_move": 6, - "rake_fee_percentage": 350, - "maximum_registration_deadline": 2592000, - "maximum_players_in_tournament": 256, - "maximum_tournament_whitelist_length": 1000, - "maximum_tournament_start_time_in_future": 5184000, - "maximum_tournament_start_delay": 259200, - "maximum_tournament_number_of_wins": 25, - "extensions": [] - }, - "initial_bts_accounts": [ - { - "name": "bts-1", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY7uWdZSmuLioy2X3QrDdYbFYRkrJkkk1brWYpKgz1k9vMFFDL1u", 1]] - }, - "active_authority": { - "key_auths": [["PPY7uWdZSmuLioy2X3QrDdYbFYRkrJkkk1brWYpKgz1k9vMFFDL1u", 1]] - } - }, - { - "name": "bts-2", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY85SpcwFd8xP9ocJRSUHckFMkrqp6TExDqtrsheNWvFWszZYo7X", 1]] - }, - "active_authority": { - "key_auths": [["PPY85SpcwFd8xP9ocJRSUHckFMkrqp6TExDqtrsheNWvFWszZYo7X", 1]] - } - }, - { - "name": "bts-3", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY6dqcLw6GNgyKDBqgU52TzZ6zvsxisP5nMKSs2J8WLJR5wLPyDc", 1]] - }, - "active_authority": { - "key_auths": [["PPY6dqcLw6GNgyKDBqgU52TzZ6zvsxisP5nMKSs2J8WLJR5wLPyDc", 1]] - } - }, - { - "name": "bts-4", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY574FdeqJjGv3GXd1YPdJnLWQk61PYPDzHJcKPZuAT2BDdP9A86", 1]] - }, - "active_authority": { - "key_auths": [["PPY574FdeqJjGv3GXd1YPdJnLWQk61PYPDzHJcKPZuAT2BDdP9A86", 1]] - } - }, - { - "name": "bts-5", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY8jx67bhMkfsZo554sR3tiVW5Ujkt91kfUcmPK54MqWcYdFgm74", 1]] - }, - "active_authority": { - "key_auths": [["PPY8jx67bhMkfsZo554sR3tiVW5Ujkt91kfUcmPK54MqWcYdFgm74", 1]] - } - }, - { - "name": "bts-6", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY6fsh8fQLvtxP1pDSYRT26PvZFaNnnLxsnbVXU3bLGvUMzy94rB", 1], - ["PPY716a2CfM86Vg6dg9jY83rognjzUoekSoCuQZn66kFiu8RD7i4w", 1]] - }, - "active_authority": { - "key_auths": [["PPY6fsh8fQLvtxP1pDSYRT26PvZFaNnnLxsnbVXU3bLGvUMzy94rB", 1], - ["PPY716a2CfM86Vg6dg9jY83rognjzUoekSoCuQZn66kFiu8RD7i4w", 1]] - } - }, - { - "name": "bts-7", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY5M3sbj817awumetoGmyUrpACbdsKBemJNHbRr4dheLxdLjd1te", 1], - ["PPY8Rg7jbF7FDPCrBjvXP9CUH2GBSWDJyh1P91PHszjsZqL73YBSy", 1]] - }, - "active_authority": { - "key_auths": [["PPY5M3sbj817awumetoGmyUrpACbdsKBemJNHbRr4dheLxdLjd1te", 1], - ["PPY8Rg7jbF7FDPCrBjvXP9CUH2GBSWDJyh1P91PHszjsZqL73YBSy", 1]] - } - }, - { - "name": "bts-8", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY8bFWUv8QWod9h5CoR78BYDPrWxpRnqbG9t9ncjwfaCigZfumHy", 1], - ["PPY8Gzi7xENM4EohQDRvySSPawN1raGhg1j3t86MvL8W3QrN7jKFX", 1]] - }, - "active_authority": { - "key_auths": [["PPY8bFWUv8QWod9h5CoR78BYDPrWxpRnqbG9t9ncjwfaCigZfumHy", 1], - ["PPY8Gzi7xENM4EohQDRvySSPawN1raGhg1j3t86MvL8W3QrN7jKFX", 1]] - } - }, - { - "name": "bts-9", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY4xuGsVMzznyvWams8XvemFHdgg9iP7BxKzqftbeyxc4qMqv18u", 1], - ["PPY7YZyxogivxKVcHe3UW8v4qeM5FryTsM3YmqJiXnML82Y7RigtY", 1]] - }, - "active_authority": { - "key_auths": [["PPY4xuGsVMzznyvWams8XvemFHdgg9iP7BxKzqftbeyxc4qMqv18u", 1], - ["PPY7YZyxogivxKVcHe3UW8v4qeM5FryTsM3YmqJiXnML82Y7RigtY", 1]] - } - }, - { - "name": "bts-10", - "core_balance": 10000000, - "owner_authority": { - "key_auths": [["PPY7nLR7Y9KybkikF5VfvW2s2NwEh4M7DGG3tktbQGtYJNkEzTpcG", 1], - ["PPY7eq1gy1oGW73AR5Y2HWz3GVuusPBTKcMVsoHEpwaihHoc6FC8x", 1]] - }, - "active_authority": { - "key_auths": [["PPY7nLR7Y9KybkikF5VfvW2s2NwEh4M7DGG3tktbQGtYJNkEzTpcG", 1], - ["PPY7eq1gy1oGW73AR5Y2HWz3GVuusPBTKcMVsoHEpwaihHoc6FC8x", 1]] - } - } - ], - "initial_accounts": [{ - "name": "init0", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init1", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init2", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init3", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init4", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init5", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init6", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init7", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init8", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init9", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init10", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "testnet1", - "owner_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - }, - - { - "name": "bal1", - "owner_key": "PPY6UYXNtsYN4rdSLu4hNgoVNer42h6QAiYTzo8irMQ5TcnSAM7nf", - "active_key": "PPY6UYXNtsYN4rdSLu4hNgoVNer42h6QAiYTzo8irMQ5TcnSAM7nf", - "is_lifetime_member": true - }, - { - "name": "bal2", - "owner_key": "PPY7v8ujQ5Jr2wqVYwkV2HFN2j9uXYSuxPoruLwc1BDHMoNUFLuoy", - "active_key": "PPY7v8ujQ5Jr2wqVYwkV2HFN2j9uXYSuxPoruLwc1BDHMoNUFLuoy", - "is_lifetime_member": true - }, - { - "name": "bal3", - "owner_key": "PPY7bHmq9MdL86c798ZX8rBv63Vj3UEyM7bhJebt2NW6X1CtpnvZ1", - "active_key": "PPY7bHmq9MdL86c798ZX8rBv63Vj3UEyM7bhJebt2NW6X1CtpnvZ1", - "is_lifetime_member": true - }, - { - "name": "bal4", - "owner_key": "PPY5ECEkU8pVPYxEsauYdzsVjd5EHadGfhmgXWtWwFYB63TpmjZH9", - "active_key": "PPY5ECEkU8pVPYxEsauYdzsVjd5EHadGfhmgXWtWwFYB63TpmjZH9", - "is_lifetime_member": true - }, - { - "name": "bal5", - "owner_key": "PPY8TMhq6b1rNVVMoowNzifXQctU48XtqoCENRGvCgXwRvvGjeaxx", - "active_key": "PPY8TMhq6b1rNVVMoowNzifXQctU48XtqoCENRGvCgXwRvvGjeaxx", - "is_lifetime_member": true - }, - { - "name": "bal6", - "owner_key": "PPY8PoqAsCGMx56F2tjFgAKyy7Cx4nShnWaqvScytUFfeyK1Mbva7", - "active_key": "PPY8PoqAsCGMx56F2tjFgAKyy7Cx4nShnWaqvScytUFfeyK1Mbva7", - "is_lifetime_member": true - }, - { - "name": "bal7", - "owner_key": "PPY6roVqWT5D1jNpEuWeBm5mRZAw7EoCgkXtT9unDqRSBiyUTaGk1", - "active_key": "PPY6roVqWT5D1jNpEuWeBm5mRZAw7EoCgkXtT9unDqRSBiyUTaGk1", - "is_lifetime_member": true - }, - { - "name": "bal8", - "owner_key": "PPY6hkoqreUEr7JuUjZnKHiwTpruAnh3v4FTEA2stvnzgcS76qn8E", - "active_key": "PPY6hkoqreUEr7JuUjZnKHiwTpruAnh3v4FTEA2stvnzgcS76qn8E", - "is_lifetime_member": true - }, - { - "name": "bal9", - "owner_key": "PPY5ogXvz1sdxV2gMgEs4Bnhb84dBML4Zto8yCGxLDqLnho1axjs8", - "active_key": "PPY5ogXvz1sdxV2gMgEs4Bnhb84dBML4Zto8yCGxLDqLnho1axjs8", - "is_lifetime_member": true - }, - { - "name": "bal10", - "owner_key": "PPY6UvHq2Bdnk8imjnd7LYrbMZrXEoFDC5pjJ6wphifdqf6twNMb8", - "active_key": "PPY6UvHq2Bdnk8imjnd7LYrbMZrXEoFDC5pjJ6wphifdqf6twNMb8", - "is_lifetime_member": true - } - ], - "initial_assets": [], - "initial_balances": [{ - "owner": "PPYFAbAx7yuxt725qSZvfwWqkdCwp9ZnUama", - "asset_symbol": "PPY2T", - "amount": "100000000000" - },{ - "owner": "PPYKdXdwpLonR1tzdLPC9KN75wikAD3vUwcd", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPY3qHup5gAshs6SBeNvExcVZj5UT542y71q", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPY97cZPDbQtVWeHHpohdd6vEWCuHkT699S8", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPYEuVv6Au277VXHb9htmwAvJvXD8M6aAXKV", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPYEQwfsiZRioNYKFhpxJhAB16uYguP5ykES", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPYZA1GZca2TFrazRaAvgPp1W73bQFMmyzp", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPYNvMhLLnVjiVCaFKmvMhx3hP54FaTBgXWM", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPY2TVnFbsdYLxjSEg5kiAc7dqupL8huVJhP", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPYPvAShbMSgVeW9PYxDQBBSQfGk1zRnUz3F", - "asset_symbol": "PPY2T", - "amount": "10000000" - },{ - "owner": "PPY3HqXEv9hfqS2pZkYRb8nG6JqAmpj6W66M", - "asset_symbol": "PPY2T", - "amount": "10000000" - } - ], - "initial_vesting_balances": [ - { - "owner": "PPYKdXdwpLonR1tzdLPC9KN75wikAD3vUwcd", - "asset_symbol": "PPY2T", - "amount": "10000000", - "begin_timestamp": "2017-05-24T12:00:00", - "vesting_duration_seconds": "7200", - "begin_balance": "10000000" - },{ - "owner": "PPY3qHup5gAshs6SBeNvExcVZj5UT542y71q", - "asset_symbol": "PPY2T", - "amount": "10000000", - "begin_timestamp": "2017-05-23T12:00:00", - "vesting_duration_seconds": "28800", - "begin_balance": "10000000" - },{ - "owner": "PPY97cZPDbQtVWeHHpohdd6vEWCuHkT699S8", - "asset_symbol": "PPY2T", - "amount": "10000000", - "begin_timestamp": "2017-05-23T12:00:00", - "vesting_duration_seconds": "50400", - "begin_balance": "10000000" - },{ - "owner": "PPYEuVv6Au277VXHb9htmwAvJvXD8M6aAXKV", - "asset_symbol": "PPY2T", - "amount": "10000000", - "begin_timestamp": "2017-05-23T12:00:00", - "vesting_duration_seconds": "72000", - "begin_balance": "10000000" - },{ - "owner": "PPYQ1R7C1zJHkgTkBJ7VSdNUQTE4kYVAHVbJ", - "asset_symbol": "PPY2T", - "amount": "10000000", - "begin_timestamp": "2017-05-23T12:00:00", - "vesting_duration_seconds": "86400", - "begin_balance": "10000000" - } - ], - "initial_active_witnesses": 11, - "initial_witness_candidates": [{ - "owner_name": "init0", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init1", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init2", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init3", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init4", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init5", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init6", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init7", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init8", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init9", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init10", - "block_signing_key": "PPY6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - } - ], - "initial_committee_candidates": [{ - "owner_name": "init0" - },{ - "owner_name": "init1" - },{ - "owner_name": "init2" - },{ - "owner_name": "init3" - },{ - "owner_name": "init4" - },{ - "owner_name": "init5" - },{ - "owner_name": "init6" - },{ - "owner_name": "init7" - },{ - "owner_name": "init8" - },{ - "owner_name": "init9" - } - ], - "initial_worker_candidates": [], - "initial_chain_id": "aa34045518f1469a28fa4578240d5f039afa9959c0b95ce3b39674efa691fb21", - "immutable_parameters": { - "min_committee_member_count": 9, - "min_witness_count": 11, - "num_special_accounts": 0, - "num_special_assets": 0 - } -} diff --git a/genesis/genesis.json b/genesis/genesis.json deleted file mode 100644 index 340503c2..00000000 --- a/genesis/genesis.json +++ /dev/null @@ -1,8828 +0,0 @@ -{ - "initial_timestamp": "2017-05-30T08:09:05", - "max_core_supply": "546053259451", - "initial_parameters": { - "current_fees": { - "parameters": [[ - 0,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 1,{ - "fee": 50 - } - ],[ - 2,{ - "fee": 0 - } - ],[ - 3,{ - "fee": 500000000000 - } - ],[ - 4,{} - ],[ - 5,{ - "basic_fee": 1000, - "premium_fee": 1000000, - "price_per_kbyte": 1000 - } - ],[ - 6,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 7,{ - "fee": 3000 - } - ],[ - 8,{ - "membership_annual_fee": 500000000000, - "membership_lifetime_fee": 500000 - } - ],[ - 9,{ - "fee": 200000 - } - ],[ - 10,{ - "symbol3": "500000000000", - "symbol4": "500000000000", - "long_symbol": 5000000, - "price_per_kbyte": 1000 - } - ],[ - 11,{ - "fee": 100000, - "price_per_kbyte": 1000 - } - ],[ - 12,{ - "fee": 500000000000 - } - ],[ - 13,{ - "fee": 500000000000 - } - ],[ - 14,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 15,{ - "fee": 1000 - } - ],[ - 16,{ - "fee": 3000 - } - ],[ - 17,{ - "fee": 500000000000 - } - ],[ - 18,{ - "fee": 500000000000 - } - ],[ - 19,{ - "fee": 500000000000 - } - ],[ - 20,{ - "fee": 1000000 - } - ],[ - 21,{ - "fee": 2000 - } - ],[ - 22,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 23,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 24,{ - "fee": 0 - } - ],[ - 25,{ - "fee": 3000 - } - ],[ - 26,{ - "fee": 200 - } - ],[ - 27,{ - "fee": 200, - "price_per_kbyte": 1000 - } - ],[ - 28,{ - "fee": 0 - } - ],[ - 29,{ - "fee": 100000 - } - ],[ - 30,{ - "fee": 2000 - } - ],[ - 31,{ - "fee": 0 - } - ],[ - 32,{ - "fee": 100000 - } - ],[ - 33,{ - "fee": 50000 - } - ],[ - 34,{ - "fee": 500000000000 - } - ],[ - 35,{ - "fee": 1000, - "price_per_kbyte": 1000 - } - ],[ - 36,{ - "fee": 1000 - } - ],[ - 37,{} - ],[ - 38,{ - "fee": 20000, - "price_per_kbyte": 1000 - } - ],[ - 39,{ - "fee": 500000000000, - "price_per_output": 500000000000 - } - ],[ - 40,{ - "fee": 500000000000, - "price_per_output": 500000000000 - } - ],[ - 41,{ - "fee": 500000000000 - } - ],[ - 42,{} - ],[ - 43,{ - "fee": 3000 - } - ],[ - 44,{} - ],[ - 45,{ - "fee": 1000 - } - ],[ - 46,{ - "fee": 5000 - } - ],[ - 47,{ - "fee": 0 - } - ],[ - 48,{ - "fee": 1000 - } - ],[ - 49,{} - ] - ], - "scale": 10000 - }, - "block_interval": 3, - "maintenance_interval": 3600, - "maintenance_skip_slots": 3, - "committee_proposal_review_period": 3600, - "maximum_transaction_size": 98304, - "maximum_block_size": 2097192, - "maximum_time_until_expiration": 86400, - "maximum_proposal_lifetime": 2419200, - "maximum_asset_whitelist_authorities": 10, - "maximum_asset_feed_publishers": 10, - "maximum_witness_count": 1001, - "maximum_committee_count": 1001, - "maximum_authority_membership": 10, - "reserve_percent_of_fee": 10000, - "network_percent_of_fee": 10000, - "lifetime_referrer_percent_of_fee": 0, - "cashback_vesting_period_seconds": 9999999, - "cashback_vesting_threshold": 500000000000, - "count_non_member_votes": true, - "allow_non_member_whitelists": true, - "witness_pay_per_block": 1000000, - "worker_budget_per_day": "0", - "max_predicate_opcode": 1, - "fee_liquidation_threshold": 500000000000, - "accounts_per_fee_scale": 1000, - "account_fee_scale_bitshifts": 0, - "max_authority_depth": 2, - "witness_schedule_algorithm": 1, - "min_round_delay": 1, - "max_round_delay": 300, - "min_time_per_commit_move": 15, - "max_time_per_commit_move": 15, - "min_time_per_reveal_move": 6, - "max_time_per_reveal_move": 6, - "rake_fee_percentage": 350, - "maximum_registration_deadline": 2592000, - "maximum_players_in_tournament": 256, - "maximum_tournament_whitelist_length": 1000, - "maximum_tournament_start_time_in_future": 5184000, - "maximum_tournament_start_delay": 259200, - "maximum_tournament_number_of_wins": 25, - "extensions": [] - }, - "initial_bts_accounts": [], - "initial_accounts": [{ - "name": "init0", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init1", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init2", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init3", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init4", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init5", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init6", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init7", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init8", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init9", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "init10", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": true - },{ - "name": "nathan", - "owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", - "is_lifetime_member": false - } - ], - "initial_assets": [], - "initial_balances": [ - { - "owner": "BTS51B1EWJZQsJhZQnbiF1j2adGPrDufNXbw", - "asset_symbol": "PPY", - "amount": "9469332" - },{ - "owner": "BTSLYoTtsy48awZzvY6n5iDHkV4iZGEcvss8", - "asset_symbol": "PPY", - "amount": "19954525" - },{ - "owner": "BTS75DDNdontbe3Ama3arfCwApoj9tWv12Rn", - "asset_symbol": "PPY", - "amount": "1028562" - },{ - "owner": "BTSBNMyx8bMZEUNaxyy721BifKwwq48YktS8", - "asset_symbol": "PPY", - "amount": "1970271" - },{ - "owner": "BTSFELQom3v38HiB5HnWEvoxvZYrq3CwaDZU", - "asset_symbol": "PPY", - "amount": "979214" - },{ - "owner": "BTSPQLzyJdHpV6XZz67qrhAuQ3zFWL2Ehza5", - "asset_symbol": "PPY", - "amount": "326201" - },{ - "owner": "BTS9A2v442nFBrnf37BVK7JuYh9hsAaXgEJx", - "asset_symbol": "PPY", - "amount": "141588" - },{ - "owner": "BTS6rcQGDJfU6TEHBJu6bToviDxHTvSdgW5f", - "asset_symbol": "PPY", - "amount": "5020568" - },{ - "owner": "BTS9MCRkr1Ny3C61tE8QUrARbHoRZ9ifAAqg", - "asset_symbol": "PPY", - "amount": "206622" - },{ - "owner": "BTS4bXhEirxXmX34QrFanayjiLLNjw1uGvH1", - "asset_symbol": "PPY", - "amount": "9305262" - },{ - "owner": "BTSBZwhjW73bKistg9RLHDmYkGovwCKzdmcQ", - "asset_symbol": "PPY", - "amount": "9511260" - },{ - "owner": "BTS3maV9c1FGu4cpXx7GBYTA1fUkusVyH1Bf", - "asset_symbol": "PPY", - "amount": "168404739" - },{ - "owner": "BTS96PxfwnDuLuqHm2FTTqjoKu9zgCRG5sGw", - "asset_symbol": "PPY", - "amount": "2130842" - },{ - "owner": "BTSPc8ECe8AWomSmhrbCmizj7fPzUH9cZjPu", - "asset_symbol": "PPY", - "amount": "9886879" - },{ - "owner": "BTSMHczuKKGf7NzBjdtCUHRfYX1kXfUq4nyj", - "asset_symbol": "PPY", - "amount": "1601620" - },{ - "owner": "BTSLz4rdZ3uXR1y8pRcx5mAxo29q7xM1M4R1", - "asset_symbol": "PPY", - "amount": "9316910" - },{ - "owner": "BTSLiqWPXyV1PzY4exUv85xSzuxdPgV6oatw", - "asset_symbol": "PPY", - "amount": "205066" - },{ - "owner": "BTSPMGSiSs6Wp2S6De4VZA56JmSNsC9a6XRz", - "asset_symbol": "PPY", - "amount": "1552830" - },{ - "owner": "BTS35heJ58QRicz3gYFkvc6N7Ew4ZLb2SaGU", - "asset_symbol": "PPY", - "amount": "225992034" - },{ - "owner": "BTS7n7sB7x7knVAUGo4zeLDfKvA5a3QQJmvX", - "asset_symbol": "PPY", - "amount": "6680471" - },{ - "owner": "BTSJJJhYdwMQ8PkiMtvQ5tQcaZcyPq7gh3sb", - "asset_symbol": "PPY", - "amount": "6888127" - },{ - "owner": "BTSKnsKbXfGbCuS5QiXaPYo6ehZKbGi6sSpJ", - "asset_symbol": "PPY", - "amount": "8823849" - },{ - "owner": "BTSEwr7j6sTpxHwqdacjNy2A9W6vcfyduHWV", - "asset_symbol": "PPY", - "amount": "1814197" - },{ - "owner": "BTS2hM8wM1grV9vu5YzhA2GD5KGoVDaTP3ty", - "asset_symbol": "PPY", - "amount": "10157435" - },{ - "owner": "BTS5kuj85HQ1V8qw4HvfuBrS1p95YKmsPsCy", - "asset_symbol": "PPY", - "amount": "6105272" - },{ - "owner": "BTS4LY3ZDFeVK8oSDLXDhxfnnnQyqDEF2dFv", - "asset_symbol": "PPY", - "amount": "6479240" - },{ - "owner": "BTSCTNeow9CYciBRjb54kffVk7XpBnNfBPbg", - "asset_symbol": "PPY", - "amount": "23539937" - },{ - "owner": "BTS65A7oVDTDqLYNNY2myziPVQC9HWge6gxs", - "asset_symbol": "PPY", - "amount": "242890919" - },{ - "owner": "BTS978Y3o8KSHz4L3wLeL4dwMpAm3n8hLJpC", - "asset_symbol": "PPY", - "amount": "4050870" - },{ - "owner": "BTSEvA7ASWk4fVRcUdbxse1yjvPxTdJ9Debs", - "asset_symbol": "PPY", - "amount": "1920074" - },{ - "owner": "BTS94XXhaGYMZoaZDuUJBSvQQBZzbx7Eg5jY", - "asset_symbol": "PPY", - "amount": "1316616" - },{ - "owner": "BTSDAQuS12un7UZ1QDySiyCjZHPc69Lsm3jF", - "asset_symbol": "PPY", - "amount": "6666099" - },{ - "owner": "BTSG2MHKrWX2fHYQjJJLRveKQ7sc7GjEpGLw", - "asset_symbol": "PPY", - "amount": "441986127" - },{ - "owner": "BTSKHp151fSidDbSLhVAiLiNuLL1EZcjvrp2", - "asset_symbol": "PPY", - "amount": "10436685" - },{ - "owner": "BTS6NkLkD4RSTXCbwREtJxJpJXm889VfrcWN", - "asset_symbol": "PPY", - "amount": "31124707" - },{ - "owner": "BTSGGJQAzA3NmnRo8z4wujRBAYFAi2buUhAU", - "asset_symbol": "PPY", - "amount": "766137" - },{ - "owner": "BTSKaicwkJXhHD3tjFD4aVVgLDLo18WUvCNC", - "asset_symbol": "PPY", - "amount": "76202915" - },{ - "owner": "BTSMVWDbBK9XXkNViZJ8hiCmU6TqxrfCXdjy", - "asset_symbol": "PPY", - "amount": "34361333" - },{ - "owner": "BTSNSsCnvMw1xrZJhTgQVgPkybZiRcyD18V8", - "asset_symbol": "PPY", - "amount": "1910866" - },{ - "owner": "BTS4bZdy4WuqjWzT5N6CqmC86usHxcxGUSsV", - "asset_symbol": "PPY", - "amount": "9967426" - },{ - "owner": "BTS4P5aE2hzePkWr7t4FGXVh98J7GZd4sw3v", - "asset_symbol": "PPY", - "amount": "286751" - },{ - "owner": "BTSNBbWFWxEgfthVaYsKHy2Yy4fBbW57ARao", - "asset_symbol": "PPY", - "amount": "23689143" - },{ - "owner": "BTSNDjH8V7AQkr7SRwPfy1VoTGfiqungTTm8", - "asset_symbol": "PPY", - "amount": "662558" - },{ - "owner": "BTSKg4qz2hW2uteN8yjWK3iLiDUFhJogje8s", - "asset_symbol": "PPY", - "amount": "264885" - },{ - "owner": "BTSFZx44yu2BPXZdeRwGoEvphn9PbbPuSJcz", - "asset_symbol": "PPY", - "amount": "3832396" - },{ - "owner": "BTStNZavztHF6sWur9ZLyyAzZTuCjPGDBPV", - "asset_symbol": "PPY", - "amount": "61056706" - },{ - "owner": "BTSDTHU98JHjnanmHwkf1xBYbBkk63Ei1C9U", - "asset_symbol": "PPY", - "amount": "3802596" - },{ - "owner": "BTS9Lg4W7kuXcGQczzDyLkVxkiBzzdbaoQ1D", - "asset_symbol": "PPY", - "amount": "7647493" - },{ - "owner": "BTS9o4spSuTg4Rczrcma5nka6iPpohM9wuVh", - "asset_symbol": "PPY", - "amount": "1744340" - },{ - "owner": "BTSKg6V6vCTpPg7Yyau2MVMP5X4pSBpUS6mQ", - "asset_symbol": "PPY", - "amount": "849224" - },{ - "owner": "BTS2nG9YHw4d7JHrr8V1qp9wnnm6jx7mxdSh", - "asset_symbol": "PPY", - "amount": "58729473" - },{ - "owner": "BTSDrDHDyzZcyex56s57fumLBxe27o3s2eJo", - "asset_symbol": "PPY", - "amount": "2501036" - },{ - "owner": "BTSQKDDTr6rets6paEYiMGAMESfWgufXmjwh", - "asset_symbol": "PPY", - "amount": "301785" - },{ - "owner": "BTSPNSnC3zHAkfvgzXaVrrpxs1U8Hs6mwXe5", - "asset_symbol": "PPY", - "amount": "9373376" - },{ - "owner": "BTSE6hWCZapBFhK6hKTuLePWmkQQzHiunNg8", - "asset_symbol": "PPY", - "amount": "3360295" - },{ - "owner": "BTS2Pruus53MXfYEwgdgGpdke5ARyTj51qd4", - "asset_symbol": "PPY", - "amount": "8851645" - },{ - "owner": "BTSNF4JvrrN8wfk6UqjGHgLHE8rNvLc4SrFZ", - "asset_symbol": "PPY", - "amount": "29163078" - },{ - "owner": "BTSGQS7a5Dwex8JqbxG9vckdQNHHr7M2XVKQ", - "asset_symbol": "PPY", - "amount": "2561479" - },{ - "owner": "BTS4fw7gKgqPmYRqsMjc7tjBFazAGmPudaz7", - "asset_symbol": "PPY", - "amount": "1043942" - },{ - "owner": "BTS5qrY3tZAeS1WPiwE7xBYbL5vKQZy8Uca6", - "asset_symbol": "PPY", - "amount": "1307910" - },{ - "owner": "BTSP34nALmU5QoiKjSkeq8X23weRum26zd7r", - "asset_symbol": "PPY", - "amount": "1016953" - },{ - "owner": "BTSD9CZSe8tm961avQvecLVVHkFAA3eK5eGU", - "asset_symbol": "PPY", - "amount": "1957265" - },{ - "owner": "BTSKzRTkAMRQk8aCpXXoT8CeGpyiPdAK4xPg", - "asset_symbol": "PPY", - "amount": "4001864" - },{ - "owner": "BTSN87o8fNf3ZmJyr1twTZV8EuGQ29ALWPyq", - "asset_symbol": "PPY", - "amount": "269868" - },{ - "owner": "BTSJCZcJQuxBiEuZ56TgZ4sXMYyYQ97yS8HN", - "asset_symbol": "PPY", - "amount": "234375" - },{ - "owner": "BTSHCdzmLEoXUAHaKAhq8tcZuzjzzqytYNJ9", - "asset_symbol": "PPY", - "amount": "5327513" - },{ - "owner": "BTS3dh7X5X7x6wzR1W6vW8xYV4nhEYeWaZeP", - "asset_symbol": "PPY", - "amount": "574437" - },{ - "owner": "BTSFXxm25gkfZm2UVDszyjpoAR7MZ61M5VcP", - "asset_symbol": "PPY", - "amount": "10493929" - },{ - "owner": "BTSBz1rj2LNTF5SWaUaGoSiLUkmwbBHEuFAA", - "asset_symbol": "PPY", - "amount": "718683" - },{ - "owner": "BTShd4hiezsQwMzfDPz7fS18Xs5SpQJFAjD", - "asset_symbol": "PPY", - "amount": "65903543" - },{ - "owner": "BTSGmjhfE5BmEufPEJtQymuZB48AnmxbSoWJ", - "asset_symbol": "PPY", - "amount": "57853313" - },{ - "owner": "BTSF7KCWqCknVgtkjSo2u3vLuA5WuPB63ZWc", - "asset_symbol": "PPY", - "amount": "992507" - },{ - "owner": "BTSFUd1X75ykhdxWJyBokZHaLor7MoeXfdxd", - "asset_symbol": "PPY", - "amount": "515664" - },{ - "owner": "BTSRpBXPRod9nar8SaVc61vqwa9fsqY2PGb", - "asset_symbol": "PPY", - "amount": "2074836" - },{ - "owner": "BTS8NeAbMi8wTVK6pxNghQ1cz6S5DqkGhFh9", - "asset_symbol": "PPY", - "amount": "889413" - },{ - "owner": "BTSLncJMFmzrnMHwywQmCx28rsD8iQU1Ehpr", - "asset_symbol": "PPY", - "amount": "19412799" - },{ - "owner": "BTSMWTRuKtWvRcy8QAZpfHacxGDywfqk6xUc", - "asset_symbol": "PPY", - "amount": "1049427" - },{ - "owner": "BTS6AHR9WBMgfoTsAXHuyxCyL5bSDoyHj6Am", - "asset_symbol": "PPY", - "amount": "35099685" - },{ - "owner": "BTSLpRSe73SBT3158duJFgcsTRTPWiKS8xhZ", - "asset_symbol": "PPY", - "amount": "11800680" - },{ - "owner": "BTSJkLZLYkY2BX9rHYcfJXSwTtrkButKoxmQ", - "asset_symbol": "PPY", - "amount": "159827994" - },{ - "owner": "BTSQ7ScezrrLnT51EDLDnuM4mUobFV8yMdS2", - "asset_symbol": "PPY", - "amount": "33068127" - },{ - "owner": "BTSF1Jq34jEhU6vVB8RPo94AqnouiQ86CxXP", - "asset_symbol": "PPY", - "amount": "006959" - },{ - "owner": "BTS268vAvdWaeY7EyVqTHjb9Qpdc9QL6rGPP", - "asset_symbol": "PPY", - "amount": "031311" - },{ - "owner": "BTSKLmV4ACHnfY1uc7aaU2C55UkR5dGKK6PW", - "asset_symbol": "PPY", - "amount": "10307147" - },{ - "owner": "BTSNHHcg2C7Fgph9rGLUYGPLeygqPvcpKdfz", - "asset_symbol": "PPY", - "amount": "165778" - },{ - "owner": "BTSFEJ2tQqE7chFU3fGz9sJZ2XQJL9AvWmrr", - "asset_symbol": "PPY", - "amount": "5004531" - },{ - "owner": "BTSHT6kdPEUgbS61ntsuQiu1yPRJmy8HD9xu", - "asset_symbol": "PPY", - "amount": "175278" - },{ - "owner": "BTSJfayAnxcs5C8JvumUhTCqk9DYGrwYkAxc", - "asset_symbol": "PPY", - "amount": "4302716" - },{ - "owner": "BTSD1jAQgUaLnDWe6eu14ufrcqi9jz6CSwFc", - "asset_symbol": "PPY", - "amount": "6628762" - },{ - "owner": "BTSMt4t725G6GKnkEiEFQUEz34D36RTtTp6Z", - "asset_symbol": "PPY", - "amount": "3820341" - },{ - "owner": "BTSK7PNbka7v2jsjA3oojpsNAHJje9LLEYCz", - "asset_symbol": "PPY", - "amount": "9777540" - },{ - "owner": "BTS994e7d1SiCUmp2MQ1tPyBX1mLd92kdsSs", - "asset_symbol": "PPY", - "amount": "206299707" - },{ - "owner": "BTSF7YKetL4wd71brRwdWypHUntczKs4tdPj", - "asset_symbol": "PPY", - "amount": "034359" - },{ - "owner": "BTSLnm9YLfGU5KJnvaA4sK99kpAZzNUFssvE", - "asset_symbol": "PPY", - "amount": "15919561" - },{ - "owner": "BTS89FumgZ7hd2j2YsiZ2KrmBenycSPTYk6u", - "asset_symbol": "PPY", - "amount": "356998000" - },{ - "owner": "BTS6NxUJpousMrd3sid7soiUdKdWRTeYk5JP", - "asset_symbol": "PPY", - "amount": "31770535" - },{ - "owner": "BTSCSWdzDc14VRjdthk3qEG3cGwK3Jo1mkPT", - "asset_symbol": "PPY", - "amount": "2642540" - },{ - "owner": "BTSFghLAVQvb8PJZ13gkAxoJYwcuTVWwWDv4", - "asset_symbol": "PPY", - "amount": "1983648" - },{ - "owner": "BTS771gUrrs2ErGP1pigFDr23KFHf3aEEbT2", - "asset_symbol": "PPY", - "amount": "15839597" - },{ - "owner": "BTSKAr3u1De2REHZpXYEgHkKnUDm696v3hCd", - "asset_symbol": "PPY", - "amount": "1891444" - },{ - "owner": "BTSP3K6no7ooN6WTJDp6vSPGgfYCQ7w3hM7r", - "asset_symbol": "PPY", - "amount": "17390000" - },{ - "owner": "BTSPEnGuwTJjAKNRXNjMDmWMwR8mMTf7ykWK", - "asset_symbol": "PPY", - "amount": "3195287" - },{ - "owner": "BTS6qHGfkSzij7rJTj69bd12pLxHQXcNktRF", - "asset_symbol": "PPY", - "amount": "9910570" - },{ - "owner": "BTSDZ8FhEdP2e1P1JvaPsWNxq7yrAs6kDpCi", - "asset_symbol": "PPY", - "amount": "4186717" - },{ - "owner": "BTSDhsRjjLtroVPuyJEYP39pPmKDLnbiARF2", - "asset_symbol": "PPY", - "amount": "040493" - },{ - "owner": "BTS66d9PRn6zNkw2axtHw7EdimY82Gk67Vwh", - "asset_symbol": "PPY", - "amount": "1169989" - },{ - "owner": "BTS3zUkMCH15PA5MjRJB9DuNfSUvEa5UGs2B", - "asset_symbol": "PPY", - "amount": "16589642" - },{ - "owner": "BTS2aY9p8HXXGMCwAHQCKp8SNt93qtFgnR31", - "asset_symbol": "PPY", - "amount": "32492776" - },{ - "owner": "BTSouKk9o9rtXjHMmUjGFMeu5WudYkHkDXg", - "asset_symbol": "PPY", - "amount": "3389844" - },{ - "owner": "BTSHu5CUmsQhhY9M642ZQFRGcPDfeQjcstbP", - "asset_symbol": "PPY", - "amount": "3923801" - },{ - "owner": "BTS6FwEbe9MYaMFMibcQTWeC4PETc8xeHxTG", - "asset_symbol": "PPY", - "amount": "598314" - },{ - "owner": "BTS6ArYtkooq1ajR112tbNBvwKDCJT6uUxy2", - "asset_symbol": "PPY", - "amount": "66358356" - },{ - "owner": "BTSEgT3jbiUqyRs1mcupAx1zvnMdiWkNqgsk", - "asset_symbol": "PPY", - "amount": "18850679" - },{ - "owner": "BTSPa14bn6GEGZG1hqpaU4upDhw356ndAyzS", - "asset_symbol": "PPY", - "amount": "478648626" - },{ - "owner": "BTSBx8bRcvEewrEfMQXD9UfYfJArkR45pZhh", - "asset_symbol": "PPY", - "amount": "8955305" - },{ - "owner": "BTSKrJLvhEEnWzAPPRtcL9oDGTmvNpfKLdtq", - "asset_symbol": "PPY", - "amount": "23927453" - },{ - "owner": "BTS2G37wYSr2n11uDLZDUXQ3XQnw9GhyQoR9", - "asset_symbol": "PPY", - "amount": "104316273" - },{ - "owner": "BTSPwc8UdcStpuWoAqSVcrPieRuPAhNP9GCM", - "asset_symbol": "PPY", - "amount": "699357" - },{ - "owner": "BTS57QLvWdY9pXn8b6HidWF8NJqeQpNxox1A", - "asset_symbol": "PPY", - "amount": "4443609" - },{ - "owner": "BTSF41Z4D3RdGTfFCcDQW4q6H1h3bPurt9qt", - "asset_symbol": "PPY", - "amount": "102771" - },{ - "owner": "BTS4qkRoNVQtU5JmS7WyrDaBNAmQQjGBcX7y", - "asset_symbol": "PPY", - "amount": "6872267" - },{ - "owner": "BTSNxfxQciUD2L3cc6pSzbfMxyxmHBzwR2SQ", - "asset_symbol": "PPY", - "amount": "385938" - },{ - "owner": "BTSJAMGu1e6ZG1Da2h4uweRU4BnPQAMfQfr3", - "asset_symbol": "PPY", - "amount": "26936360" - },{ - "owner": "BTSDKn5XNN5cKiVnWCvfT9kwxbJygkSBGrZw", - "asset_symbol": "PPY", - "amount": "347581" - },{ - "owner": "BTSEuUF5Mc7biCvBGQFXwWwegk1aKMP7Q2YN", - "asset_symbol": "PPY", - "amount": "19634691" - },{ - "owner": "BTSDP6iw8Aqz9AztJTVSYPpJypeveBUte1Ug", - "asset_symbol": "PPY", - "amount": "33347518" - },{ - "owner": "BTS2RWWtAw8cwTSnLzGwpXynDBqZ6mKh3eHk", - "asset_symbol": "PPY", - "amount": "057129" - },{ - "owner": "BTSGc8H6M1BPcpeNMLUCocw9MbZzT829PvaW", - "asset_symbol": "PPY", - "amount": "175312" - },{ - "owner": "BTSAwcHoy565gkJxFCb4RH3rYhWW23YyNyrp", - "asset_symbol": "PPY", - "amount": "5026931" - },{ - "owner": "BTSFNntmzMELgreynH59wUGRcpYUC3nT2pJN", - "asset_symbol": "PPY", - "amount": "17511784" - },{ - "owner": "BTS98Hei4CmAcPk1duwAUYhy2rcVY7KndgGL", - "asset_symbol": "PPY", - "amount": "1015437" - },{ - "owner": "BTSDGwNokD4R6EfhkX7UAiiFPHUZZPusd8Ad", - "asset_symbol": "PPY", - "amount": "060045" - },{ - "owner": "BTSGYe8Ln1uXUrs9Emd5e7tfxDXwrGoarC8Q", - "asset_symbol": "PPY", - "amount": "204858" - },{ - "owner": "BTSJZbZuFAo9XPBedu5LWC67HPDHZyXVZnr9", - "asset_symbol": "PPY", - "amount": "411655" - },{ - "owner": "BTS6iF94znHtm8WdQBNmucggTuUaGXpqPNqt", - "asset_symbol": "PPY", - "amount": "102027000" - },{ - "owner": "BTS3QghsG78Hs92je5mxnUNh4JnSekEnKGDt", - "asset_symbol": "PPY", - "amount": "5799968" - },{ - "owner": "BTSDzshKBfxtCS6CzhqGWdMfqNJ7W1yjhPmN", - "asset_symbol": "PPY", - "amount": "2759025" - },{ - "owner": "BTSEKVgwXPtnw2uVqK8Rk1PMENkSGp7ZuY52", - "asset_symbol": "PPY", - "amount": "6407661" - },{ - "owner": "BTSC8AgN9et3pkM2vX1skxfooDNvuAwgAW5d", - "asset_symbol": "PPY", - "amount": "25670837" - },{ - "owner": "BTS4FAufHF7Jx8ezDNh2wXNUYip7TNuk6Xpr", - "asset_symbol": "PPY", - "amount": "246800" - },{ - "owner": "BTSN5M9HFkzJBoN296rdSQ4gr7gQYpGCxQxp", - "asset_symbol": "PPY", - "amount": "20509242" - },{ - "owner": "BTSBLtjMhXX54y41yUbUgSNZjYWvuDGTCefq", - "asset_symbol": "PPY", - "amount": "7980210" - },{ - "owner": "BTSDtAnPrTziTDLkHjBEiQeJMEKi7esatN3F", - "asset_symbol": "PPY", - "amount": "16766634" - },{ - "owner": "BTSHXErGzkKoMgM3A8UkQ5bSMH9SxyyMsGJg", - "asset_symbol": "PPY", - "amount": "897612" - },{ - "owner": "BTSnF4HES9NACSCUJb64yG6BF2JF3wiyz8W", - "asset_symbol": "PPY", - "amount": "405881" - },{ - "owner": "BTSHaVC6uEwWsSCiWPbNF5Jis6yK3WLZMfjz", - "asset_symbol": "PPY", - "amount": "19163867" - },{ - "owner": "BTS4qdKFHfWKn1NV8qvZm78hPZHLyMZRieUp", - "asset_symbol": "PPY", - "amount": "1569492" - },{ - "owner": "BTSPj9ic4V5udE2pthmeDE7mHVPHW3QfrJpA", - "asset_symbol": "PPY", - "amount": "1975295" - },{ - "owner": "BTSEhyj5we3RHnyuypPpM9qFRSBzRnJDRktG", - "asset_symbol": "PPY", - "amount": "7099326" - },{ - "owner": "BTS3CBYNP9W3xobjU5FxiNXYLpgptFB4h9Uo", - "asset_symbol": "PPY", - "amount": "13748531" - },{ - "owner": "BTSM4dr8LwbZDsXEHHYHTEU3SuZywwjLi4BK", - "asset_symbol": "PPY", - "amount": "30172078" - },{ - "owner": "BTSJ9Ld8jLcab6cbvYYB3zJV4xyKGmoSE4Fy", - "asset_symbol": "PPY", - "amount": "16837857" - },{ - "owner": "BTSGFXBMztTV8mqogSDAnybwV5fkbfb8TZbp", - "asset_symbol": "PPY", - "amount": "034092" - },{ - "owner": "BTS9npPxrkp1oiWLML2MhMLnzDxTaXVxurK4", - "asset_symbol": "PPY", - "amount": "25095112" - },{ - "owner": "BTSU44n3BF3emzg7PReKKceS4Y3RkaFG9v2", - "asset_symbol": "PPY", - "amount": "2812466" - },{ - "owner": "BTS2HWw4uzV9Q1XNHZHF1SBrDWbL4xUT5RjF", - "asset_symbol": "PPY", - "amount": "1696401" - },{ - "owner": "BTS9C6Mkh4tQCXnFToXopEE82VVRTadcdeWx", - "asset_symbol": "PPY", - "amount": "020166" - },{ - "owner": "BTSAbr5yH3ZTPqCMU5HzAwC2dk6RrvoL7ZEr", - "asset_symbol": "PPY", - "amount": "1483978" - },{ - "owner": "BTSKsoXGJNzNy3zVkypcgD7yuyNrDMBJRLPq", - "asset_symbol": "PPY", - "amount": "8949082" - },{ - "owner": "BTSGPYE72zHmqdwfDvi9eP3rUWLLTBa7R9Vp", - "asset_symbol": "PPY", - "amount": "3429697" - },{ - "owner": "BTSLVSrv6iHKYmttXpJVgVoT35zcJeGs9Qwd", - "asset_symbol": "PPY", - "amount": "2010293" - },{ - "owner": "BTS9GhkLA1eBR4HfHANUY5EBTw8GtauEZFsb", - "asset_symbol": "PPY", - "amount": "101248305" - },{ - "owner": "BTSMiH2pTVVhrqzsciy2JGuWWjELYQW9Rjfo", - "asset_symbol": "PPY", - "amount": "9752137" - },{ - "owner": "BTSLTGeXAMYfoACi1dhYxDmKHMMhSoqwjhSu", - "asset_symbol": "PPY", - "amount": "574155566" - },{ - "owner": "BTSDmR5gzUiWUeBPQ9bGsC2B6GDrCc9HWYd9", - "asset_symbol": "PPY", - "amount": "2669708" - },{ - "owner": "BTS6URBcbpKTV28h9GNfYqx6aTMhCEoyVCJL", - "asset_symbol": "PPY", - "amount": "8827814" - },{ - "owner": "BTSMpnCKJ4tBTs7VDW5MpGLfzd9397VFQ2Yn", - "asset_symbol": "PPY", - "amount": "2350296" - },{ - "owner": "BTSKHYmWG5u7aoWz3orrHPnTWsTnGBdREvED", - "asset_symbol": "PPY", - "amount": "062412" - },{ - "owner": "BTS5gmVv6QY82wywTRei25mKCfwhnGxNM3jo", - "asset_symbol": "PPY", - "amount": "872216" - },{ - "owner": "BTS7c5SLFmkrRkNXZHYQGD55iU13uvUxLChp", - "asset_symbol": "PPY", - "amount": "979241" - },{ - "owner": "BTSLwc2GoFLXA2EMY42ZfeJ7nKGcP485NwpR", - "asset_symbol": "PPY", - "amount": "13226734" - },{ - "owner": "BTSFdwGpwkNaX9M5hM2SQmkC5FaRJzkRP94k", - "asset_symbol": "PPY", - "amount": "88418078" - },{ - "owner": "BTSAkrLfmmFWK9bDDPxNxFzMec4NcuzDi2yi", - "asset_symbol": "PPY", - "amount": "3982551" - },{ - "owner": "BTSHQDmWf3Q6BGZaBvFWiB9xVq3vpVd3G8jy", - "asset_symbol": "PPY", - "amount": "4980677" - },{ - "owner": "BTSswp6pufVRfgpRdKNuxqtqivLAue8Zxig", - "asset_symbol": "PPY", - "amount": "26473682" - },{ - "owner": "BTSFFtrPhJBHmesFaoDnocSV3U9TFGQrpzaT", - "asset_symbol": "PPY", - "amount": "28566209" - },{ - "owner": "BTSMtD8F9PMRwqcCJCXTdBcqkoVtDfj3xzSG", - "asset_symbol": "PPY", - "amount": "2355445" - },{ - "owner": "BTSFHcf4caJabo5U49LTkktcZodu43MKjVXU", - "asset_symbol": "PPY", - "amount": "2687215" - },{ - "owner": "BTS9FG99PA64h5pdHkGUBKdyJ7kpWGVzmmpp", - "asset_symbol": "PPY", - "amount": "1204602" - },{ - "owner": "BTSFETX1mSC3reGxNNUNbCfgQki2CDSpZAWr", - "asset_symbol": "PPY", - "amount": "2119366" - },{ - "owner": "BTS2zoqFhH9oL4VTCEhNXpXiAVkxsVXnsdiP", - "asset_symbol": "PPY", - "amount": "14582167" - },{ - "owner": "BTSph1YqywcVsiQwh2cZmC4oiSctEtc36CQ", - "asset_symbol": "PPY", - "amount": "1357636" - },{ - "owner": "BTSNahdgvTCXmSmHEYypvtNq9n9h2qi8nZn8", - "asset_symbol": "PPY", - "amount": "5153761" - },{ - "owner": "BTSPAt5N3y1t4k8hCdH5RJAetSyxaurYQDkZ", - "asset_symbol": "PPY", - "amount": "12799930" - },{ - "owner": "BTSCsbzBgxfi55NhG6Cp4UKai4SFJCYjsgim", - "asset_symbol": "PPY", - "amount": "5066717" - },{ - "owner": "BTSBkpas3sVrAqqqzm7x8dsgBgAjt9THXTth", - "asset_symbol": "PPY", - "amount": "175048" - },{ - "owner": "BTSKkCT9wuzue8FT7P1MPRguFqHEW2cJmo5", - "asset_symbol": "PPY", - "amount": "322149775" - },{ - "owner": "BTSNkbrPEUgzSJEWLodVFg4KtwpkAXWXtnSd", - "asset_symbol": "PPY", - "amount": "3544934" - },{ - "owner": "BTS5bteDcu1EkYvMKJWX4SfeJ4FrkYRKw9xS", - "asset_symbol": "PPY", - "amount": "2458279" - },{ - "owner": "BTSKBpa3P2C5zKzj8HANrcCRDvEKUC41F5x4", - "asset_symbol": "PPY", - "amount": "4944514" - },{ - "owner": "BTSHfRiH9uzEshtcrfGdT3cACuEHfLjaU82C", - "asset_symbol": "PPY", - "amount": "6730703" - },{ - "owner": "BTS82PR9E8i8f5acnBPZbMQs7eeTpTJhr1Js", - "asset_symbol": "PPY", - "amount": "63951386" - },{ - "owner": "BTS47AUdygz1YZX3ZxksURgtKQNFvcGukAjH", - "asset_symbol": "PPY", - "amount": "15522403" - },{ - "owner": "BTSCMpeJuBfCpjJrm1MXEf8vMjpFynjsU2g5", - "asset_symbol": "PPY", - "amount": "509150845" - },{ - "owner": "BTSAPSsSMYJmfyfez7yvgheYn8qTxM42E9qn", - "asset_symbol": "PPY", - "amount": "343236" - },{ - "owner": "BTSQ8na5TgyozykAcHQiSjXkh68KfNPrRScD", - "asset_symbol": "PPY", - "amount": "6871320" - },{ - "owner": "BTSAopMp32fo9rBZuYcfozq69FAEDgKVMdk7", - "asset_symbol": "PPY", - "amount": "290582" - },{ - "owner": "BTSDR1gSs9w3bcjboScchMhfj3cuHryJg7kW", - "asset_symbol": "PPY", - "amount": "436642101" - },{ - "owner": "BTSP5ybPYgh29zdfKhmBGJi2tGiH1UVX3QeX", - "asset_symbol": "PPY", - "amount": "067788" - },{ - "owner": "BTS25KHKBAZG3Fv5qfEhuWt4WBHdjBaSis6m", - "asset_symbol": "PPY", - "amount": "17452530" - },{ - "owner": "BTSLkV2m2TreFTSP3cFt8qi8fBTPGaimYcXz", - "asset_symbol": "PPY", - "amount": "2247358" - },{ - "owner": "BTSzJoyHbyjRaAB5jQW4nRDyDudPhmgnmBT", - "asset_symbol": "PPY", - "amount": "236594" - },{ - "owner": "BTS4rESGs4G4knhENnY5YaLLv35RcmbBc1LF", - "asset_symbol": "PPY", - "amount": "999876" - },{ - "owner": "BTS3maaDvPV83VYYtgGgfdacKxmoCuVX4mZx", - "asset_symbol": "PPY", - "amount": "262381" - },{ - "owner": "BTSFvTpv91vKWGQdfzFvdi2A2JGMQmQstvuY", - "asset_symbol": "PPY", - "amount": "1756499" - },{ - "owner": "BTS45Z5WBztTTXtSmGRXZXEuzPRaZamFKbcL", - "asset_symbol": "PPY", - "amount": "32634334" - },{ - "owner": "BTSKEvSmqXV6cKaq5B5fRBq1mC7fLA2VU69Z", - "asset_symbol": "PPY", - "amount": "14640634" - },{ - "owner": "BTSLiHzuvoXRBZsPeXp6BPxUGJ1fjGv3iAa", - "asset_symbol": "PPY", - "amount": "10486476" - },{ - "owner": "BTSM7nN3rVN5rfCWKhLGfY6Vd2jJgfpdnQ4J", - "asset_symbol": "PPY", - "amount": "332003" - },{ - "owner": "BTSLYeB34ACPHNyZdxrCUWTmkFh1SUWTigbW", - "asset_symbol": "PPY", - "amount": "935982" - },{ - "owner": "BTS7VhAtQS1jGrDqWGYiPSu63PzkF5zeXJYc", - "asset_symbol": "PPY", - "amount": "4588329" - },{ - "owner": "BTSDkFeaJAhyqGjqGmTdNAQosdgtyzbA3PM5", - "asset_symbol": "PPY", - "amount": "3439386" - },{ - "owner": "BTS6mUMdB1zR2EduEqh6Lud46REZDx91iZGE", - "asset_symbol": "PPY", - "amount": "2389253" - },{ - "owner": "BTS4aY4puaUwpdXMGn2sMHydV4MJcZubLHD4", - "asset_symbol": "PPY", - "amount": "10464082" - },{ - "owner": "BTSEAHpr4yFmDvMvioEqWTeXuXr144Pdjhz3", - "asset_symbol": "PPY", - "amount": "6456684" - },{ - "owner": "BTSAupcs9uUwmaXJ8zhuCNPxvrg8o6mcUD9g", - "asset_symbol": "PPY", - "amount": "1850573" - },{ - "owner": "BTS2vvAqKnvqch89gHEdKzHC3UScFDdTJEug", - "asset_symbol": "PPY", - "amount": "6722500" - },{ - "owner": "BTSBhhhnWg6okwetBpmCfWvFoXskJJWCCZEL", - "asset_symbol": "PPY", - "amount": "18986002" - },{ - "owner": "BTSE4CnLkseDqqTU3pf7UxXZMEWdYJCgh8VQ", - "asset_symbol": "PPY", - "amount": "9788208" - },{ - "owner": "BTSPkhxynEt8XTKMG5uQLpxPQ58bhLjygWQV", - "asset_symbol": "PPY", - "amount": "25599860" - },{ - "owner": "BTS9EJaxbxaSgojz3ZJTjCdiRfcrkTmHWVq6", - "asset_symbol": "PPY", - "amount": "24781158" - },{ - "owner": "BTSCXrHgfKzroudCkqt1p1uRobfRmAAGaPi9", - "asset_symbol": "PPY", - "amount": "7121467" - },{ - "owner": "BTSLQsWqBfc1jKRa8Vdc746hE2vXZbbZ9dbn", - "asset_symbol": "PPY", - "amount": "1788251" - },{ - "owner": "BTSBf9gdJcpFCbv46Uycnbz24RGN3TRGSmgT", - "asset_symbol": "PPY", - "amount": "5706701" - },{ - "owner": "BTSGDGz7EtLnhMTci7Voi52UYxbtjFeHXX1F", - "asset_symbol": "PPY", - "amount": "10769996" - },{ - "owner": "BTSACiTiKR72PieVMtyzMR6QAhPBfqE81Bws", - "asset_symbol": "PPY", - "amount": "908684" - },{ - "owner": "BTSD4E9DWdhhpMykZFfpNZf7KtWgZrXuzPSV", - "asset_symbol": "PPY", - "amount": "2562182" - },{ - "owner": "BTS6vnKYzFZp4eR8pk1rvHWt3BhbA6y2gFcS", - "asset_symbol": "PPY", - "amount": "338352" - },{ - "owner": "BTS3ybQmwx9dg47ZsfXJM8wAw6v9xweASBxy", - "asset_symbol": "PPY", - "amount": "1688678" - },{ - "owner": "BTSPTwp9DZKCCrqrc4NdHRXzb2A6vYH85QoD", - "asset_symbol": "PPY", - "amount": "273309" - },{ - "owner": "BTSJFCyTBbPtw7qXkraQJVMwwF72CCdwpzwf", - "asset_symbol": "PPY", - "amount": "50445839" - },{ - "owner": "BTSEnaXRpCaBSBdEDZqgVxVq46KuQMhr3KGs", - "asset_symbol": "PPY", - "amount": "402127" - },{ - "owner": "BTSJLz3nuaY7WTABK481tJ8W2Qpute3kb1g5", - "asset_symbol": "PPY", - "amount": "21748936" - },{ - "owner": "BTS9XAni1Qg9E8mWG3j6haF53BTSMBZUzoXw", - "asset_symbol": "PPY", - "amount": "8096576" - },{ - "owner": "BTSGcUQAZW7CgVPdoGqk7NNwDX3aNu3nmEzq", - "asset_symbol": "PPY", - "amount": "4281440" - },{ - "owner": "BTS5o6LUKd91DwnBLRzCHYE8DXcfNKX9SyTo", - "asset_symbol": "PPY", - "amount": "8987782" - },{ - "owner": "BTS27K3fbTWrdL8qgzHc8npz5mekSTLWiPx1", - "asset_symbol": "PPY", - "amount": "5104733" - },{ - "owner": "BTSFQNjcmSw5Nzmfqct3DhntaYRbcFJY7yDG", - "asset_symbol": "PPY", - "amount": "2209977" - },{ - "owner": "BTSKZRo3YYot7wUoUZKt1t6wUZ9Nvk6miZxu", - "asset_symbol": "PPY", - "amount": "34314905" - },{ - "owner": "BTS4hpSSAdCLVupj9KsngGwwEonjEPrVPPjX", - "asset_symbol": "PPY", - "amount": "323079" - },{ - "owner": "BTSExCFNihHHvhci2ySKRJgFRsVuhaXLdd3c", - "asset_symbol": "PPY", - "amount": "1508230" - },{ - "owner": "BTSHpYjeA3HNci2oo4XFe4rG63ihKv1vNBjF", - "asset_symbol": "PPY", - "amount": "4705832" - },{ - "owner": "BTSAjXVL9qwpMvKTNCZSxktNPEizw2rsi97V", - "asset_symbol": "PPY", - "amount": "1647322" - },{ - "owner": "BTSLrwKtTGmstq1XXSX7NtbBk3CE4yiJKm7T", - "asset_symbol": "PPY", - "amount": "340750" - },{ - "owner": "BTSGXaUGGzmtccsXzZ6D1Tpf6B9GCcvFpr2q", - "asset_symbol": "PPY", - "amount": "3375640" - },{ - "owner": "BTSPUjC6qCdbLddzjUzEi87pzJGUZZmT6b6G", - "asset_symbol": "PPY", - "amount": "084191" - },{ - "owner": "BTS31L1U6snLtaf584p2xwYdfX212hsuBYgm", - "asset_symbol": "PPY", - "amount": "9600273" - },{ - "owner": "BTSAXG1EFFd2PB5pmUSwccTPxRFqR7BqraD4", - "asset_symbol": "PPY", - "amount": "1968862" - },{ - "owner": "BTSL7YdJFV9YyFiBqpkvDzrvnQrAQrWRTKNj", - "asset_symbol": "PPY", - "amount": "33511758" - },{ - "owner": "BTSCfcQBVPefhzqsJP4tpmwNznCz1vzGDB6A", - "asset_symbol": "PPY", - "amount": "295762" - },{ - "owner": "BTS23U5vbxBsbLv4kDZHpLBe3sdGH2kwHCHT", - "asset_symbol": "PPY", - "amount": "13805289" - },{ - "owner": "BTSLhzZfxjqTKAmu4VxhdwCXAXpescP7PiEb", - "asset_symbol": "PPY", - "amount": "1715562" - },{ - "owner": "BTSMjyFXgfUR3TdKfUQ3qQFSH6Auuh9qyjBB", - "asset_symbol": "PPY", - "amount": "2359791" - },{ - "owner": "BTSDMHwGnYs1n1a5vsMb5ASpago1aZL4DttE", - "asset_symbol": "PPY", - "amount": "829643" - },{ - "owner": "BTS81VU3WFyQEmn1nou2TGoZtRaEoZHcVWDF", - "asset_symbol": "PPY", - "amount": "20384417" - },{ - "owner": "BTSGBwKQJnanPz285tcgcXnmPR9wUfap5TPx", - "asset_symbol": "PPY", - "amount": "5638867" - },{ - "owner": "BTSMWQeA7EjmeRTzkLQ4NoLr6wpWnWkewvd8", - "asset_symbol": "PPY", - "amount": "1547839" - },{ - "owner": "BTS8NCC52hU3KSakoYitdEHBVDZnZYGqbBdY", - "asset_symbol": "PPY", - "amount": "2882825" - },{ - "owner": "BTS2q9dNaZdY4Mjhq2JNaTShJrvTAN2Nu2EX", - "asset_symbol": "PPY", - "amount": "1347594" - },{ - "owner": "BTSEriMArRB6PWBWaQSXfo5Q2brFmAXvAQxK", - "asset_symbol": "PPY", - "amount": "1121883" - },{ - "owner": "BTS2avbMwACtxfadChaeYvFkdArDTYmyDdap", - "asset_symbol": "PPY", - "amount": "2305225" - },{ - "owner": "BTSFacfiiV1bMYd4czZDZsPbmRybBhuuC8Hc", - "asset_symbol": "PPY", - "amount": "987741" - },{ - "owner": "BTSKWnfv7p4ZNtA2aYPe8scUCvafQrtjja8v", - "asset_symbol": "PPY", - "amount": "2559392" - },{ - "owner": "BTSH3xyS8MSpCrSEXcMc5tWqTAZg4i8EPHVp", - "asset_symbol": "PPY", - "amount": "12506060" - },{ - "owner": "BTSKk5B5Yf5xYEY1QrSCiW71qzCvuQbDZqPU", - "asset_symbol": "PPY", - "amount": "4562080" - },{ - "owner": "BTSCE1EaPqkcH1VjbvXS7rYybrjsYmYwjVgp", - "asset_symbol": "PPY", - "amount": "7647177" - },{ - "owner": "BTSPyPKVmv6CsizeGKFPE6Ji7abitqD484hR", - "asset_symbol": "PPY", - "amount": "2148528" - },{ - "owner": "BTSVDeSnXW3YiD6eKQtr5o9efo7zh5cTEvc", - "asset_symbol": "PPY", - "amount": "218854" - },{ - "owner": "BTSDPG5B5FZTJgnF1gd7HNkdkeSMzCjYyx1w", - "asset_symbol": "PPY", - "amount": "68647105" - },{ - "owner": "BTSLaidfbTuAQtoAmvRT5N1A3fMJ5Xcy1ndM", - "asset_symbol": "PPY", - "amount": "541184" - },{ - "owner": "BTSFjbWyFQCCgqNdzYLncAVcMZegykCcVYG8", - "asset_symbol": "PPY", - "amount": "37794146" - },{ - "owner": "BTSEz3a6giwKzbJHFCrRv6jwYAFynz3HbXXh", - "asset_symbol": "PPY", - "amount": "17328000" - },{ - "owner": "BTSBXjw8bCxDtcSiExThbyjYgSpoSCFt9fdo", - "asset_symbol": "PPY", - "amount": "95713313" - },{ - "owner": "BTSCgBHJBFxn6iraAAECeLSjVjbFfjHE9kbv", - "asset_symbol": "PPY", - "amount": "5683640" - },{ - "owner": "BTSDQ3NHvuUPU3BHuTz8teWzwvchcUUtsB1k", - "asset_symbol": "PPY", - "amount": "8712628" - },{ - "owner": "BTS4kskptVmJPowunj3x3TgPk4iSo2JeLZJ8", - "asset_symbol": "PPY", - "amount": "16165036" - },{ - "owner": "BTSFpujVui5T7iuEdURLZ3v6ErsvfVn2tBQv", - "asset_symbol": "PPY", - "amount": "14486519" - },{ - "owner": "BTSFSZ9YkWcfRmoARNXrbWwXXsboWMkfWMuG", - "asset_symbol": "PPY", - "amount": "1718095" - },{ - "owner": "BTSKN5NQ1Dyu5eHUkn56JcLzvJWPuRoXXL8w", - "asset_symbol": "PPY", - "amount": "2126839" - },{ - "owner": "BTSKv9gRNGqNkwsJ7REBgNZmJScTCSiwDkKR", - "asset_symbol": "PPY", - "amount": "3526850" - },{ - "owner": "BTSJpXbgLR7LiYSbfxTKNA8c2qWWSz12GKvc", - "asset_symbol": "PPY", - "amount": "708641" - },{ - "owner": "BTS3WjJhQoMcLqQhjabEL1WLcDybbgbzQp9q", - "asset_symbol": "PPY", - "amount": "6586610" - },{ - "owner": "BTS3sjvyHrueuxvqNvE8qqg9ZFFrnL82ZDeQ", - "asset_symbol": "PPY", - "amount": "068000" - },{ - "owner": "BTSEnWS9v8NrzuJGsrDpXUGAhNPArBweR8je", - "asset_symbol": "PPY", - "amount": "23157415" - },{ - "owner": "BTSK8KTu8Mm8417KUcsfU2JfDAo7s9Btom4F", - "asset_symbol": "PPY", - "amount": "23930988" - },{ - "owner": "BTS3sN5ZQGhkhEvU2GVw9RL9c748S4L6vahH", - "asset_symbol": "PPY", - "amount": "1904518" - },{ - "owner": "BTSPuD7MHjmPCCjAWSUMJqFVzSDabycBof5m", - "asset_symbol": "PPY", - "amount": "60318933" - },{ - "owner": "BTSGEXrY62hkkB1M86iFxEPrMJqSQW2J3v48", - "asset_symbol": "PPY", - "amount": "1855821" - },{ - "owner": "BTSLp6CQ1JkkvcHCChfWa7fWGDxsX5SatAzz", - "asset_symbol": "PPY", - "amount": "2039514" - },{ - "owner": "BTSD9wG6gqpbUDxUei4tPViurXuXNyb1NyFj", - "asset_symbol": "PPY", - "amount": "10214065" - },{ - "owner": "BTS9L8w1MZisZY2iGPDs8vvjWf1iMwHK82LW", - "asset_symbol": "PPY", - "amount": "15218692" - },{ - "owner": "BTSJXxSZE1vk8g7JpTbThhzp4QXbyA7KovQZ", - "asset_symbol": "PPY", - "amount": "3497745" - },{ - "owner": "BTSNodWx9STeoWZVsQpa56faFhUmSvDKZA59", - "asset_symbol": "PPY", - "amount": "3258682" - },{ - "owner": "BTSNKX6RFzhjrXEbMwHzTjCngmdBVq2TTjCu", - "asset_symbol": "PPY", - "amount": "16379673" - },{ - "owner": "BTSitedk3o7SHQsfUW3vCdaUMc2aMMWSro2", - "asset_symbol": "PPY", - "amount": "3366939" - },{ - "owner": "BTSDzZXDzeTeY7X9Hy2LB38Bs1kvHRCwxkAA", - "asset_symbol": "PPY", - "amount": "1549747" - },{ - "owner": "BTS6c997V3fhXNTWLHWFEbFh3fiGJn6eX9cU", - "asset_symbol": "PPY", - "amount": "4106522" - },{ - "owner": "BTSP4noACM7fQZSRTf8ftEhuCT21ZT2exkCy", - "asset_symbol": "PPY", - "amount": "1224927" - },{ - "owner": "BTSAukz48LATPmYbdVAqAFVLA9J9TdKB7YDu", - "asset_symbol": "PPY", - "amount": "1773587" - },{ - "owner": "BTSLUaS6kbxSVD6xJ7YZnsFbHkKioQLL2RxV", - "asset_symbol": "PPY", - "amount": "1575005" - },{ - "owner": "BTSPZz4nRMnB1MHas9qDFaZSh1EQKsBXBLkY", - "asset_symbol": "PPY", - "amount": "22332159" - },{ - "owner": "BTS3QH6UJzmQtUGg6KszHfNebRZEmhZpj2aY", - "asset_symbol": "PPY", - "amount": "310362" - },{ - "owner": "BTSQG5Y53yzLhw7DR7E5phpdRaH5GR59uE1C", - "asset_symbol": "PPY", - "amount": "19318269" - },{ - "owner": "BTSaXvYqbGcaRQn1c7aiLBq4g66T8XJRvLD", - "asset_symbol": "PPY", - "amount": "3321867" - },{ - "owner": "BTSDeC995doF8F7THj7dqATMdrMnb2vs7p7X", - "asset_symbol": "PPY", - "amount": "350950200" - },{ - "owner": "BTS2JS8NGjrAvAv7KqtYw7z43PaRuuwogoQ5", - "asset_symbol": "PPY", - "amount": "1699079" - },{ - "owner": "BTS2dySh1pZfJuu7HYLC5urDzvMN9KFhv7eN", - "asset_symbol": "PPY", - "amount": "113102285" - },{ - "owner": "BTS58e9BWeXXZdgcKyPXEUv4ipHgWXemq7Ug", - "asset_symbol": "PPY", - "amount": "1014900" - },{ - "owner": "BTS6tbhmbLBbtrwHEXQhx44yGswYzV7cAZiX", - "asset_symbol": "PPY", - "amount": "156910" - },{ - "owner": "BTS2qpQUjEnR1DUxn7GoY91M2Dxxmq1f7GxP", - "asset_symbol": "PPY", - "amount": "5620396" - },{ - "owner": "BTSNQno7nDDuvNnqggEmwZHivr6fqRsFQQoK", - "asset_symbol": "PPY", - "amount": "10732396" - },{ - "owner": "BTSFRK5FYnXmJbXdpuVoWTsnhu29WuZhVy2S", - "asset_symbol": "PPY", - "amount": "638526" - },{ - "owner": "BTSPUJCQsd6voYai5N1cGh8ihxota5Y3dcq", - "asset_symbol": "PPY", - "amount": "2591696" - },{ - "owner": "BTS3R6fscMrMeYiygvu3xD3ATJsKyDqBTKNf", - "asset_symbol": "PPY", - "amount": "103294334" - },{ - "owner": "BTSM4SmXXwSS1UYk3DpQ9RvC5p62Gxx7ykEA", - "asset_symbol": "PPY", - "amount": "106834380" - },{ - "owner": "BTS3paR5N1uXiVLRw2yjs9zRMDDn2MstQ3se", - "asset_symbol": "PPY", - "amount": "15591401" - },{ - "owner": "BTS7Z8WBJgFRWjdyJ6f97LetJyHmJQhboaZQ", - "asset_symbol": "PPY", - "amount": "2320354" - },{ - "owner": "BTSCPYEwFTLHEwPJSNq2CHkp3ityR9qiYN9o", - "asset_symbol": "PPY", - "amount": "11699895" - },{ - "owner": "BTSQ9ADSuNFvcpB9zprjMYRjmY77DTzmnvNG", - "asset_symbol": "PPY", - "amount": "4940867" - },{ - "owner": "BTS5ZJFCfTGYQagC6YuuktHKwTkC3sFnm78U", - "asset_symbol": "PPY", - "amount": "103140000" - },{ - "owner": "BTS6jtjhEyw7syYHZieGvPWN3R1SkDD8u7KJ", - "asset_symbol": "PPY", - "amount": "6676308" - },{ - "owner": "BTScQ684oRGSbBv5Xszjb1W4aQraf1mZWfX", - "asset_symbol": "PPY", - "amount": "166625" - },{ - "owner": "BTSD3wqgkaVTCHWNMxZgTkQMRGkwnLY9AZPQ", - "asset_symbol": "PPY", - "amount": "15835300" - },{ - "owner": "BTS3GRG9HaTAxBXa8PJFsvd4WBhgmek2ojms", - "asset_symbol": "PPY", - "amount": "1737921" - },{ - "owner": "BTSGwVzUYoXezmYNCpovcntMf6jXtL5gYNvV", - "asset_symbol": "PPY", - "amount": "1985332" - },{ - "owner": "BTSABV583Cv4LYzhcgGTQSAT5PP5tP4cbpX9", - "asset_symbol": "PPY", - "amount": "17161810" - },{ - "owner": "BTSBLvZw6PzKhPRDGwSvpCGayPFgkiQfCoKq", - "asset_symbol": "PPY", - "amount": "31158844" - },{ - "owner": "BTSQGk9wpEaEVcdifnLNrV45LZMnpQCdH7K9", - "asset_symbol": "PPY", - "amount": "13491947" - },{ - "owner": "BTS34zwbLHnQpBuq8nMkHpZMK8XSHUsYU8KU", - "asset_symbol": "PPY", - "amount": "22019713" - },{ - "owner": "BTS91gExhdX7UdnBp4Y9YZZN93kfZvchoNqF", - "asset_symbol": "PPY", - "amount": "34783480" - },{ - "owner": "BTSJkHV5oxtmYne5v16Z1dSXxvUaMJw68dor", - "asset_symbol": "PPY", - "amount": "20662569" - },{ - "owner": "BTS3bgwusTGZ2YTxiPjjgDUothUaVKZ7qfZB", - "asset_symbol": "PPY", - "amount": "6344267" - },{ - "owner": "BTSPZqUuWrdTLtv9wb4WbGqvTX4oyu46diSF", - "asset_symbol": "PPY", - "amount": "3360886" - },{ - "owner": "BTSLnX1miT5KcNCBcsve1MNotnh1SDeZ4Pyp", - "asset_symbol": "PPY", - "amount": "4086618" - },{ - "owner": "BTSGLpNhM1V7xwmQBo8SzYopGXXJfRw3ex5T", - "asset_symbol": "PPY", - "amount": "9325127" - },{ - "owner": "BTSA1GCoM5L7iK1f69Zb2npCixVbzheQp6SU", - "asset_symbol": "PPY", - "amount": "684106" - },{ - "owner": "BTSEk1n5ybH4Cs9XD6rRokAk2gXvAodfwKEN", - "asset_symbol": "PPY", - "amount": "20842308" - },{ - "owner": "BTSG6jJ71Zp1XSxP9mv7EpiDVsSQsEhpjKZm", - "asset_symbol": "PPY", - "amount": "4115096" - },{ - "owner": "BTSN5hg7s5Gk11A5vAH9Xy6aidiDJcbwUtC2", - "asset_symbol": "PPY", - "amount": "20381950" - },{ - "owner": "BTSMJTiX7oFzo284ETPu3TnChtENTKx5c9Rb", - "asset_symbol": "PPY", - "amount": "47618858" - },{ - "owner": "BTSAR9taVnKHjPg14zdgo8AQ95yTA8iBUmjR", - "asset_symbol": "PPY", - "amount": "21519120" - },{ - "owner": "BTSCutd8TeCgb9VNxAsCEa5SZHcYZzK9WDk4", - "asset_symbol": "PPY", - "amount": "9010352" - },{ - "owner": "BTSJorM36zudfp8sQmw4fV2zk2NMEQhBsvGW", - "asset_symbol": "PPY", - "amount": "4809857" - },{ - "owner": "BTSAzUytRLoziGUTqQmCWinMJJ9N29xm51fU", - "asset_symbol": "PPY", - "amount": "62340409" - },{ - "owner": "BTS4R8QDL2aq3FYnK4m8o9y8UogeMj9W9KRQ", - "asset_symbol": "PPY", - "amount": "4416744" - },{ - "owner": "BTSEGCg1PhJXxN6VtyPxa8QiPs5XNTaFkvhW", - "asset_symbol": "PPY", - "amount": "118089522" - },{ - "owner": "BTScNoWnh4ESfq3rk9awtry5cZV7X3tFS8u", - "asset_symbol": "PPY", - "amount": "16012326" - },{ - "owner": "BTS68qNUZ7XFKMxRYAUb9xqF3QKeDPPtKiMA", - "asset_symbol": "PPY", - "amount": "2225286" - },{ - "owner": "BTS3yLvDK2SMUUDHLHheARWDyfsjFrrFjPqn", - "asset_symbol": "PPY", - "amount": "34323619" - },{ - "owner": "BTSCLnP6SbN6q5RaTwAAZnj9ZpNYwoARg1Ag", - "asset_symbol": "PPY", - "amount": "16965495" - },{ - "owner": "BTS9X7qRz7XM8abJ1HMy6PX27X1UreLdkPan", - "asset_symbol": "PPY", - "amount": "114643732" - },{ - "owner": "BTS5JHFoEkz983qyji2wwtV3YTQqYmBQxf7Q", - "asset_symbol": "PPY", - "amount": "356193" - },{ - "owner": "BTS757iH6M28mEJX6xZbuM6rRzCzBGuZ28D3", - "asset_symbol": "PPY", - "amount": "27029847" - },{ - "owner": "BTSMjYMjC9j5Ba44xBDnr1xY76rXFaTjSeLN", - "asset_symbol": "PPY", - "amount": "1754971" - },{ - "owner": "BTSHdDnu1pbKRs4N4eoGXDEMFMehnxochC3D", - "asset_symbol": "PPY", - "amount": "959590" - },{ - "owner": "BTS4xAEZbaPo6s4jYA829iAqCZT79qamgc3X", - "asset_symbol": "PPY", - "amount": "608656" - },{ - "owner": "BTSNf6iUEWNsiPTAJHizuGwtX81EDwZdKNb", - "asset_symbol": "PPY", - "amount": "1647575" - },{ - "owner": "BTSFtsBwYNFVMH9gSmb4NQ4qMkperPoPw9B4", - "asset_symbol": "PPY", - "amount": "3551454" - },{ - "owner": "BTS8s2wnPkieFLykmqp9yP2Xa8aeeLqf9vLv", - "asset_symbol": "PPY", - "amount": "4737078" - },{ - "owner": "BTSBX92aA1T5XoovMikMsybmWQBeQzyU1mFm", - "asset_symbol": "PPY", - "amount": "5897174" - },{ - "owner": "BTSJ5fPY7rzQfR8TT9LCtYh9SvaeDzxkhV3b", - "asset_symbol": "PPY", - "amount": "213998400" - },{ - "owner": "BTS321KdFFoUEFqTdaiE8FMddaCbbcRqyznc", - "asset_symbol": "PPY", - "amount": "800047" - },{ - "owner": "BTS39mvq9jpy65QBbXEpPdwF8E7VWVFu6zvX", - "asset_symbol": "PPY", - "amount": "107718" - },{ - "owner": "BTS4eXkVLFAUcY2YQehirkH5mo4Av5kFVJx9", - "asset_symbol": "PPY", - "amount": "3435886" - },{ - "owner": "BTSPm51hs6ufT8VfrKFvdfXx4gXfobBTaZgk", - "asset_symbol": "PPY", - "amount": "51103009" - },{ - "owner": "BTS7gPJAZuiJGud8N2hmkN5usuxZRX2w8km", - "asset_symbol": "PPY", - "amount": "6480456" - },{ - "owner": "BTSKtxVGfp7FH9d25L6JQHBiwvM7x8a4xno5", - "asset_symbol": "PPY", - "amount": "6903320" - },{ - "owner": "BTSJTLoJNtw65CjCV9g7wdk2D4sZMBpr4umg", - "asset_symbol": "PPY", - "amount": "4204643" - },{ - "owner": "BTS5yicXuouC5zTsJ1LKE1gozvUjemMpkNW4", - "asset_symbol": "PPY", - "amount": "13832239" - },{ - "owner": "BTS8WuGvB9yKxUkbtWYEnTQzMKpmuEBobdY9", - "asset_symbol": "PPY", - "amount": "1686279" - },{ - "owner": "BTSGpreYczX7nYen5pva6oPiG3oVHx9v6M2Z", - "asset_symbol": "PPY", - "amount": "1030768" - },{ - "owner": "BTSH2nupMVaScg55PqdtNjM56c1oYdsjZaBe", - "asset_symbol": "PPY", - "amount": "11822800" - },{ - "owner": "BTSsrrMmFPzuvBXLZogWSrdMwWuhCQvoXK7", - "asset_symbol": "PPY", - "amount": "1673950" - },{ - "owner": "BTS6KFEc2594Y7TryF1cK18KqkJRPXc6uACi", - "asset_symbol": "PPY", - "amount": "2135964" - },{ - "owner": "BTS9L8GeRKdt8tnzRrCQ76rV25x4vzJxtXdG", - "asset_symbol": "PPY", - "amount": "2313751" - },{ - "owner": "BTSNfMTdDTKC1obvArMzrYe1uQgP9ivfmPZZ", - "asset_symbol": "PPY", - "amount": "2363905" - },{ - "owner": "BTSmqNa74vQDnQrBZKgwQquoWkjXYzZ9Mp3", - "asset_symbol": "PPY", - "amount": "26885375" - },{ - "owner": "BTSBPgQ2eiPenAzss3xujan6anC1gXxtT8ns", - "asset_symbol": "PPY", - "amount": "1561716" - },{ - "owner": "BTSErVqvVLkxnWk4MHxBhtVs97Q5NCU5cA39", - "asset_symbol": "PPY", - "amount": "1119566" - },{ - "owner": "BTS5wX88LFVvjs6DQzyqdveyRLdyHBH1AfGy", - "asset_symbol": "PPY", - "amount": "97830933" - },{ - "owner": "BTS9CDEFfjpeVk1SGQtCHjHJnXabUjoHTyaK", - "asset_symbol": "PPY", - "amount": "206002" - },{ - "owner": "BTSPgYzcUvfzd7Pk8iTtLYym38sxLwczteZo", - "asset_symbol": "PPY", - "amount": "7219957" - },{ - "owner": "BTS5eBvrmbN8MLQ3X8dK2z9H2fmBjfsoAgmJ", - "asset_symbol": "PPY", - "amount": "794523" - },{ - "owner": "BTSKyh4qt7fzxAgM985sM6nno3149W4p3VpC", - "asset_symbol": "PPY", - "amount": "3903486" - },{ - "owner": "BTSHnvxkfucAGB3cQuAPwQpmERRaZFyJ6stU", - "asset_symbol": "PPY", - "amount": "11091853" - },{ - "owner": "BTSDsMywSfkn2WKwYLuMbZVtG3kCRRgRu9Xq", - "asset_symbol": "PPY", - "amount": "5703124" - },{ - "owner": "BTSMnK9rQhesSjB4PYypY2jzEGGHxa2vQy93", - "asset_symbol": "PPY", - "amount": "1921761" - },{ - "owner": "BTS6FFirq5MWom8Wvt7h52MBismfTA34KjpC", - "asset_symbol": "PPY", - "amount": "33374808" - },{ - "owner": "BTSQAUC8y2SDhv4oPdwFDcoMemFUu5hJi6z1", - "asset_symbol": "PPY", - "amount": "1910783" - },{ - "owner": "BTSJmg1Svyq2vuA2pp29dCY1ebGxH6zs4Jji", - "asset_symbol": "PPY", - "amount": "4726725" - },{ - "owner": "BTSSdaaJZGn4aLZK9mZ528snraFos2syHD4", - "asset_symbol": "PPY", - "amount": "109978" - },{ - "owner": "BTSES2MkKaKJzfoRTVsmG6PBZrWAeBZX1JKa", - "asset_symbol": "PPY", - "amount": "2743715" - },{ - "owner": "BTSFPx7tUff6fCtTHvQRkR4J5q2SSBmNov8J", - "asset_symbol": "PPY", - "amount": "7851132" - },{ - "owner": "BTSD7Z3mZir1TnpF7ja5TAtxM6rMkBtar1JJ", - "asset_symbol": "PPY", - "amount": "1651861" - },{ - "owner": "BTSFGgaqPEAiNHGGxkYxdzy3BfnjpnCrEJXk", - "asset_symbol": "PPY", - "amount": "9588757" - },{ - "owner": "BTSDewtC7APTY7rbtWBduu3hr1c6TwuKDTHE", - "asset_symbol": "PPY", - "amount": "103262080" - },{ - "owner": "BTS3RJXcuwdX8TrafQQFDXyEPDKEuKtajy7T", - "asset_symbol": "PPY", - "amount": "32732377" - },{ - "owner": "BTS8h78N3f2TStr7DVp8NfyeGADF68Z9sANu", - "asset_symbol": "PPY", - "amount": "875626" - },{ - "owner": "BTSJZFxrCMhXm2HwqVkTjszLidTQkZVTH1nG", - "asset_symbol": "PPY", - "amount": "008702" - },{ - "owner": "BTS5mAwoXFTgY8wzeaBGhZzrUFegvLfqcK2b", - "asset_symbol": "PPY", - "amount": "87691867" - },{ - "owner": "BTS5kPtDuw7wsHN9zxtEszdVauctTzqepKo4", - "asset_symbol": "PPY", - "amount": "12899100" - },{ - "owner": "BTS7qrKDdrgKwjExNpKodKpK5qbQPSstkUPt", - "asset_symbol": "PPY", - "amount": "2713631" - },{ - "owner": "BTSCPPNKiiNSZM9SHcZRdAGJ5ojeP67c4WP7", - "asset_symbol": "PPY", - "amount": "6063007" - },{ - "owner": "BTSAZDg4TQZ5rd32zf3arbfSt928LV7cgFjs", - "asset_symbol": "PPY", - "amount": "106505802" - },{ - "owner": "BTSLyKF6RZf8Emwfut5dF3tt6UHRZqg49wNt", - "asset_symbol": "PPY", - "amount": "10599654" - },{ - "owner": "BTS3xtQjceteBLmzfpud5CQuq12sniSfefHc", - "asset_symbol": "PPY", - "amount": "1400156" - },{ - "owner": "BTSVvV3ibErptbyeTiC59mo8pwtbvT8Nt8R", - "asset_symbol": "PPY", - "amount": "2478932" - },{ - "owner": "BTS9qcrihJptwMLG3VLaDPmAKKoni9gEzfG", - "asset_symbol": "PPY", - "amount": "3287269" - },{ - "owner": "BTS8D4S8w6dBMKQ2dyoN4TQocTjJjkqgS6WJ", - "asset_symbol": "PPY", - "amount": "894500" - },{ - "owner": "BTSGZcMQRYdqEMuxDjGfJXXTghmdboxwiCM3", - "asset_symbol": "PPY", - "amount": "167525" - },{ - "owner": "BTSPQxz1XULV9bAT5cAwjbnzEKwd8tbkPHkW", - "asset_symbol": "PPY", - "amount": "19047479" - },{ - "owner": "BTSDWSth5vRpMCf6opHXAsYRf2oyiL9i8dUw", - "asset_symbol": "PPY", - "amount": "1674252" - },{ - "owner": "BTSP2rpXy8HpYhc7eeMQvNkNzenTXnkW59Gc", - "asset_symbol": "PPY", - "amount": "1623443" - },{ - "owner": "BTSNzNnZFfxStobwSHTzXhKScVF5zRR7qUCf", - "asset_symbol": "PPY", - "amount": "17179800" - },{ - "owner": "BTSNfXnhQRyxY74CKxNZXLLKXr4oFPFxrqr8", - "asset_symbol": "PPY", - "amount": "98936714" - },{ - "owner": "BTSMshwX9Rq5L2edrVSfDQEw76o76DeDxjzX", - "asset_symbol": "PPY", - "amount": "188887" - },{ - "owner": "BTSP1u8W5mqcvsG3vpDtG2NKS33YcJJDfpvq", - "asset_symbol": "PPY", - "amount": "290498" - },{ - "owner": "BTS4e4Rqyv354VmPj5FiEJJ9qM4nLgUePA9z", - "asset_symbol": "PPY", - "amount": "9979918" - },{ - "owner": "BTSQGGJ31Hcrf977jsTa61BgVttZjueNyF3y", - "asset_symbol": "PPY", - "amount": "11867605" - },{ - "owner": "BTS4GF1nnvouo9PCpij8o2kfjkG7jPnpGLR1", - "asset_symbol": "PPY", - "amount": "272229534" - },{ - "owner": "BTSLL2CnfxbcvudW9jUDgzXHFBNgR5gbPEs5", - "asset_symbol": "PPY", - "amount": "4473656" - },{ - "owner": "BTS468stcQMDtYu6f2wDpfydMKXnubG9ke9X", - "asset_symbol": "PPY", - "amount": "5559703" - },{ - "owner": "BTSD34DkmudzgSQmC8VCWM8HMqC2sajzxXrx", - "asset_symbol": "PPY", - "amount": "1872664" - },{ - "owner": "BTSjzZywi3XpnyTZK3ue5ssdsiKDMpSLodV", - "asset_symbol": "PPY", - "amount": "3334497" - },{ - "owner": "BTSPt6VWLnhLCX8H8dx53eTVLF1K4nJQwddP", - "asset_symbol": "PPY", - "amount": "1996583" - },{ - "owner": "BTSMrGuewuwgW5Q3rPjGy4i8iz1V7ZzJRfaL", - "asset_symbol": "PPY", - "amount": "6620914" - },{ - "owner": "BTSFNJvPguqkQL91BF8jxkNEFWt13UPhUiwX", - "asset_symbol": "PPY", - "amount": "5128753" - },{ - "owner": "BTSB1xMaQWcVXcnMmRK8pBWWq85kH6XYx5Zv", - "asset_symbol": "PPY", - "amount": "117098000" - },{ - "owner": "BTSPJydUmN5CEvMsgiRGnZV9cRDyTGTNku1K", - "asset_symbol": "PPY", - "amount": "34743893" - },{ - "owner": "BTS6LfEdLyWYT6PXkpnMyUf6BAFQrM8ykfGZ", - "asset_symbol": "PPY", - "amount": "1143736" - },{ - "owner": "BTS8bJitTkgZWAKGdF4iCCDJtK8BN8jHJzWZ", - "asset_symbol": "PPY", - "amount": "1060476" - },{ - "owner": "BTSBjzfPqemH8bNCyQMXVU7TbKNaxpuXopgP", - "asset_symbol": "PPY", - "amount": "8885873" - },{ - "owner": "BTS3f5mRkwsnX7dtEQTMCVCbdrERvQKL4zM1", - "asset_symbol": "PPY", - "amount": "2430175" - },{ - "owner": "BTS9up4u7wKz2a7QF9HeLd7UUReSgxzYwtYJ", - "asset_symbol": "PPY", - "amount": "522445" - },{ - "owner": "BTSLTH5iDXzoaJ1b4sfdRvGK8LQXnPj2dRdh", - "asset_symbol": "PPY", - "amount": "10749745" - },{ - "owner": "BTSEdLWf2AEnYZzp5dCAJm43Brmqq84KWgpS", - "asset_symbol": "PPY", - "amount": "325908" - },{ - "owner": "BTSHjpqYUYuwXy1yuzJo9D4Gq9obdqeBEn5E", - "asset_symbol": "PPY", - "amount": "2807873" - },{ - "owner": "BTSA8FKYUjkbGJG1cTqdAjnqYdWnhgkqDnZK", - "asset_symbol": "PPY", - "amount": "1011875" - },{ - "owner": "BTSAEEed4whZgx7q2xpszHxtKW5BJQEKgFrW", - "asset_symbol": "PPY", - "amount": "4011755" - },{ - "owner": "BTSBzTeZe2X6DHcyEXnkHf9owjwfrez7c5xh", - "asset_symbol": "PPY", - "amount": "37181913" - },{ - "owner": "BTSGHkT6UVFxJDFX58Ck2qFw7nMy91EGsxzU", - "asset_symbol": "PPY", - "amount": "327961066" - },{ - "owner": "BTSAjFf2dKveU72HL5i1MZj3Wzhg3PsawqQb", - "asset_symbol": "PPY", - "amount": "2961568" - },{ - "owner": "BTSHnHrKo9xCjGkSAMKF9akn4KUsLEBMFTSo", - "asset_symbol": "PPY", - "amount": "3075940" - },{ - "owner": "BTSA41DVe78jkU4ovRVBgttmwaYZSUV3en1B", - "asset_symbol": "PPY", - "amount": "16469295" - },{ - "owner": "BTSar52JxYZLdWKvuiQjN21qhZu2U5ztqc1", - "asset_symbol": "PPY", - "amount": "188815989" - },{ - "owner": "BTSNuptFDC1Jq595Et9F5bEkPKsBH7CMZ21v", - "asset_symbol": "PPY", - "amount": "8763042" - },{ - "owner": "BTS55AoSR88rJ1CkmHXvBESc2gMSKroFe74W", - "asset_symbol": "PPY", - "amount": "6660173" - },{ - "owner": "BTS5ySWHqfMFaKZc77gRGVQyiKcTP4zTy7r3", - "asset_symbol": "PPY", - "amount": "11621537" - },{ - "owner": "BTS91nFMJ98LWFaJpCTRtCr5nxG1PBHEzjKc", - "asset_symbol": "PPY", - "amount": "1666987" - },{ - "owner": "BTSDDXHdMB1xguqAh6MwaFRmGBvtU1t2BNyB", - "asset_symbol": "PPY", - "amount": "1738133" - },{ - "owner": "BTS5hKdZbMWbz7AVFHXe2iRnsX18ijuj5Pi8", - "asset_symbol": "PPY", - "amount": "6267497" - },{ - "owner": "BTSFjVLanuTM5RgWu9ZH2nGow9tQA48gcvr6", - "asset_symbol": "PPY", - "amount": "512418" - },{ - "owner": "BTSLpVXg8DNrygnb3dbBFKDzfM58Fo1DdJ28", - "asset_symbol": "PPY", - "amount": "3317229" - },{ - "owner": "BTS8a3HosrwsmQQPuwQcaF1oB5zdLgAQiGGg", - "asset_symbol": "PPY", - "amount": "16659574" - },{ - "owner": "BTSD62h2wk9gXnyGCotPMk1i4P2oz64Wdc4d", - "asset_symbol": "PPY", - "amount": "1275816" - },{ - "owner": "BTSCwo4SJfTScMEfFXJLNZcB8nj2QJUdvniC", - "asset_symbol": "PPY", - "amount": "32065987" - },{ - "owner": "BTS7ttehHhgypHyn6dSB4ScrNULSx5V9wbdk", - "asset_symbol": "PPY", - "amount": "1650718" - },{ - "owner": "BTS7x3V4XKcZTo8vS6N3Bakf4z9U5jxUtx4L", - "asset_symbol": "PPY", - "amount": "28049950" - },{ - "owner": "BTSBzhGbiTX64TGYUWxA6f6RkBE1QhGJSiRB", - "asset_symbol": "PPY", - "amount": "13066610" - },{ - "owner": "BTSG4h8fdH3QLphmi6bNiacFuttKd9Y1qJqx", - "asset_symbol": "PPY", - "amount": "8118147" - },{ - "owner": "BTSAGvoiqynxwoJeM9XaqRasD82tp5F1R5gv", - "asset_symbol": "PPY", - "amount": "5602779" - },{ - "owner": "BTS5Poj1ttyQxDzzQ7Pxi3VbfwjqsXQZbVXD", - "asset_symbol": "PPY", - "amount": "24107077" - },{ - "owner": "BTS7o5HDYa1QH1fmb5eWPzNKhgi33Uma4DZP", - "asset_symbol": "PPY", - "amount": "15527382" - },{ - "owner": "BTSHkZhRmA65FRjKH79pP1nhHG9mDmQ6Qwqu", - "asset_symbol": "PPY", - "amount": "1202648" - },{ - "owner": "BTSPqBFdEGwuj4uLZCG3QxFGqod5qSCEUYLU", - "asset_symbol": "PPY", - "amount": "37266404" - },{ - "owner": "BTS2wFhU1C1d1uQhoNGTU1MjgSZNXiPuiuxF", - "asset_symbol": "PPY", - "amount": "68669200" - },{ - "owner": "BTSHPW92CmV9jGKDnpFmVjgMFg4VaLiaqmG4", - "asset_symbol": "PPY", - "amount": "114375265" - },{ - "owner": "BTSCXvY951fVmishqE8brWy6QHkYK8McA2Dz", - "asset_symbol": "PPY", - "amount": "1005371" - },{ - "owner": "BTSHp4LL1Tb46nVPLHGLTzb2NeepjrRNhuXS", - "asset_symbol": "PPY", - "amount": "2044330894" - },{ - "owner": "BTSP2TE5wtDgezZMVNpb5Chw8N4VfzziGoR1", - "asset_symbol": "PPY", - "amount": "343589" - },{ - "owner": "BTSPPiFMbPPNYZzYwu45awiYyjJ1LGLF87rZ", - "asset_symbol": "PPY", - "amount": "69584152" - },{ - "owner": "BTS6Q7zZtaVZEaLsGXe3yoSXjfkhZsnso8zk", - "asset_symbol": "PPY", - "amount": "202068" - },{ - "owner": "BTSBATYnit7n4D33GE6Qcv5AorUm3AHe6Mi", - "asset_symbol": "PPY", - "amount": "521317714" - },{ - "owner": "BTS98NeDgoWvXcba8Uo5k8iCAgAKHMLCEA5Z", - "asset_symbol": "PPY", - "amount": "33927990" - },{ - "owner": "BTS8k3TiRg2ogbnb7hn8CLpPnmejkrvHua8F", - "asset_symbol": "PPY", - "amount": "446835" - },{ - "owner": "BTSNqn24bxT4R5eoaxh4X6JVNAhsKb1rEXJz", - "asset_symbol": "PPY", - "amount": "54116440" - },{ - "owner": "BTSGN5DKC3RrUqC6KutnYCLb6hBujiMAb28v", - "asset_symbol": "PPY", - "amount": "10264356" - },{ - "owner": "BTSBLjCt1TSueJhGNZuNNnr43EwMSHsRKioU", - "asset_symbol": "PPY", - "amount": "24355761" - },{ - "owner": "BTSPcMAADinqQR93beeSKZZnnGK8PkfKefEQ", - "asset_symbol": "PPY", - "amount": "398743" - },{ - "owner": "BTSrLNUCsUuPiNJpRGkKMfRmXxUs3nqGJK2", - "asset_symbol": "PPY", - "amount": "10309284" - },{ - "owner": "BTSLDAKcjLmTmDYEUJpbeZqdQqXhDtBk18s2", - "asset_symbol": "PPY", - "amount": "3363645" - },{ - "owner": "BTSKyHRhLj6YmMQoF2n2LTH6e9ChG46TMJgS", - "asset_symbol": "PPY", - "amount": "5087743" - },{ - "owner": "BTS4FijceeQcEcKrMzyUQxKDFRvPeDQALqeD", - "asset_symbol": "PPY", - "amount": "2149916" - },{ - "owner": "BTSAcfLQGZG4YxNFBwqmYcWA5ZMra8Muk7aU", - "asset_symbol": "PPY", - "amount": "235992" - },{ - "owner": "BTSBDsP7LMfRLMSPvuhykW5TCgFsBN8Rrga", - "asset_symbol": "PPY", - "amount": "897011" - },{ - "owner": "BTSJvyCAEgcevYXPHFDwp4STAwUubzGJ2MF8", - "asset_symbol": "PPY", - "amount": "810693" - },{ - "owner": "BTSJNkWBsBVF8WQ2tTtmdBvtyK5vDMnArYKx", - "asset_symbol": "PPY", - "amount": "4862250" - },{ - "owner": "BTSD4jW4MUbRuTkCvkU4vaMYgojGtipBsWjK", - "asset_symbol": "PPY", - "amount": "222383" - },{ - "owner": "BTSKnBLWwpgyyNwHKSHJQAVvSwwWSjJDS3uw", - "asset_symbol": "PPY", - "amount": "2683834" - },{ - "owner": "BTSAneweorifRk6SZrchw8gz7H8s9qAMCAcB", - "asset_symbol": "PPY", - "amount": "1621295" - },{ - "owner": "BTS3pVGG9tUio1usUoKe4LvMK2por9xJj6U6", - "asset_symbol": "PPY", - "amount": "69028394" - },{ - "owner": "BTSFHRY7SPUtWbqui9rpvDLntdXH1rPCWuiB", - "asset_symbol": "PPY", - "amount": "412543" - },{ - "owner": "BTS34ZMFgK7FgHuHa5GgrkG5h77YijvGRqHd", - "asset_symbol": "PPY", - "amount": "1547118" - },{ - "owner": "BTSGskTo2iEtfojAAGxCQY36ueXw3yDPhGsy", - "asset_symbol": "PPY", - "amount": "688600" - },{ - "owner": "BTS8UWaYMHwAm9visQvgvv6nZTFjU11swKEi", - "asset_symbol": "PPY", - "amount": "069566" - },{ - "owner": "BTSLy2woB3c4ZrmeJwoiJtNDaaYkYB8UB8nS", - "asset_symbol": "PPY", - "amount": "5211117" - },{ - "owner": "BTS4Vje7qUzAXVJNJWwknS5nCtwMREUxEHbY", - "asset_symbol": "PPY", - "amount": "158829" - },{ - "owner": "BTSPyrBSW7qNDoKred5Tbi3dCJavMVMJJD8", - "asset_symbol": "PPY", - "amount": "5119317" - },{ - "owner": "BTS29A6rzjTSVeptrMjM8kxQdPY5GD3KpwZF", - "asset_symbol": "PPY", - "amount": "6599137" - },{ - "owner": "BTS2sPDyE7MS8XmMm63nrV9HF5ivm6SKXHeS", - "asset_symbol": "PPY", - "amount": "6776775" - },{ - "owner": "BTS7Pjk5LUNZ93jfgdtyjYcW63itDoHZ3egV", - "asset_symbol": "PPY", - "amount": "886138" - },{ - "owner": "BTS9sbHpZNThq8QWEW7g9DYbdgpy4ycegsPN", - "asset_symbol": "PPY", - "amount": "1490878" - },{ - "owner": "BTS6xq4Z7AEu5jG4JZS2mFNpvvxGesHbv4Vf", - "asset_symbol": "PPY", - "amount": "1507889" - },{ - "owner": "BTSGtx4UJxTwX9YE9CxzXa9gL1HacC6iymEz", - "asset_symbol": "PPY", - "amount": "2755251" - },{ - "owner": "BTSHq6qcLLnCD5PU4VUPvUbPAC6CfXuRchxa", - "asset_symbol": "PPY", - "amount": "099715" - },{ - "owner": "BTSDQqFvctvPxBiCXVYj7iwHdrtyw2r5jAqs", - "asset_symbol": "PPY", - "amount": "8294450" - },{ - "owner": "BTSKwgtd2z4yp4ZdtYX9eh8M54RD6cmC9b19", - "asset_symbol": "PPY", - "amount": "109098" - },{ - "owner": "BTS2sSHBgCmsvxPFXkXTiLW8jF7GBDmUtPHv", - "asset_symbol": "PPY", - "amount": "104678364" - },{ - "owner": "BTSH9r4ScTSNMmZkiTLsVnJG5NUzpg8jqbW5", - "asset_symbol": "PPY", - "amount": "2585654" - },{ - "owner": "BTSN485YQkPFjmwg1zJLe8wYRA8RXETGtWbw", - "asset_symbol": "PPY", - "amount": "9316996" - },{ - "owner": "BTS2pQ2S2dP4vhS4G4GuRHP9dYuzH8aR9A1Y", - "asset_symbol": "PPY", - "amount": "198003" - },{ - "owner": "BTS6zxTVPVKGjFv2neWGi5LhqBPdLLgCSp61", - "asset_symbol": "PPY", - "amount": "1007527" - },{ - "owner": "BTS6VxsMk8FEQW6pP9aLnTbmkdr9qZtdVseH", - "asset_symbol": "PPY", - "amount": "11497740" - },{ - "owner": "BTS8biZ4VBmZ8JqYPoyx6EQUPJAkvnLGPt4m", - "asset_symbol": "PPY", - "amount": "204414" - },{ - "owner": "BTS3zvYEGe2z2MfHND4sGFCx4LhenwhBcpaZ", - "asset_symbol": "PPY", - "amount": "11952400" - },{ - "owner": "BTS85A5ftviF3xnefRFMrCjM28qVMg9QS5Pi", - "asset_symbol": "PPY", - "amount": "10008091" - },{ - "owner": "BTSFgYFHLxmPa1KziLKtsP2KxdJMMYp92jmW", - "asset_symbol": "PPY", - "amount": "332657" - },{ - "owner": "BTS9fFYWaWxAdS6xMifG4vGjweeyn6DMLT63", - "asset_symbol": "PPY", - "amount": "193612667" - },{ - "owner": "BTS58zSf34Jn9ZFAWrNmmpUoeEkqViQjAjmq", - "asset_symbol": "PPY", - "amount": "8194015" - },{ - "owner": "BTS2mxn2PqDMQNeBJMiwXYqF2n6mu7J1eNBx", - "asset_symbol": "PPY", - "amount": "30016327" - },{ - "owner": "BTS7gXQpVHx89HBqoNmMsyJQisbNotFDW73M", - "asset_symbol": "PPY", - "amount": "12443113" - },{ - "owner": "BTS6UwAgY9U3wtK1ku3rcKBicPkUqeH5Sh86", - "asset_symbol": "PPY", - "amount": "7310267" - },{ - "owner": "BTSMDy6dcuXBqbH5gbC9mWPSyxCUfdA2a81P", - "asset_symbol": "PPY", - "amount": "040888" - },{ - "owner": "BTSN2RLxR8eXDNJrbhoV57rBktXUVvUWyyVP", - "asset_symbol": "PPY", - "amount": "9951887" - },{ - "owner": "BTS3mrJ8kxeBQge2MTgMoxNFTem4oZzmgTW7", - "asset_symbol": "PPY", - "amount": "2641615" - },{ - "owner": "BTSKeUj6Fp9Uv4uoX6Ty1wVB2K7wvNBHhXW4", - "asset_symbol": "PPY", - "amount": "3392599" - },{ - "owner": "BTSA3RZuazzrSk94PiKAdfQicXiTgDZCTq9c", - "asset_symbol": "PPY", - "amount": "3355916" - },{ - "owner": "BTSPLDwKLWVqMZiNhRm1YzfN4bvWBmsWkAjZ", - "asset_symbol": "PPY", - "amount": "003230" - },{ - "owner": "BTSJHStEv7dhSu7vAuHEVeN8moQ2TRxFVF9P", - "asset_symbol": "PPY", - "amount": "002033" - },{ - "owner": "BTSL6GrHyGXsxv4WTzRy3MimiQ6PuGVw1JcT", - "asset_symbol": "PPY", - "amount": "10476633" - },{ - "owner": "BTSKRAMvK9KFPonnmisJkq9oVARWRuRfYjT4", - "asset_symbol": "PPY", - "amount": "5639944" - },{ - "owner": "BTSGQe2PoFDJEnNthCDVnpBVGRbXrcSiQZa8", - "asset_symbol": "PPY", - "amount": "5359767" - },{ - "owner": "BTS7Vt7kyoZ3DfvKWqzxvAs1zik1DnSH2L3q", - "asset_symbol": "PPY", - "amount": "66790813" - },{ - "owner": "BTSrdUXcwf6EFzmceqFTGauPXTNno8AFx9s", - "asset_symbol": "PPY", - "amount": "845721" - },{ - "owner": "BTSNv2W36RftG4Tm7jobxqD6WJ9A6Nzf45AB", - "asset_symbol": "PPY", - "amount": "34210213" - },{ - "owner": "BTS86hQm5GQZsVMeAnjBLqvNwBLZLRyeAsFb", - "asset_symbol": "PPY", - "amount": "12818640" - },{ - "owner": "BTSFdyvDd2776mGgU9cchnmLxP75DpTkM4Yw", - "asset_symbol": "PPY", - "amount": "19410636" - },{ - "owner": "BTSFRA7KgKVK2GRMuAAwvSoumiRPVjURBmnD", - "asset_symbol": "PPY", - "amount": "2066508" - },{ - "owner": "BTSN6ivQ4HUPvaL5T1Syn56hPYgJVwSdRD3K", - "asset_symbol": "PPY", - "amount": "047397" - },{ - "owner": "BTS5aoGTzbh11H9DDjRqnrk8EFga1cs1ZtJA", - "asset_symbol": "PPY", - "amount": "301730" - },{ - "owner": "BTS8wdQV3YN6ag695anp9hmGfdhGC9fXJmNv", - "asset_symbol": "PPY", - "amount": "346552" - },{ - "owner": "BTSEL6t985yKJph4DhJGWn7bzd9unQuxkXV4", - "asset_symbol": "PPY", - "amount": "6608711" - },{ - "owner": "BTSNMbQXtcKeqWYCjgALhjN6izvHpehcdEsP", - "asset_symbol": "PPY", - "amount": "435190" - },{ - "owner": "BTS6sFhi3K9zNn5fHQyndVdskaxiTkf7d51j", - "asset_symbol": "PPY", - "amount": "479830490" - },{ - "owner": "BTSGq6JraoWw7kdg1wG6Q7hvJA8sXDYThUeh", - "asset_symbol": "PPY", - "amount": "4745380" - },{ - "owner": "BTSCeYfncC8aYQBNSyJp6JovgKQCdAQCoisN", - "asset_symbol": "PPY", - "amount": "1090830" - },{ - "owner": "BTS5KMARR5AN8DoJcnh2gxSrHMjeBFLPvov8", - "asset_symbol": "PPY", - "amount": "8610159" - },{ - "owner": "BTSGxqarqkUFGBrmzteDfcXw1EpyJwADJLcq", - "asset_symbol": "PPY", - "amount": "30115372" - },{ - "owner": "BTSQ3rXPQJMdBwvfS4YxiVVu39n9xEkcRghq", - "asset_symbol": "PPY", - "amount": "47096606" - },{ - "owner": "BTSLDs3JdXmMtBVG4AP5YNsSRpkHNXadRN3k", - "asset_symbol": "PPY", - "amount": "29030790" - },{ - "owner": "BTSNvrFEB9mA2rwQD2FJie7pxC19KN7QUeEh", - "asset_symbol": "PPY", - "amount": "003309" - },{ - "owner": "BTSNp5u4qtRehaRs2XrMoGaMGZvWqSkA65Zz", - "asset_symbol": "PPY", - "amount": "3178178" - },{ - "owner": "BTSEvamgZVJ7AoRbcqQn4kMzEtYhbeAPCkzC", - "asset_symbol": "PPY", - "amount": "11934037" - },{ - "owner": "BTS36d2F5bSSr72itFw1XNaacdDbjNyszFPn", - "asset_symbol": "PPY", - "amount": "161189" - },{ - "owner": "BTSGLsTDGoVuQypdosuScLU5SM6dFBFWkhmf", - "asset_symbol": "PPY", - "amount": "29490964" - },{ - "owner": "BTSEhyLzNK2TkwN7ZsaTBJPB5fWg41B3iyBh", - "asset_symbol": "PPY", - "amount": "21160200" - },{ - "owner": "BTSMVVJCyZnzCHNFAAfsnbVYhkGJM2VGdhYq", - "asset_symbol": "PPY", - "amount": "10095505" - },{ - "owner": "BTSDBMeA2QA3imsdwsSHmkAbGhu3WqDHBS2a", - "asset_symbol": "PPY", - "amount": "77085586" - },{ - "owner": "BTS9GvxY8CFRjVqGVdG8gsZNBnqPkd2xWCDC", - "asset_symbol": "PPY", - "amount": "203973" - },{ - "owner": "BTSKDucBVdJA3hcbtB1EJQ5NSMDbA8T3VJKA", - "asset_symbol": "PPY", - "amount": "696004" - },{ - "owner": "BTS65dzuXmsiYmbxUdL3N6oCKSM3ihJj6cNy", - "asset_symbol": "PPY", - "amount": "5252544" - },{ - "owner": "BTS2MgbgDkQWGXrg3zoJhX1Tx5TSykVBKKdR", - "asset_symbol": "PPY", - "amount": "25714286" - },{ - "owner": "BTSDfQMuQ23SAmQoKGvnsuTjFngSu116K8oQ", - "asset_symbol": "PPY", - "amount": "335631" - },{ - "owner": "BTSKmsCEzQuDueVXu4NxuNXVkUygfAXdgJ2n", - "asset_symbol": "PPY", - "amount": "317497" - },{ - "owner": "BTS5JDCobvUDsPTEWB4c9TpHjFNwcR47Emfp", - "asset_symbol": "PPY", - "amount": "3139372" - },{ - "owner": "BTSBHFLyJQDdHxgARKynfA5A2zDf9RzS5EfP", - "asset_symbol": "PPY", - "amount": "1710667" - },{ - "owner": "BTSArtVFJ2Eq63hu8MzzS5y7oqx8vvJDDx6A", - "asset_symbol": "PPY", - "amount": "34957880" - },{ - "owner": "BTSL8EBaZSwHou8qK4jtpR9Z2xqLsyhPKKru", - "asset_symbol": "PPY", - "amount": "4582351" - },{ - "owner": "BTSJCP49fJRUsjn6g3rBY7eeghn17eFRg6M6", - "asset_symbol": "PPY", - "amount": "9323778" - },{ - "owner": "BTS3oPdh394LwjDnAAop1iMSujLMwsBuHLjQ", - "asset_symbol": "PPY", - "amount": "001639" - },{ - "owner": "BTSJ4Q8RoFuNFjbNo8XwgwWSo4gzdX8hDrMU", - "asset_symbol": "PPY", - "amount": "1635209" - },{ - "owner": "BTS4bZemzCZdaXe9rwwJ9tHvXEc4e77xwXFW", - "asset_symbol": "PPY", - "amount": "481897" - },{ - "owner": "BTSGSi7KMRnidfepeLAJ822zXCLpMEQzpEk5", - "asset_symbol": "PPY", - "amount": "10001278" - },{ - "owner": "BTSB3ugJ1CUgGwMmn6jztg4vTjPdfLNdFe8N", - "asset_symbol": "PPY", - "amount": "4610509" - },{ - "owner": "BTS6A1Y8fHNQCRY9N5GQV9oiJcGEUudzZXmm", - "asset_symbol": "PPY", - "amount": "149873" - },{ - "owner": "BTSFY18wjvMtSNLRfcEorUJd7VVWReVjVt5Q", - "asset_symbol": "PPY", - "amount": "5136000" - },{ - "owner": "BTSKoMam57WgDrfi5LF4M6Vw6YyuE6WuUdkm", - "asset_symbol": "PPY", - "amount": "607164" - },{ - "owner": "BTS3Lw45ixjZKBHiG25AYH7EVLR9WFN967FD", - "asset_symbol": "PPY", - "amount": "566428" - },{ - "owner": "BTS2zS4RwnZHYuJkcn9gwCmhyqJ5k14ZC7HM", - "asset_symbol": "PPY", - "amount": "3435222" - },{ - "owner": "BTS9STtDwF1DoJqAmQ6PkQvN4mKGwXGvXdz7", - "asset_symbol": "PPY", - "amount": "39370092" - },{ - "owner": "BTSJjwowMr79auSsrRGGv8kF5atHESLC855B", - "asset_symbol": "PPY", - "amount": "947565" - },{ - "owner": "BTS31S9hGo84sXW5TXv6gftRXuVSMhdYC13B", - "asset_symbol": "PPY", - "amount": "16359223" - },{ - "owner": "BTSHwHFFLmpz7xcqtfrZ9SQZzj2nreYL9Qjz", - "asset_symbol": "PPY", - "amount": "10141746" - },{ - "owner": "BTSBjL3xHFLA9GPjauoje3HDtbZEfAEUmEfs", - "asset_symbol": "PPY", - "amount": "8886406" - },{ - "owner": "BTSNDnAJdTtNmggK2ARWTRjhASRwKAHBJnN4", - "asset_symbol": "PPY", - "amount": "58111944" - },{ - "owner": "BTS2ZnjyJWZcaZGxYMmhzZnG5o7pxsdkMndT", - "asset_symbol": "PPY", - "amount": "1889644" - },{ - "owner": "BTSGcZLqo8Pdq9h1i8SYin9bKpQVfp6k1tcb", - "asset_symbol": "PPY", - "amount": "5593314" - },{ - "owner": "BTSBSWssA3rZeScpJEkHdyGLWt3SGhVuLNHt", - "asset_symbol": "PPY", - "amount": "69175108" - },{ - "owner": "BTS3B16GFVJnao4MvoVP5CTziGRHDZdSpkqB", - "asset_symbol": "PPY", - "amount": "4926945" - },{ - "owner": "BTS6T7kTd8LFjLQJNSN5g9RRqPTLViHa8RMq", - "asset_symbol": "PPY", - "amount": "34971238" - },{ - "owner": "BTS9W3fyTga7qtJk88Nnq8uXN9N7ycxvhNpY", - "asset_symbol": "PPY", - "amount": "67800476" - },{ - "owner": "BTSBXJ9YzQFaFsaRJuBdbCihqndFn4tct76m", - "asset_symbol": "PPY", - "amount": "5965112" - },{ - "owner": "BTS3CQ1atWPx4MNxZeMmtiDpXW7i9V8xo5PY", - "asset_symbol": "PPY", - "amount": "1666710" - },{ - "owner": "BTSJjA8MDfBN2h8QgP5sAXzh9ZmzXKaZhjsq", - "asset_symbol": "PPY", - "amount": "624525" - },{ - "owner": "BTSBYueKmKiBTPBtwhonWuo2vXRNCF32tskc", - "asset_symbol": "PPY", - "amount": "2194417" - },{ - "owner": "BTSBG7RKBHzsV3GqyvLJd5oLVwnhDhsCs2Sh", - "asset_symbol": "PPY", - "amount": "1718067" - },{ - "owner": "BTSF2Vono4WMCy3QV6x17DrGnajpPWXtz9Yz", - "asset_symbol": "PPY", - "amount": "3994869" - },{ - "owner": "BTSDTFenQc5NiGGPgGaMZ865BzPNEXXWMRTM", - "asset_symbol": "PPY", - "amount": "2448101" - },{ - "owner": "BTS4vAgYRqymy7xLhPwVcjeHCDUDeSBSvHKc", - "asset_symbol": "PPY", - "amount": "16381272" - },{ - "owner": "BTS9NKvFdirdUSJMsptc7VQT9ZemLRgQ8t5L", - "asset_symbol": "PPY", - "amount": "68866286" - },{ - "owner": "BTSAmhrqcaXTJXeeThesLDLbsjFDK22B7dba", - "asset_symbol": "PPY", - "amount": "1675351" - },{ - "owner": "BTSExc1UdKHkuLr6N86HH44QjBNgkXSS6EfG", - "asset_symbol": "PPY", - "amount": "461145" - },{ - "owner": "BTS33ok77ZYW5ZX6eVEZ7a475bTWGqvJmU6A", - "asset_symbol": "PPY", - "amount": "2450664" - },{ - "owner": "BTS5fhYfJQrvtgniBqfW48QbFSLnUCBctRbY", - "asset_symbol": "PPY", - "amount": "23440765" - },{ - "owner": "BTSKrRNbwfbb8hsR55LJYKHZzcwVfqNcXWM7", - "asset_symbol": "PPY", - "amount": "2868698" - },{ - "owner": "BTS2xAB9j6VDhm6MZMHzBpL8cRjmz8z9uyDh", - "asset_symbol": "PPY", - "amount": "16635150" - },{ - "owner": "BTSLqZ1CAh48ggQWVe6pqNo1poiWrUJ7A4R8", - "asset_symbol": "PPY", - "amount": "10196153" - },{ - "owner": "BTS6sFszcZrZXUnFtKjsQ6xWoJN5sVZ7GhPk", - "asset_symbol": "PPY", - "amount": "131964137" - },{ - "owner": "BTSPyr5nFe2o2RifK89BTwuVxoghhXKwDPoM", - "asset_symbol": "PPY", - "amount": "2019905" - },{ - "owner": "BTSLWJfKZmxKKMt4McsBxoMeZxPqpihZRYam", - "asset_symbol": "PPY", - "amount": "38660681" - },{ - "owner": "BTS2vzSDVsJFv3VNg6kEtVytbWkB5CQpxkcC", - "asset_symbol": "PPY", - "amount": "26443320" - },{ - "owner": "BTSdWkvEaDVFvA75RjEyDXxqBJUJxYdV3RP", - "asset_symbol": "PPY", - "amount": "3094625" - },{ - "owner": "BTSBXXoVkcugJ9h5KPrfQoXDBxxEahcUXrWd", - "asset_symbol": "PPY", - "amount": "2750394" - },{ - "owner": "BTS8vv82HM5Es4QcPSPTr4pjWmXvskKeNv5M", - "asset_symbol": "PPY", - "amount": "23640000" - },{ - "owner": "BTSEBuAjv1Cs1Yu3dK1dTPRnpwvs9JXwo9J2", - "asset_symbol": "PPY", - "amount": "17261695" - },{ - "owner": "BTSJXDDsDJeUkrizmPsFu7uTXxtZgRrBCXbo", - "asset_symbol": "PPY", - "amount": "1739352" - },{ - "owner": "BTSBiyyM3t4HdTEd6LpuoZAtrYwJBgVjYwkR", - "asset_symbol": "PPY", - "amount": "1306721" - },{ - "owner": "BTS6cKNvfAA1hs3guuUzhY4bStvfr1EYkG4z", - "asset_symbol": "PPY", - "amount": "11401240" - },{ - "owner": "BTSLUszmLGynJkRPEEKtjjYGLWfE7n8iyqwy", - "asset_symbol": "PPY", - "amount": "2975199" - },{ - "owner": "BTSPcB3r68SKcGxvGm82hJfWq2EzqwdPcGHq", - "asset_symbol": "PPY", - "amount": "097436" - },{ - "owner": "BTS3Y42JQofWSCrFbgimroRQM77pNFdmszhc", - "asset_symbol": "PPY", - "amount": "12527394" - },{ - "owner": "BTS6PEpLd3K84eREWcFxan2khw4g681E6kht", - "asset_symbol": "PPY", - "amount": "16460794" - },{ - "owner": "BTSNFgDd1JQfTpKrapkJnAdvZ6LgQtaykRGP", - "asset_symbol": "PPY", - "amount": "6872267" - },{ - "owner": "BTS2aqMdkwWvwGemVPWeAJdrh5kLzhPUgaha", - "asset_symbol": "PPY", - "amount": "1245480" - },{ - "owner": "BTS3BrTNjQLVMtrtxVbduUaKymMCZqnpDdHJ", - "asset_symbol": "PPY", - "amount": "1079992" - },{ - "owner": "BTSmAHtoEGYQaJCajcZKWzN5Bq6ctYwTdjY", - "asset_symbol": "PPY", - "amount": "7159228" - },{ - "owner": "BTSMFYQpUkPPz9aKByrfwTd59VHugk4CHbFR", - "asset_symbol": "PPY", - "amount": "3012578" - },{ - "owner": "BTSA4YNTMaMnHmAVPPTDgiR3MEJbgSADCtSi", - "asset_symbol": "PPY", - "amount": "1469308" - },{ - "owner": "BTSJvNYBwNn3iiMjeKb3yHDD7KVpyoWkzf5a", - "asset_symbol": "PPY", - "amount": "7501048" - },{ - "owner": "BTSABRmirmcjV7U4vEVpAofFxz4hJh9LYYv7", - "asset_symbol": "PPY", - "amount": "3638247" - },{ - "owner": "BTSJ7v1tZFdVL3fzqwRVMwLtvcqtaw3vekQz", - "asset_symbol": "PPY", - "amount": "1692370" - },{ - "owner": "BTSHAXF7AGgRBzbgjberHiJtCFbPKaRNSDFy", - "asset_symbol": "PPY", - "amount": "1181513" - },{ - "owner": "BTSHWpA29umrwLDEeaEmEK32o9vVoWWMXzLa", - "asset_symbol": "PPY", - "amount": "779034" - },{ - "owner": "BTS4eHn7NiQnm1eHzTUmmyrFKqJNt5RSKAxY", - "asset_symbol": "PPY", - "amount": "2391548" - },{ - "owner": "BTSPoDS2nj9sLJm21S5jVzva7a6Hfm5s4PHy", - "asset_symbol": "PPY", - "amount": "1520435" - },{ - "owner": "BTSNzDBNb1kUmHfMkhPafNZJYy1ShfYUbfAa", - "asset_symbol": "PPY", - "amount": "1620968" - },{ - "owner": "BTS37Pgxq41f8fV9rLevLLYbDz7moYazn68E", - "asset_symbol": "PPY", - "amount": "10131039" - },{ - "owner": "BTS7aFLtUeNk9CEeCnwPN3RtgtNTzZpAzo4g", - "asset_symbol": "PPY", - "amount": "1379928" - },{ - "owner": "BTS5eEdv3aXwxPB4rRqB4ef7xCmpWQKuvan8", - "asset_symbol": "PPY", - "amount": "32371187" - },{ - "owner": "BTSNvkfraSKKLHvChfP6rXvNyDFFWifMUvZE", - "asset_symbol": "PPY", - "amount": "11810975" - },{ - "owner": "BTSKCkMKe6AYuxEmCDa4Yhv8TLKqSS1pmUqZ", - "asset_symbol": "PPY", - "amount": "3066890" - },{ - "owner": "BTSFeME2b9FKkMRTSxmLWLTUwbDKw3AsZuuY", - "asset_symbol": "PPY", - "amount": "19891013" - },{ - "owner": "BTS5eo4qWwLyg81knRDNK91wTfd7rXCS3MwW", - "asset_symbol": "PPY", - "amount": "1953412" - },{ - "owner": "BTSP6L1KJJ9hya5aHi6MjQZ6CZJv1FjXBThC", - "asset_symbol": "PPY", - "amount": "1308690" - },{ - "owner": "BTSBg27rgEXp6QJEzdNNyHicegeFpT34TTWt", - "asset_symbol": "PPY", - "amount": "8650405" - },{ - "owner": "BTSML2hAhVLeGKQx7hMt5DfnTitmY8GDgtDM", - "asset_symbol": "PPY", - "amount": "3266590" - },{ - "owner": "BTSC5Z7pjPxZR2ocbHCkoxek1xGh67erYYJB", - "asset_symbol": "PPY", - "amount": "13510009" - },{ - "owner": "BTSE4ZoDnLKRamj74nxRqPf9QqFoQEZSWhrM", - "asset_symbol": "PPY", - "amount": "3949530" - },{ - "owner": "BTS3fZDGKrBT9FHsTgrwcoAtF3r9cx7wsPPQ", - "asset_symbol": "PPY", - "amount": "2466543" - },{ - "owner": "BTSMrz4U6eRYrE6biA4iP1RZns9Y29emvrUT", - "asset_symbol": "PPY", - "amount": "494569" - },{ - "owner": "BTSCwM9mYTHhRjkQjCDNCuaovs6FK3p719fo", - "asset_symbol": "PPY", - "amount": "11767321" - },{ - "owner": "BTSMrUJKic5WW5EoRFdtHdy93faXgAMdoQ9", - "asset_symbol": "PPY", - "amount": "1175089" - },{ - "owner": "BTS7n4wNhQG2ouvuxphv4Yy8oRFzT4LCwwXt", - "asset_symbol": "PPY", - "amount": "979675" - },{ - "owner": "BTSA4E3hXCEJf6fr5QieXhiJoeTtqLqqvhHc", - "asset_symbol": "PPY", - "amount": "3673953" - },{ - "owner": "BTSMQcDvPdQkoXjmATbz7i8zQSigJz8wxRgM", - "asset_symbol": "PPY", - "amount": "67518095" - },{ - "owner": "BTSD7beaX61fRhAtnNyGPNnNdBH8H86wVbxo", - "asset_symbol": "PPY", - "amount": "12274010" - },{ - "owner": "BTSNaAHjtaKbgNgdpamPYixsGMBwyEs58dBr", - "asset_symbol": "PPY", - "amount": "10488914" - },{ - "owner": "BTS9gTGkEuCszWY4e42gx3eNVW8NYCWki5dq", - "asset_symbol": "PPY", - "amount": "17253750" - },{ - "owner": "BTS4BkH4d74HN1WNVYTHN6XKqALwyxa4eseB", - "asset_symbol": "PPY", - "amount": "1677682" - },{ - "owner": "BTSP2Txs7WkR7XftYjKde4PTxGVQyCXGsgKh", - "asset_symbol": "PPY", - "amount": "4481189" - },{ - "owner": "BTSKLStxrXfwXAjLZtZDuYAyXMRmJS1WBCPL", - "asset_symbol": "PPY", - "amount": "2880609" - },{ - "owner": "BTSM7jtzAox3ixi3pPKSPamqTQ5TRmakgW2c", - "asset_symbol": "PPY", - "amount": "996433" - },{ - "owner": "BTS5nHhC1f6ekRbRcNfVo8qaXoL1imBbHt8o", - "asset_symbol": "PPY", - "amount": "1462928" - },{ - "owner": "BTS9ueDweMEYKQUVy5oeqkYPRibkHGioQL39", - "asset_symbol": "PPY", - "amount": "020455" - },{ - "owner": "BTSBejdf99AdykRoYXXsGw77vYqsrByvsU8Z", - "asset_symbol": "PPY", - "amount": "13226368" - },{ - "owner": "BTS5DttXH9EupVXyqr6Acghu9rvAuJfev5fq", - "asset_symbol": "PPY", - "amount": "27067306" - },{ - "owner": "BTS2JNLs94efKPkwjcqD9NriMVnLnPkgfBj2", - "asset_symbol": "PPY", - "amount": "003149" - },{ - "owner": "BTSEHPYqqcco5r9fs8UFyLrgoa6A73sRZVMw", - "asset_symbol": "PPY", - "amount": "3078476" - },{ - "owner": "BTS4XgaW6V39rBYtgaiMKfUcJksC9k1vm9Mz", - "asset_symbol": "PPY", - "amount": "597496" - },{ - "owner": "BTSBxc51VzydrpzDFBBcUXCXXhCPcWqmpoq3", - "asset_symbol": "PPY", - "amount": "10114157" - },{ - "owner": "BTSBswrcaGt4fvnP9hvmEXCvaPsDEsHBSX9o", - "asset_symbol": "PPY", - "amount": "34971238" - },{ - "owner": "BTSNLf1p43Bps6DdZquBUhgL5YfDsTEumZcd", - "asset_symbol": "PPY", - "amount": "82527586" - },{ - "owner": "BTSMcJNq6Twie9R5E6W8yH6fdfLamxNYbjX5", - "asset_symbol": "PPY", - "amount": "400800" - },{ - "owner": "BTSFT6R22CLztwrvLqD8HM1dZ6qnLyatewCH", - "asset_symbol": "PPY", - "amount": "1739604" - },{ - "owner": "BTS8MHiELYK9G8YaKHEYxYiLqXq53f8mdshJ", - "asset_symbol": "PPY", - "amount": "169929667" - },{ - "owner": "BTS2wYoH9JK2XYsJXguddHp1TbXuNARzderV", - "asset_symbol": "PPY", - "amount": "963278" - },{ - "owner": "BTSCJq9LndN7E5WVHb55jMrWHUainfqDbs9k", - "asset_symbol": "PPY", - "amount": "487302044" - },{ - "owner": "BTSKoNWJH5g8P4xfqfzcXTE6MBt6fy5jrxnf", - "asset_symbol": "PPY", - "amount": "2357804" - },{ - "owner": "BTSDvWmbgCs87WGyUqJH95Fi1S5zmvZnHiom", - "asset_symbol": "PPY", - "amount": "3464876" - },{ - "owner": "BTSKFAmtSzk4TAwXwiUyhdSRB9PuudZNDAVN", - "asset_symbol": "PPY", - "amount": "101933486" - },{ - "owner": "BTSLErGEtFS26zq4PjLRSgEWAfWeNkU6a9Pn", - "asset_symbol": "PPY", - "amount": "2066960" - },{ - "owner": "BTSPHnziXFxs6DigVUdscsUVot3oHP4JYPSy", - "asset_symbol": "PPY", - "amount": "43702493" - },{ - "owner": "BTSFVFhvwKSsZL2pH9aotXRf7sCuYTnLvADK", - "asset_symbol": "PPY", - "amount": "3194000" - },{ - "owner": "BTSGWZgcLi3bqBmU2tBTbqgaf6Q42NDE4Nh8", - "asset_symbol": "PPY", - "amount": "059094" - },{ - "owner": "BTSHvmNjAWFSdb9mTSN9pDFjLDxkDSnqWRRF", - "asset_symbol": "PPY", - "amount": "3680985" - },{ - "owner": "BTSHHRB9LDwBu9NQ52ww1QkZfH3w7aWEfwaG", - "asset_symbol": "PPY", - "amount": "11990018" - },{ - "owner": "BTSNxbxHrKbSrzHgDvVqQ8ZxgEV8RpmQ1K1G", - "asset_symbol": "PPY", - "amount": "333923" - },{ - "owner": "BTS9zSWdRAJjHoireXruhMe2q9iHZrfnXERt", - "asset_symbol": "PPY", - "amount": "65012952" - },{ - "owner": "BTSPQVY53R9D1MGHvipmXv7q6RA13Cvu2E7G", - "asset_symbol": "PPY", - "amount": "1502873" - },{ - "owner": "BTSENVDaMX9L8MG7VVzCcKyuX4DkkQJkzyFW", - "asset_symbol": "PPY", - "amount": "21186379" - },{ - "owner": "BTSLRED7CuY5z436s2UK6Zog2zjj1pe4XRLb", - "asset_symbol": "PPY", - "amount": "10066938" - },{ - "owner": "BTSGUhxvvKJ5PrMEB34ojGjZeHDNBMR47Fbh", - "asset_symbol": "PPY", - "amount": "9419757" - },{ - "owner": "BTSMWxogiQrHDQJRCju8wV2aVG7VXJEfQXJ8", - "asset_symbol": "PPY", - "amount": "1740364" - },{ - "owner": "BTSL8gGCWvQzwWtCZ9A7WouoJHyWqY9AowRX", - "asset_symbol": "PPY", - "amount": "1734251" - },{ - "owner": "BTSD48qdjJWRmYcyZqpPQkTkjZgzbZnu3R86", - "asset_symbol": "PPY", - "amount": "27175400" - },{ - "owner": "BTS6JADBYq1jKry3zA2Zf8AYRRqvtJdS5wB7", - "asset_symbol": "PPY", - "amount": "021918" - },{ - "owner": "BTS7EoaCGnZksK8neyfSHKNgkfLoKsoCqmRH", - "asset_symbol": "PPY", - "amount": "1321125" - },{ - "owner": "BTS9SQXDwXPKwujsQGFezggPrWscKNT8cgBT", - "asset_symbol": "PPY", - "amount": "146577462" - },{ - "owner": "BTS2WRbMd8jQJ4DsabHaWtovXJtCQZjAXr9q", - "asset_symbol": "PPY", - "amount": "1890000" - },{ - "owner": "BTS5a7B2nDy7CyZZeYA8oK889yU2s8m8g6LT", - "asset_symbol": "PPY", - "amount": "384548" - },{ - "owner": "BTSPhSM1JazgPQGx3reJLgsVAMKQcKaKHYKZ", - "asset_symbol": "PPY", - "amount": "6676947" - },{ - "owner": "BTS4QWuvsgSnuFgQUkq2qKKftPb2u4oymSi5", - "asset_symbol": "PPY", - "amount": "13861364" - },{ - "owner": "BTSDA5qbPdWFQmNPUv1RW3f9AnPmnmPB6ub2", - "asset_symbol": "PPY", - "amount": "41892000" - },{ - "owner": "BTSEDH1cG7AGAW42wrdfLxZx83DRTFNEdj73", - "asset_symbol": "PPY", - "amount": "4640616" - },{ - "owner": "BTS2BgFzpkeNtUfg5Yh2YGqqHUc2wUsBbYWW", - "asset_symbol": "PPY", - "amount": "1072324" - },{ - "owner": "BTS4YKCXABJUdQPefULHnhCtgKHLpwaQL5Ub", - "asset_symbol": "PPY", - "amount": "957437" - },{ - "owner": "BTSNgK6vTKEH1ntRqW6pgoSaMH3YB9XXaybo", - "asset_symbol": "PPY", - "amount": "9040288" - },{ - "owner": "BTSMDD3QsmYM5fsE3XvcWZ4HkU3CXSxrpd9", - "asset_symbol": "PPY", - "amount": "2676614" - },{ - "owner": "BTSHDJHjov1KDB2q8Z59y1yZEMPR62mu8xiQ", - "asset_symbol": "PPY", - "amount": "81829072" - },{ - "owner": "BTS7T5NXb89jM9TLaCZEfvTmG1oNQiDiUtPn", - "asset_symbol": "PPY", - "amount": "1374853" - },{ - "owner": "BTSFpxwEUsCAjNA7xa6PhC2EYmaDaiL3szoV", - "asset_symbol": "PPY", - "amount": "3615935" - },{ - "owner": "BTSRfzoiWDQLkwfMuy2QZgiqG5heNqSyYmX", - "asset_symbol": "PPY", - "amount": "84151371" - },{ - "owner": "BTS6S6eSCz3juYK3Lw3fqeK1JwcSCdJh5Fgf", - "asset_symbol": "PPY", - "amount": "11308900" - },{ - "owner": "BTSFQAnUU2ATxvbKoLagXiqXaBi5PJArBeYv", - "asset_symbol": "PPY", - "amount": "1167091" - },{ - "owner": "BTS6oDYKuJDTfoZz4mvVySzsjHnXq4iU719x", - "asset_symbol": "PPY", - "amount": "965091" - },{ - "owner": "BTS8uUSuy5uUtvmowaC2r4NkxErsGf6jtCs7", - "asset_symbol": "PPY", - "amount": "001150" - },{ - "owner": "BTSQ2UiF7WZ3f3s54K9RHQ43Aw1wPTNgLbz4", - "asset_symbol": "PPY", - "amount": "2033814" - },{ - "owner": "BTSA4XNsSdtaxrcqbLnp1Lwc7JZ9jE5CLJt5", - "asset_symbol": "PPY", - "amount": "23887307" - },{ - "owner": "BTSKyMmi1G4g6qC15CBpw74odqdtArAfNvsy", - "asset_symbol": "PPY", - "amount": "3435886" - },{ - "owner": "BTSAmQ5FB7dyYnbUkbmmWeZ5jfsAFAKuP6nm", - "asset_symbol": "PPY", - "amount": "200719" - },{ - "owner": "BTS2h7xtRhghok77YF435Un5sBsxuJYd1Qcz", - "asset_symbol": "PPY", - "amount": "14282429" - },{ - "owner": "BTSHzFjM27BAGQdRnr1PdSu2VSB71Q34S9Bg", - "asset_symbol": "PPY", - "amount": "5983470" - },{ - "owner": "BTSBhjq6xpum2527j7svNeSiFUbtNiRdhC2T", - "asset_symbol": "PPY", - "amount": "377615" - },{ - "owner": "BTS8ZCqrHJ18Pt7YbyAcvPsrTArB7wBJPCzc", - "asset_symbol": "PPY", - "amount": "6289744" - },{ - "owner": "BTS6SEjxQK2vaZrNwi1hYSGv6aRf5ur8Tun1", - "asset_symbol": "PPY", - "amount": "755325" - },{ - "owner": "BTSGn2198DBzrQncBuVoJLCCefyLE6S8ZuFY", - "asset_symbol": "PPY", - "amount": "23639767" - },{ - "owner": "BTS7hfhvDwLw2bMsD8UjozzoipLUyiiFPkr9", - "asset_symbol": "PPY", - "amount": "16560303" - },{ - "owner": "BTS5LgmdZJqNejQyFV7JbsvmXLVXkWLyx4bu", - "asset_symbol": "PPY", - "amount": "4048184" - },{ - "owner": "BTSFwZHKFKhtPiZqGG4UeXf35JA7QbfUYiP4", - "asset_symbol": "PPY", - "amount": "46588239" - },{ - "owner": "BTSCvAetYkJXg39GnrxTqyrR63QfrzUEoeF7", - "asset_symbol": "PPY", - "amount": "6641301" - },{ - "owner": "BTSJwMP3K6Dxg5N8tiUkW6HCK6gF5E8SbGR4", - "asset_symbol": "PPY", - "amount": "16422451" - },{ - "owner": "BTS8bvtU51pgCmgJSaSkaqv7nhXa2hn6HhSH", - "asset_symbol": "PPY", - "amount": "6865415" - },{ - "owner": "BTSKJbWMeFxoG219HV92cimADgm28W6YbYmW", - "asset_symbol": "PPY", - "amount": "72518840" - },{ - "owner": "BTS8H5WvvptmQPwFXe1Dc35yr2YPQ56H4994", - "asset_symbol": "PPY", - "amount": "15940059" - },{ - "owner": "BTSF25cg2r8RQUgnZ79bmeoFEsTpHfWRmMUm", - "asset_symbol": "PPY", - "amount": "7931275" - },{ - "owner": "BTS9urTtLwcEs5rH6AtgAxTFXHwkW2F7siwg", - "asset_symbol": "PPY", - "amount": "24324883" - },{ - "owner": "BTS7HgwoWZ4fJaXsoJEAb3MF71yYo6RJBnhd", - "asset_symbol": "PPY", - "amount": "34327045" - },{ - "owner": "BTSDNfYCFTSF41SVXemxA2wSFXjX1Urkb2YK", - "asset_symbol": "PPY", - "amount": "18613653" - },{ - "owner": "BTSEihfLBnFHHeV7iAgDi3dX6fXTd8gphQy2", - "asset_symbol": "PPY", - "amount": "14508555" - },{ - "owner": "BTSEWquX51ZMqY2nVLPfETJqyx6SrXmupbd1", - "asset_symbol": "PPY", - "amount": "49852788" - },{ - "owner": "BTSKVjDA8ziDsbKnxBCMpC4HJAGUYnXHehLC", - "asset_symbol": "PPY", - "amount": "3376134" - },{ - "owner": "BTSBFQVFKvX8bVpFdHCwa68TswpZ6H9Lufaq", - "asset_symbol": "PPY", - "amount": "12000367" - },{ - "owner": "BTS3iLKTdZ2AK6ynF5RNKVGykLxg5uQzNxww", - "asset_symbol": "PPY", - "amount": "49755646" - },{ - "owner": "BTSC9w4rgK7CLqzG5pvKkWsfgN2nQUVUg6wu", - "asset_symbol": "PPY", - "amount": "36758135" - },{ - "owner": "BTS3SbuZh8cd2R1nR7J9842XEhLU8nC6EF6Q", - "asset_symbol": "PPY", - "amount": "335939" - },{ - "owner": "BTS62DPUKoExQMYUeg1nH7DqBoQgCYATGcZF", - "asset_symbol": "PPY", - "amount": "1229265" - },{ - "owner": "BTSEST8s5JN5tCSsTSJnPJ5d3YKa2YteyV4R", - "asset_symbol": "PPY", - "amount": "30942000" - },{ - "owner": "BTSGWUDfqurbVVNGHURDkNtUkyxTYVNzQp8X", - "asset_symbol": "PPY", - "amount": "166221459" - },{ - "owner": "BTSAriQusw4d9tJ2V3c7GwdF6jXX97HPruTS", - "asset_symbol": "PPY", - "amount": "199424" - },{ - "owner": "BTS2sZx63r9JdJcTRoP8LTaC5gjgghFrEX3S", - "asset_symbol": "PPY", - "amount": "6895054" - },{ - "owner": "BTS8NPs3Bauyj2mtS1Beawp3bLAFPeVWZrcr", - "asset_symbol": "PPY", - "amount": "248233712" - },{ - "owner": "BTSGjm4HAkkhoJToVzoJ61iXcdKxBUpuQi6M", - "asset_symbol": "PPY", - "amount": "9361283" - },{ - "owner": "BTSF1h1xrGAveBVUc2mUHNobFRRHDAHuNsHH", - "asset_symbol": "PPY", - "amount": "329720" - },{ - "owner": "BTSJgKgXCiJXZBLRL81uJ3YScwGVp1tdjBD8", - "asset_symbol": "PPY", - "amount": "128304677" - },{ - "owner": "BTSJ9rsY2Vy3zBogVoQT6piv8zHa2yzSUAAC", - "asset_symbol": "PPY", - "amount": "2081785" - },{ - "owner": "BTSMq4ST5znHiQrP5dGZzZaJQBBGY1dAkQej", - "asset_symbol": "PPY", - "amount": "66696239" - },{ - "owner": "BTSQ5RcW4bxKzRTSn5ZCwEvgyvdNbmBpyYx1", - "asset_symbol": "PPY", - "amount": "34436434" - },{ - "owner": "BTSJCCMzUBSD8PuwvaPG9yaVJkKUrzemiKKH", - "asset_symbol": "PPY", - "amount": "258270" - },{ - "owner": "BTSE8YCpGQ4grHUqFcWk7cZSwJ3mzvspN7HC", - "asset_symbol": "PPY", - "amount": "15732843" - },{ - "owner": "BTSAVJhwGViffm1JMw48EZJpLDdN3qaLN5a1", - "asset_symbol": "PPY", - "amount": "9567821" - },{ - "owner": "BTS9iNLYPp6vDzPHJFWSXrhy9CxhaLZi9EJb", - "asset_symbol": "PPY", - "amount": "6594738" - },{ - "owner": "BTSLRbmzQCa5j3zTWyR31udLvhuni6Ag7d73", - "asset_symbol": "PPY", - "amount": "4715254" - },{ - "owner": "BTSb7LCRgfVEyoD3McsBeY81fswkPUJWgNs", - "asset_symbol": "PPY", - "amount": "22655719" - },{ - "owner": "BTS4A1Nko4Y6AgZuSkbRRFUugRz9uvpws962", - "asset_symbol": "PPY", - "amount": "5695436" - },{ - "owner": "BTSFfd8daPKwDTHvNJHdHzQHdRBZwUpurG7f", - "asset_symbol": "PPY", - "amount": "28448557" - },{ - "owner": "BTSDGnr9W7ht4YYo7KeBaGAwyEeiJVVrBND9", - "asset_symbol": "PPY", - "amount": "102048286" - },{ - "owner": "BTS7r4fXRxAsTUCqSME2v6gpCPvBVwvFksvP", - "asset_symbol": "PPY", - "amount": "50042474" - },{ - "owner": "BTSAm1KuVnsk8J4rKwuy9wnCx7y85HbRVuQd", - "asset_symbol": "PPY", - "amount": "10974623" - },{ - "owner": "BTS9kTroU6sUd439NkEGN4vG7faRZ1pmbRXK", - "asset_symbol": "PPY", - "amount": "5993169" - },{ - "owner": "BTSqZ5iKxTEUhaUJWJ1FkDNAHqcFbgpmn6y", - "asset_symbol": "PPY", - "amount": "95619783" - },{ - "owner": "BTS2DsaJ8pwuEr9SMe7uH1g9j9dCoNTWgtRc", - "asset_symbol": "PPY", - "amount": "208079" - },{ - "owner": "BTSDPZ82U3hdfTAcnczwXKLvqymXfP8yBKgP", - "asset_symbol": "PPY", - "amount": "19150080" - },{ - "owner": "BTSNEN8G5epTLAgmbR1tQMJT3y7hNiCxFFMp", - "asset_symbol": "PPY", - "amount": "814615" - },{ - "owner": "BTSJ9upfqHDmZEuhAFkTCnZmNvEYsPidNejU", - "asset_symbol": "PPY", - "amount": "3409231" - },{ - "owner": "BTSCCtVXJJ1dT2mftEwtBTa49zH7j1rstadL", - "asset_symbol": "PPY", - "amount": "34554900" - },{ - "owner": "BTSHBYZku5okVNJWumFcgXbCe4FcyraiEQNf", - "asset_symbol": "PPY", - "amount": "1973706" - },{ - "owner": "BTS61KfsXuHWH58Pepgg1j2Xnt2SEt3A8Bp4", - "asset_symbol": "PPY", - "amount": "2301347" - },{ - "owner": "BTS53g7qU4d8dDpyQRkebM1r8WCWVpDR7w4B", - "asset_symbol": "PPY", - "amount": "1737323" - },{ - "owner": "BTS3oeqTkkZF4oH491Mp9qoDXBJPv1Rq97y8", - "asset_symbol": "PPY", - "amount": "2206739" - },{ - "owner": "BTSAixGPqHBQhk1SyLD6qEQPMXZjv3mWy9Y4", - "asset_symbol": "PPY", - "amount": "1528986" - },{ - "owner": "BTS5o6r3MwCEE17TYXT8kVMXGeCcGFViB17o", - "asset_symbol": "PPY", - "amount": "10041152" - },{ - "owner": "BTSJrVF5VnfwA21kNwRwtnzhZkJJsCCPLVU4", - "asset_symbol": "PPY", - "amount": "21760748" - },{ - "owner": "BTSDsDqQ5x4LekUsH2z8v5eoeasTdBdkMwD7", - "asset_symbol": "PPY", - "amount": "8411433" - },{ - "owner": "BTS2bX4KahoFkS52nQNGDnbrrgUJej2kLc7M", - "asset_symbol": "PPY", - "amount": "32188980" - },{ - "owner": "BTSBcJKNnwGVry4xmg2HQmFS5Lc9cm4KykLU", - "asset_symbol": "PPY", - "amount": "1748244" - },{ - "owner": "BTSAhzscfiJT5CDmxiSwyw47D6zscDM8M15p", - "asset_symbol": "PPY", - "amount": "5013528" - },{ - "owner": "BTS8qDFW6cf99Co28mwNSMVYw1mndkerNAkz", - "asset_symbol": "PPY", - "amount": "34285714" - },{ - "owner": "BTS3FfLu5So8giqEH65Zjxo9kzwzLtRjn7uB", - "asset_symbol": "PPY", - "amount": "130466960" - },{ - "owner": "BTS9N7AJqHkRbBqW6o2gYZ6fRpJKxbcBkr3s", - "asset_symbol": "PPY", - "amount": "4900199" - },{ - "owner": "BTS2Rpb9CLBuiL4o7vgRxMZCH1N1R7ndXzg3", - "asset_symbol": "PPY", - "amount": "3432355" - },{ - "owner": "BTSJoGo84Zk4D4tSUJ4FfKtCBGpWqHemsAQR", - "asset_symbol": "PPY", - "amount": "338984" - },{ - "owner": "BTS5xkXZUMAVqCC6oXDhrUXV6MvfHmQXgxbx", - "asset_symbol": "PPY", - "amount": "681505" - },{ - "owner": "BTSBPpbkroZcEcvECfdhqNrKPQHidtXq9KG", - "asset_symbol": "PPY", - "amount": "5222550" - },{ - "owner": "BTSEzKL3bmV499AcPRRpU35ifer3xpj2fpMN", - "asset_symbol": "PPY", - "amount": "005752" - },{ - "owner": "BTS5wma5cCbHWKxRvzhnJcMW2M4JSb2wJCbm", - "asset_symbol": "PPY", - "amount": "1785112" - },{ - "owner": "BTSJTEqGzmSmBpC6Y9SCDrN9v4LfTpdPoUFR", - "asset_symbol": "PPY", - "amount": "533962" - },{ - "owner": "BTSCN49vqbcN78JsdgHjQo2VytxCjWEWnFiy", - "asset_symbol": "PPY", - "amount": "2001647" - },{ - "owner": "BTS46c2NjdnPrir2FyiY5KVhtQrT4zpAGtKD", - "asset_symbol": "PPY", - "amount": "229494" - },{ - "owner": "BTS5ikMFJUmwYY946CuPxnyTDSTbEE6w8VwA", - "asset_symbol": "PPY", - "amount": "2330175" - },{ - "owner": "BTSGuVdMAhYpJyTx8ArtGfzovvN4xNGnDLcX", - "asset_symbol": "PPY", - "amount": "824958" - },{ - "owner": "BTS5Kw11tZpM9pNiX4eMxGGm3Zwm88FXAsxL", - "asset_symbol": "PPY", - "amount": "2642295" - },{ - "owner": "BTSB2bfcSQQyMY8aYJ1hSYENqeVjkMSQVQ8W", - "asset_symbol": "PPY", - "amount": "1869643" - },{ - "owner": "BTSJRYtoxQXyx4J9pk8qvi1wT4N2QBKPb318", - "asset_symbol": "PPY", - "amount": "1511633" - },{ - "owner": "BTSG1TkT37ZegajMaNW7SjqriWpQJfco5DsR", - "asset_symbol": "PPY", - "amount": "058728" - },{ - "owner": "BTSAQhtin4EN2oaFUiDF1AKPCt6Mp4ksSK3A", - "asset_symbol": "PPY", - "amount": "3820158" - },{ - "owner": "BTSAWDhqhSZYHVdt5qceuwEBh5aVb6EyRfAC", - "asset_symbol": "PPY", - "amount": "206507" - },{ - "owner": "BTSNpwcA22tkhTCPbUjtaduYB2n9fAiCVfRN", - "asset_symbol": "PPY", - "amount": "18583330" - },{ - "owner": "BTSpdS4691rBSbWefDjwAt2LZcPR8qG934e", - "asset_symbol": "PPY", - "amount": "3840818" - },{ - "owner": "BTS6ZPVfofaMMzsZL9uJweQKiBoXNYszjTmV", - "asset_symbol": "PPY", - "amount": "1764146" - },{ - "owner": "BTSD48fc5dMhtmBKcnN5F9BjHp9ogZ38VHfC", - "asset_symbol": "PPY", - "amount": "683817" - },{ - "owner": "BTSFEM8C51RSroLucZSBAw6MofK8JrAWhBJw", - "asset_symbol": "PPY", - "amount": "34792076" - },{ - "owner": "BTSPjwJDABRpqMk6zLv8iKUpabDJnwYXAaVA", - "asset_symbol": "PPY", - "amount": "13915951" - },{ - "owner": "BTS7arMBnpBNhsAi6FsD8YuXH6MMWhW7nhjL", - "asset_symbol": "PPY", - "amount": "33909379" - },{ - "owner": "BTS37feqYzKSFuxbPxAWt1vi8F1vo2MgwUrj", - "asset_symbol": "PPY", - "amount": "14827882" - },{ - "owner": "BTSPb1uVEXvP3zT6NqZH7sumsF2x41STySwh", - "asset_symbol": "PPY", - "amount": "9176741" - },{ - "owner": "BTS6KSrokh96bF72fcb4NZnkqwjdZM3fQ7dL", - "asset_symbol": "PPY", - "amount": "10183003" - },{ - "owner": "BTS587Gzn4VSbhrTfuBRig8DXdZ1P7fGiNVK", - "asset_symbol": "PPY", - "amount": "33900238" - },{ - "owner": "BTSAmYYhGSh4mFvLTB9MhPsN4DzjqnsyPogv", - "asset_symbol": "PPY", - "amount": "4705980" - },{ - "owner": "BTSFiuJx6T3sCLMXAZZc2qWW6M7dZeUjBptg", - "asset_symbol": "PPY", - "amount": "1571053" - },{ - "owner": "BTSJozQKVdP8xjq2Ty3Y6SB6a2FhBRZZjpou", - "asset_symbol": "PPY", - "amount": "3313725" - },{ - "owner": "BTS7ajiVnWeaypqoJi9mEDF5rFgypQPnZksd", - "asset_symbol": "PPY", - "amount": "20339800" - },{ - "owner": "BTSFsWX8oVn7J1hhxznJubyEyfmBnGDTT6N9", - "asset_symbol": "PPY", - "amount": "23638297" - },{ - "owner": "BTS53hg7TEUNoqi8ENU8ywa5d36bEwH6aoLa", - "asset_symbol": "PPY", - "amount": "16816788" - },{ - "owner": "BTSGW66HEFogJKR1oDmXEJsqP2GKuPt1g2CR", - "asset_symbol": "PPY", - "amount": "21057012" - },{ - "owner": "BTSG5TxVrWryLdmHnp926qnw5F7qSdJxEf7V", - "asset_symbol": "PPY", - "amount": "8448862" - },{ - "owner": "BTS8xhf9TN4LSKK7PwYKB36Jjmdr9sw56ssw", - "asset_symbol": "PPY", - "amount": "26229142" - },{ - "owner": "BTSC4Ac6wPKeeHQUx7EDPMPvYuBJUzppNKJB", - "asset_symbol": "PPY", - "amount": "1397751" - },{ - "owner": "BTSG1or37CrzrsRrPEycQn7znAR2YDAkMefT", - "asset_symbol": "PPY", - "amount": "499083" - },{ - "owner": "BTSHtY8vFg1UpYPyp26BNR9K8QxqG3Cr5jiP", - "asset_symbol": "PPY", - "amount": "9873988" - },{ - "owner": "BTS9jmsDi6cbfUhsgd2vcxPjavK27EUVP6Y", - "asset_symbol": "PPY", - "amount": "1736597" - },{ - "owner": "BTS8Zx2aBnpSgRNsP4PhzzooBg3qG6n2nNMo", - "asset_symbol": "PPY", - "amount": "4067832" - },{ - "owner": "BTS3kDymwpL55iLgg8pyQLKAeX4YWgSeTbJr", - "asset_symbol": "PPY", - "amount": "850891" - },{ - "owner": "BTS9vg1Hea5EQGjWCMZo7WU3B71aBccDz37W", - "asset_symbol": "PPY", - "amount": "2499560" - },{ - "owner": "BTS7TzE3ReWUCT7VYVAWnzWtmPULUJnCLcaP", - "asset_symbol": "PPY", - "amount": "1738790" - },{ - "owner": "BTSLXVffLjY4pYWp3ztF1w6Gcg1zHvoWdsfm", - "asset_symbol": "PPY", - "amount": "3167060" - },{ - "owner": "BTSPaz3XDWHzvdvdZMKzVD1BGTeXGHimatV1", - "asset_symbol": "PPY", - "amount": "46808999" - },{ - "owner": "BTS2R3hYjGynZVdeiadS6JmbfYj6Xig6e743", - "asset_symbol": "PPY", - "amount": "154235" - },{ - "owner": "BTSCiUCEmA5FwuDUofS79QV4hzGUcb5F3Gkf", - "asset_symbol": "PPY", - "amount": "1202776" - },{ - "owner": "BTSAYEgGougiJovQPeBgnAsajNzUijoVcQ5H", - "asset_symbol": "PPY", - "amount": "2087555" - },{ - "owner": "BTSExCC6riz6kXVPgdjZ3FNsJq6jnPTWLmTX", - "asset_symbol": "PPY", - "amount": "211586" - },{ - "owner": "BTSGcWVECYpXpQxWcCKJo3QHUrqUXwtsWMoG", - "asset_symbol": "PPY", - "amount": "085842" - },{ - "owner": "BTSHaTQ7V2FdruV9sHTd92NijLLFEWorad4e", - "asset_symbol": "PPY", - "amount": "107748194" - },{ - "owner": "BTSBzTQu6Xkgd7WA56gZxqv7nx5eqFv37iR1", - "asset_symbol": "PPY", - "amount": "627341" - },{ - "owner": "BTSCtDLUmcbG2JBofZspbuLb8Q9euyv2WevZ", - "asset_symbol": "PPY", - "amount": "080794" - },{ - "owner": "BTSFnkqPJHUGwmFHhrwLAMdTqQ7eAEL3efG6", - "asset_symbol": "PPY", - "amount": "10039332" - },{ - "owner": "BTSH8zpVuxPfyqRVM85rv6Lh29Vf4XiCTcM", - "asset_symbol": "PPY", - "amount": "12338310" - },{ - "owner": "BTSNxu1fTrAyVMW2aLBRrpAshWzXuscAnQWu", - "asset_symbol": "PPY", - "amount": "1609956" - },{ - "owner": "BTS12oMWk88tgAa6s1tHyrw6ZjBv6Nb1xNS7", - "asset_symbol": "PPY", - "amount": "3435717" - },{ - "owner": "BTS4FBSwV2JfbNVwGyZ7KWDp1AVSN9AvkDF4", - "asset_symbol": "PPY", - "amount": "549775" - },{ - "owner": "BTSK47WJbigAzfVviCFJySjQUVptnMweDP79", - "asset_symbol": "PPY", - "amount": "375955" - },{ - "owner": "BTSGzN8gdRfBJ71WCHsPsRrYZyby9eS3ozR7", - "asset_symbol": "PPY", - "amount": "2139759" - },{ - "owner": "BTSJtvmqKFZiTWXuEc6pg65xiorhWCJFXK4h", - "asset_symbol": "PPY", - "amount": "2977354" - },{ - "owner": "BTS5QPMLKgfShWCpZdZt33AXVbVrGE7rJoKu", - "asset_symbol": "PPY", - "amount": "2618008" - },{ - "owner": "BTS61R5UyR7GNpo6kReoNejWKyuUteF7hXuC", - "asset_symbol": "PPY", - "amount": "2398816" - },{ - "owner": "BTSCSre9Ti8HFhbMNZF35E2ovzpHwFvf71ua", - "asset_symbol": "PPY", - "amount": "1571220" - },{ - "owner": "BTSM5MrrmwPPfwvMkDrzgryNXWkdtfwqQrSL", - "asset_symbol": "PPY", - "amount": "26935246" - },{ - "owner": "BTS2i4DQw4TVjf99is1U7pE2UD52ZqbSTJ5W", - "asset_symbol": "PPY", - "amount": "3231394" - },{ - "owner": "BTSL7Via8a7NE2kvwuQvMrHeUB4uCucr7Z22", - "asset_symbol": "PPY", - "amount": "51172991" - },{ - "owner": "BTS4MyFscnmffmCmtzMTeHktjELXSxor3349", - "asset_symbol": "PPY", - "amount": "2500156" - },{ - "owner": "BTSPF9V7LyVkDhQev3BD3B1i6g3uq7Ty4SwP", - "asset_symbol": "PPY", - "amount": "5561584" - },{ - "owner": "BTSGV5C7k9BZnzgpTR82FvAxzMZYndhKBwV8", - "asset_symbol": "PPY", - "amount": "473686" - },{ - "owner": "BTS3n8pocywZpiJjhvRfJz71sCmYCsiBQZD6", - "asset_symbol": "PPY", - "amount": "86815580" - },{ - "owner": "BTSL9rHxY22YE9gWzWYgEobFFdueNCj5jbKz", - "asset_symbol": "PPY", - "amount": "6345020" - },{ - "owner": "BTSYr42D9KbdG1PX617P88qLfrBb9Xpam1n", - "asset_symbol": "PPY", - "amount": "276415" - },{ - "owner": "BTS2nCms7CSBzkbbM87GVMQZhrmSLba2WUWJ", - "asset_symbol": "PPY", - "amount": "67658821" - },{ - "owner": "BTSLgXSXjsuZopxJjqeRMj6KnuSzDxMFhh19", - "asset_symbol": "PPY", - "amount": "10249488" - },{ - "owner": "BTS6P2bkKtfxPrjDQ3B87Cok4TJ9wou51m8Z", - "asset_symbol": "PPY", - "amount": "9771617" - },{ - "owner": "BTSFKnmHzubN3n1JQKXcQYudP3uTbXD2eFgm", - "asset_symbol": "PPY", - "amount": "10261582" - },{ - "owner": "BTSP3tSSxp2W41enym3fRMGAFXrRchJRhEBZ", - "asset_symbol": "PPY", - "amount": "023120" - },{ - "owner": "BTSTgBDB8duzKDfJ7Acs4oSRbm2S8PdKtJv", - "asset_symbol": "PPY", - "amount": "3499737" - },{ - "owner": "BTSBFLZCmTxfQenPGY8HcNZtLnSb7p4QxDt2", - "asset_symbol": "PPY", - "amount": "1212293" - },{ - "owner": "BTS74wv2ASL2yDYbSeUZoRB3SFJDV1ZuMUzH", - "asset_symbol": "PPY", - "amount": "513143" - },{ - "owner": "BTSHA5EhjDh3U3rBrTCzVR1tDvfEB1XEPYM7", - "asset_symbol": "PPY", - "amount": "464441" - },{ - "owner": "BTS5SQjWUTmUqhYL6mxkdq1WtHXz2XqiKZk6", - "asset_symbol": "PPY", - "amount": "19922978" - },{ - "owner": "BTS2VdPNNwGyCEe6Hf8na3PRnVUo47H33PLR", - "asset_symbol": "PPY", - "amount": "1119741" - },{ - "owner": "BTSLMAE3X7hwbguxZWnXDuC1ZWAdPPhQXdCR", - "asset_symbol": "PPY", - "amount": "65283237" - },{ - "owner": "BTSBd6PNkBocnnLPhLPeCjTk2NMbkDyQZGB4", - "asset_symbol": "PPY", - "amount": "1922884" - },{ - "owner": "BTS14FjodsMggpr8sU5wi33Z1NFbMRKmAvMz", - "asset_symbol": "PPY", - "amount": "652415" - },{ - "owner": "BTSCN9RXyhqCLgnARWtg97ZoJzrwZoQXuWMf", - "asset_symbol": "PPY", - "amount": "35614438" - },{ - "owner": "BTSLZ7yVmj6abCf2GFwKiStKvNiLvBsANTv3", - "asset_symbol": "PPY", - "amount": "1159793" - },{ - "owner": "BTSDxUu48pyMGdjf4R4UnrTdEGYsmfRthMWr", - "asset_symbol": "PPY", - "amount": "6124828" - },{ - "owner": "BTS4r3MG4ga4NmZSK7uLw8A79gAMSZqEkJeU", - "asset_symbol": "PPY", - "amount": "347921" - },{ - "owner": "BTS5oPJiLdU3qwfbgb9y96qWrF1miYvbfPMA", - "asset_symbol": "PPY", - "amount": "17203050" - },{ - "owner": "BTSPL8ER5dyfyVswvzBTHf6jhp6vf9FzDKZs", - "asset_symbol": "PPY", - "amount": "18327747" - },{ - "owner": "BTSFgxH7PsrbmVokV6VRKHxgEvwkHvfiM2Hh", - "asset_symbol": "PPY", - "amount": "96742986" - },{ - "owner": "BTS9vVnEvdEpAHAJqrUbpR7F2AxidbbXGHpw", - "asset_symbol": "PPY", - "amount": "6901500" - },{ - "owner": "BTSGxDpLJ93wUbyDTquBwgXJQpP6Eaobu152", - "asset_symbol": "PPY", - "amount": "6319960" - },{ - "owner": "BTS3D5MSeZS46AMhfWBjZXvoaXVqqKL9PKiF", - "asset_symbol": "PPY", - "amount": "891943" - },{ - "owner": "BTSDXWw2CACQwqpmLHw8W8uwDPxMsmey1vpP", - "asset_symbol": "PPY", - "amount": "23702160" - },{ - "owner": "BTS9CtVD2xmKK2tWxqQWxFSEQSNZSQ2LsfCo", - "asset_symbol": "PPY", - "amount": "33749435" - },{ - "owner": "BTSPvunJFx6YNs4NCoKVntkW4ZPdUVKJYU6F", - "asset_symbol": "PPY", - "amount": "2049904" - },{ - "owner": "BTSEhnAr3gb8khZ8fouM3MsN4zWUooyiwf1a", - "asset_symbol": "PPY", - "amount": "4741168" - },{ - "owner": "BTS8eYdb8cKhPdgLCHP6hCFwCq7K9MyNBuSz", - "asset_symbol": "PPY", - "amount": "133964320" - },{ - "owner": "BTSPB19gHvVCN1ExJiUSGfaAXZxL3Sfz1qtX", - "asset_symbol": "PPY", - "amount": "5014877" - },{ - "owner": "BTSB2g89sjU3TUm3xmB6wGMU2KQYRQSFYNj8", - "asset_symbol": "PPY", - "amount": "1087402" - },{ - "owner": "BTSHaN4mCmK7fophNeiWg3pD9R5W8utqpV7K", - "asset_symbol": "PPY", - "amount": "12516376" - },{ - "owner": "BTSDoT6H7kGanRxBjwuf87rDWAHWELJmK9A3", - "asset_symbol": "PPY", - "amount": "5118234" - },{ - "owner": "BTSAEx1iQtU2QnbAqdGcDoGeaV1r6vetpdMT", - "asset_symbol": "PPY", - "amount": "3463379" - },{ - "owner": "BTSQCtxcFkYxZnb9W7Vfc2a6gpFAEHnBxL6y", - "asset_symbol": "PPY", - "amount": "1047826" - },{ - "owner": "BTSLoNBvwLtWAD7unsi8Jgj2ecLY1x3Ph3EC", - "asset_symbol": "PPY", - "amount": "102494914" - },{ - "owner": "BTSKyiaMk8yeY5JBp8kC7RU3AQkivUh9KahP", - "asset_symbol": "PPY", - "amount": "1979979" - },{ - "owner": "BTS73awx5Aso7MXJzZjYdHW5WsuaUjg2yrj6", - "asset_symbol": "PPY", - "amount": "19032299" - },{ - "owner": "BTSJRBKK8PW8VmEog1LsXTs374TYABqkJ9y9", - "asset_symbol": "PPY", - "amount": "536994950" - },{ - "owner": "BTSPWPprcNdKxp4CYCo8gqJ64nB745qT5dC1", - "asset_symbol": "PPY", - "amount": "228102" - },{ - "owner": "BTSH1ToGN4tvY9npEUbGHKKJjk84h8aJ4CpR", - "asset_symbol": "PPY", - "amount": "8962023" - },{ - "owner": "BTSNLLGoaGsneedD9jFJD5GZ3GBLiNB92X6u", - "asset_symbol": "PPY", - "amount": "6605613" - },{ - "owner": "BTS6NR41VA5dvECaoEkBfzoFsuQ2fAcPRGKG", - "asset_symbol": "PPY", - "amount": "2078926" - },{ - "owner": "BTSC22DL554EPYaR52XjHJdvxyHtFdu22CCy", - "asset_symbol": "PPY", - "amount": "16744644" - },{ - "owner": "BTSGEoXPeeef4xUF7eh1yMsSsLyhXJuLLHYU", - "asset_symbol": "PPY", - "amount": "030041" - },{ - "owner": "BTSQ5BGrbaHuQYxD4KZRK6odANAsVxDYeXnH", - "asset_symbol": "PPY", - "amount": "5406511" - },{ - "owner": "BTS8bR28zL4sKn3oANFdCPiWZXdLwVJ1tfqF", - "asset_symbol": "PPY", - "amount": "9270809" - },{ - "owner": "BTS4mTGkAHFnqCopwzY5AHqrZ3RsxjmLLeDD", - "asset_symbol": "PPY", - "amount": "224077" - },{ - "owner": "BTS9ao6LEJnLhdB3ke3CuG7muYzJ2JUehqGP", - "asset_symbol": "PPY", - "amount": "4921786" - },{ - "owner": "BTSL5b6RUxEuVQcaFyitnjQrMp6MbSm6nF8L", - "asset_symbol": "PPY", - "amount": "11762254" - },{ - "owner": "BTSLHAxUaH6htJtkXLTXnY4SrgM4695zNVBb", - "asset_symbol": "PPY", - "amount": "69092571" - },{ - "owner": "BTSFQP1AAMFeLfqjLUH19YsonSoHfj9evA4p", - "asset_symbol": "PPY", - "amount": "694908" - },{ - "owner": "BTSHL8ZWxT5HMmRcdKhZug6APSVkBUyhJEwU", - "asset_symbol": "PPY", - "amount": "116378280" - },{ - "owner": "BTSCaB6wwYzEbVbGG48dx98QBSULDX2pXiXK", - "asset_symbol": "PPY", - "amount": "368513" - },{ - "owner": "BTSJB5bwziPgzsqdtv4ioPS49sDqaJzfBWV9", - "asset_symbol": "PPY", - "amount": "1872701" - },{ - "owner": "BTSFyeBGCW2Wxrj1Z8KhDpLdEezB7SJyxQbX", - "asset_symbol": "PPY", - "amount": "308776" - },{ - "owner": "BTSNaRMuDceJsbAti6aZbJG8FBLUS8uetmcb", - "asset_symbol": "PPY", - "amount": "859172" - },{ - "owner": "BTSNnBvNWsPByQL91CHgpp6Xqcx4JJRwSKnL", - "asset_symbol": "PPY", - "amount": "3432362" - },{ - "owner": "BTSQHW6kCsUYnBFRmqDoKP6DX7Jsa6KApXvs", - "asset_symbol": "PPY", - "amount": "20181816" - },{ - "owner": "BTSD4U8KchwWBYA88pNpXHnoZdomzZSokHUN", - "asset_symbol": "PPY", - "amount": "4085577" - },{ - "owner": "BTSDqVKV9ZcCTXM7vghJzvZzM3iPF9DehJ5o", - "asset_symbol": "PPY", - "amount": "19081614" - },{ - "owner": "BTSLMRpzuq85EGQpmJfSCpCZSCBWee4vdj71", - "asset_symbol": "PPY", - "amount": "432900" - },{ - "owner": "BTSAUmMBj2qVGgtDKYVL4cDeu2r2eMb9yvDG", - "asset_symbol": "PPY", - "amount": "1986190" - },{ - "owner": "BTSB6aN3sAaHEpd9oG63gZNAbS2Nom6P2gu2", - "asset_symbol": "PPY", - "amount": "1013983" - },{ - "owner": "BTSFq4KTb5yuNCTwicEZMyb74Ln89mk2pnrf", - "asset_symbol": "PPY", - "amount": "1130673" - },{ - "owner": "BTS7xaoUvGrxVxa5L92u7WpHjcUB1TBR2AfM", - "asset_symbol": "PPY", - "amount": "2330731" - },{ - "owner": "BTS7rSv219mBUkPY72vqSrDk6ihw5b6Zxurm", - "asset_symbol": "PPY", - "amount": "247100981" - },{ - "owner": "BTSJLJK2jxM3t2vg4JPpcAJkDxTYNDu9g9gQ", - "asset_symbol": "PPY", - "amount": "1597677" - },{ - "owner": "BTSK36ubzF3UBrn6QMQMvtd7rYmPVTazTc6p", - "asset_symbol": "PPY", - "amount": "5674580" - },{ - "owner": "BTS4xgzG6u8grWBh3nUgZaXynsJnWWNzg8r9", - "asset_symbol": "PPY", - "amount": "6656639" - },{ - "owner": "BTS3nsTLwTvf2c5q51FN8r72XiPQFiptbQVB", - "asset_symbol": "PPY", - "amount": "6802538" - },{ - "owner": "BTSF8R5u48w8ADkXYVV9YDs6rzHvMPDeg3hx", - "asset_symbol": "PPY", - "amount": "6188064" - },{ - "owner": "BTS6kijGv7hSGvELhbFx8145oiBVZWSwB8Vo", - "asset_symbol": "PPY", - "amount": "6497803" - },{ - "owner": "BTSQKkKvp8zkDgDgsuEqeLGonGAHGnqfhbDS", - "asset_symbol": "PPY", - "amount": "85836500" - },{ - "owner": "BTS69r8svahTmzdTYQsxfsQhL5pyHL19fWRz", - "asset_symbol": "PPY", - "amount": "26246730" - },{ - "owner": "BTSCkbFVDwmSefqWYahuhvrdzcM6P1r6aSnh", - "asset_symbol": "PPY", - "amount": "1904445" - },{ - "owner": "BTSJtGz48Qvwtrdh37cwYRPcTjppz8JCdVZE", - "asset_symbol": "PPY", - "amount": "8354469" - },{ - "owner": "BTS7QxaBDqaAYyF1nbtYDqmFaJhEWVsttwku", - "asset_symbol": "PPY", - "amount": "102857143" - },{ - "owner": "BTS6xRkqvCdZFci2aS9CuRVfvh3AHKJSAxnY", - "asset_symbol": "PPY", - "amount": "1897031" - },{ - "owner": "BTS672spw2tZuADzV2u2FsS3aBn3vhNQwvU2", - "asset_symbol": "PPY", - "amount": "094475" - },{ - "owner": "BTSHhhbaLcZC4ja9ehF8x99o2ecUrfHwPhbi", - "asset_symbol": "PPY", - "amount": "161246500" - },{ - "owner": "BTS587qDEf76dYjQMnFapyktDsVAYZbgvEvg", - "asset_symbol": "PPY", - "amount": "29192714" - },{ - "owner": "BTSKg4AKehiEXHzigM7S4A7MUptwuo1DvkZt", - "asset_symbol": "PPY", - "amount": "105801000" - },{ - "owner": "BTSNB1N49BqRjVUTcXmW3eULM82krNRPaEx7", - "asset_symbol": "PPY", - "amount": "7058491" - },{ - "owner": "BTS3D5LnEFqiy8sMaYLYyVcG7E2qYRHpBmqZ", - "asset_symbol": "PPY", - "amount": "4890636" - },{ - "owner": "BTSHeHLH3QCMgkNHbXHk6a7k6GpEuCry31Du", - "asset_symbol": "PPY", - "amount": "3388144" - },{ - "owner": "BTSJDJfjpopk8YGQjLaGeuTYQyRHLTEqUvt8", - "asset_symbol": "PPY", - "amount": "16489452" - },{ - "owner": "BTS2Sp4tm4JgdCXuWRdjZXhebze63CgGjwSJ", - "asset_symbol": "PPY", - "amount": "611686" - },{ - "owner": "BTS4zmKf1icN4mGTxYVHhBUZK7751ch4Z7m4", - "asset_symbol": "PPY", - "amount": "155812541" - },{ - "owner": "BTSBzeWG3ho822VTYpjcgUJQconSBF9PYPug", - "asset_symbol": "PPY", - "amount": "23663800" - },{ - "owner": "BTSCJYCfgeTfZRkA7BVSiiRKgP2XWiKcxt6p", - "asset_symbol": "PPY", - "amount": "037745" - },{ - "owner": "BTS6tgehEoRTwU6wLW8So4rcY59GvXe9vG1A", - "asset_symbol": "PPY", - "amount": "019907" - },{ - "owner": "BTS2GU33JAqHFkrTbcGceK54CRTtcytn3wC8", - "asset_symbol": "PPY", - "amount": "6592437" - },{ - "owner": "BTS6AnKn28CfZRAQR39ezJZR1CpxDydPcopb", - "asset_symbol": "PPY", - "amount": "910426" - },{ - "owner": "BTS6Z8u35rtQQQEgk2QMRsXm5vYr7skm777T", - "asset_symbol": "PPY", - "amount": "4740432" - },{ - "owner": "BTS8ttdjjoCtv8n2r8jZ6UYYXVKhFMh31svr", - "asset_symbol": "PPY", - "amount": "5718304" - },{ - "owner": "BTSMT1DCdZLAjUikM9mMD6B1CRKJeCYYPm4G", - "asset_symbol": "PPY", - "amount": "026479" - },{ - "owner": "BTSCoKBUNGecsN4ZxKdZuyLRwDezCJBAZ2K2", - "asset_symbol": "PPY", - "amount": "1630888" - },{ - "owner": "BTSGsyuc68A34f6j1Pi4fKDeAfejnmudoSQu", - "asset_symbol": "PPY", - "amount": "37193915" - },{ - "owner": "BTSBDETuuU3wMipyx6dHQGbauoYxdip6TthX", - "asset_symbol": "PPY", - "amount": "1094242" - },{ - "owner": "BTS5zzaVFXe6AHDEGQCqgn8G9dPRdv2o13PX", - "asset_symbol": "PPY", - "amount": "1747754" - },{ - "owner": "BTSB2muKDktUuFUMxfbTVcELpWirkvLfuRke", - "asset_symbol": "PPY", - "amount": "6859395" - },{ - "owner": "BTSBArpjj2vKFqghgE6biJt8Mus68M5BJwPR", - "asset_symbol": "PPY", - "amount": "2100242" - },{ - "owner": "BTSJEk6D3PFS4w44AGKutxt768ZfUwPLKe5U", - "asset_symbol": "PPY", - "amount": "5529747" - },{ - "owner": "BTS5FkJWp8QWZ1LCmWLb9DeQ3aCThcjvj2XN", - "asset_symbol": "PPY", - "amount": "4726488" - },{ - "owner": "BTSC6KrwJ1djDbMqjZ89Ee95HFQmgFxETLBE", - "asset_symbol": "PPY", - "amount": "20718172" - },{ - "owner": "BTSJ3YrBha84gjUHanDuhoDD38SB68rBC7Gd", - "asset_symbol": "PPY", - "amount": "88813924" - },{ - "owner": "BTSH1Fzia3Qs9B35RDVik61AGYnvGPiPSeLw", - "asset_symbol": "PPY", - "amount": "3275299" - },{ - "owner": "BTS7FubkazaoLKgYpss8jfQ1pEGUYaGxuz9V", - "asset_symbol": "PPY", - "amount": "8591542" - },{ - "owner": "BTS6rnyg5FvZ7hVPPy5wBLhozHMHntXGPvxc", - "asset_symbol": "PPY", - "amount": "14038008" - },{ - "owner": "BTSA4AmK8jbc9VW48R4KudDpygu1mtDj1E2t", - "asset_symbol": "PPY", - "amount": "4776909" - },{ - "owner": "BTSLynNjiqj6DuBAueA6CWijgtiNWktZYo4m", - "asset_symbol": "PPY", - "amount": "38562914" - },{ - "owner": "BTSBrBZctAL1LomjuSFXbh3hp5ofT5xu68ip", - "asset_symbol": "PPY", - "amount": "526909" - },{ - "owner": "BTSNBeDxbgk6bSRBi7u6YVZjWHTN62VzF4VF", - "asset_symbol": "PPY", - "amount": "3561146" - },{ - "owner": "BTS2KzufJuLVUryvEJSVnM3WojvySRb2WQu8", - "asset_symbol": "PPY", - "amount": "510269" - },{ - "owner": "BTSJ3GCx4MEdjzaxVCHwWWSFyuD5fR5YwiXy", - "asset_symbol": "PPY", - "amount": "1744841" - },{ - "owner": "BTSPKraTYeRHyrM5BN9o6ihoFabYv6tkNPjw", - "asset_symbol": "PPY", - "amount": "16104134" - },{ - "owner": "BTS4ZfCnX9qnTHuiUzqby7Xm63p5hcC8fCh3", - "asset_symbol": "PPY", - "amount": "22800550" - },{ - "owner": "BTSAPraEzhYzg5DT9eLsbprRmCEiGQ5u5W6P", - "asset_symbol": "PPY", - "amount": "782183" - },{ - "owner": "BTSHsfEcehpMbXUFhazKcGZDit2L2McsmSMJ", - "asset_symbol": "PPY", - "amount": "957381" - },{ - "owner": "BTSNBtzVfhTCDexRfAefgo8ze8ye8NJGUnYz", - "asset_symbol": "PPY", - "amount": "753732" - },{ - "owner": "BTS4uizZo4FeWm2vbb3a3a58Tr6YvQoXdARD", - "asset_symbol": "PPY", - "amount": "1341700" - },{ - "owner": "BTSGpvhxsUfj68nXygp9XXXtce8T261vyS99", - "asset_symbol": "PPY", - "amount": "323105" - },{ - "owner": "BTSEBehTsmBpb4vckMgV3jSJvgkn5M6dvPV9", - "asset_symbol": "PPY", - "amount": "1004956" - },{ - "owner": "BTSKiThkzW4zDt9UccXSwYWCMVjoFFLwBcrC", - "asset_symbol": "PPY", - "amount": "2191535" - },{ - "owner": "BTS9SWSn5T4oJWLv4N5hjgMLYN3A4WJC1w2U", - "asset_symbol": "PPY", - "amount": "016831" - },{ - "owner": "BTSKyx7Yxvvcr7FvH4K7tvs3M6hTwW22Cu2x", - "asset_symbol": "PPY", - "amount": "1644693" - },{ - "owner": "BTS3zaPzN3GuWU3ENx4bxK1c4CrYYjWV7Xt3", - "asset_symbol": "PPY", - "amount": "53630884" - },{ - "owner": "BTSMadVnS5xYcsN5LaGEZoLe46B8QYBoomMz", - "asset_symbol": "PPY", - "amount": "032269" - },{ - "owner": "BTSQ3qsoNz7tHip9KZ4aQrv2yLheqn7LN3uE", - "asset_symbol": "PPY", - "amount": "846657" - },{ - "owner": "BTS5ikuu5nGqbKqA7mrJ33r5zj6SVz8ikraC", - "asset_symbol": "PPY", - "amount": "10270573" - },{ - "owner": "BTSDDA6Y5JcXV4eV9ybBUo7mPBKL9ZM4RLAa", - "asset_symbol": "PPY", - "amount": "21634204" - },{ - "owner": "BTS7KAtgjTfnBaVgDobEvRKM3JYYeJtSf8Nh", - "asset_symbol": "PPY", - "amount": "912228" - },{ - "owner": "BTSPYLYjCacioMxc1Y58y7YCcrhzGUCMq1c7", - "asset_symbol": "PPY", - "amount": "174526" - },{ - "owner": "BTSMVPwQz39FXMhCfGfwBESxnEX5aNgkCjW5", - "asset_symbol": "PPY", - "amount": "2179391" - },{ - "owner": "BTS7nmo1psrujHnTco7JKD1BJrM8GaxkRvk2", - "asset_symbol": "PPY", - "amount": "24448000" - },{ - "owner": "BTS2i4CrwYChbUehxTukBcx2NasUAciy3Xkh", - "asset_symbol": "PPY", - "amount": "6773007" - },{ - "owner": "BTSMajvVp3v9P5joA9DWNk2r3qV9yoTYiCXo", - "asset_symbol": "PPY", - "amount": "313538779" - },{ - "owner": "BTSAd4RrnqjZtvud7NL3kqguojtbQuKXhvWk", - "asset_symbol": "PPY", - "amount": "21755800" - },{ - "owner": "BTSNQTJcXiX1ojW2PWCBkR2mAuH346Sebin1", - "asset_symbol": "PPY", - "amount": "3174498" - },{ - "owner": "BTSH7vQDt5TpBUZ2HYHRkPgT884idRG55zmc", - "asset_symbol": "PPY", - "amount": "4151570" - },{ - "owner": "BTSMLkLi3QQLpEz5iYUht43vMYX1ZX9TYPE", - "asset_symbol": "PPY", - "amount": "25471582" - },{ - "owner": "BTSCHSH9oe83UiCuE7UrYs5cKa4h85ubfPpp", - "asset_symbol": "PPY", - "amount": "166042" - },{ - "owner": "BTSK1p9LBSPSx9oSoYBm2XaWzwCLMMTfFaF9", - "asset_symbol": "PPY", - "amount": "141723" - },{ - "owner": "BTS6DXpYHgy83pUafXBQbCm7siNGw9VW7w9D", - "asset_symbol": "PPY", - "amount": "5041101" - },{ - "owner": "BTSBQgZkCp1okohXDCr9ir7djV1pZYYXdSbz", - "asset_symbol": "PPY", - "amount": "761434" - },{ - "owner": "BTSFvnWGYhtaqx31YEtQgfWBCxTpEp49oZpp", - "asset_symbol": "PPY", - "amount": "919617" - },{ - "owner": "BTSGENVN6Ub6hSw5xmfwozQbAumZ5wPcSD8u", - "asset_symbol": "PPY", - "amount": "16212394" - },{ - "owner": "BTS7azNW6CR1MuVDnDZjNcDkGQoSgk8RFXzo", - "asset_symbol": "PPY", - "amount": "24368274" - },{ - "owner": "BTS5KYKwqiFHHtvQQyh2ZZQBqeW7iT6FxfaB", - "asset_symbol": "PPY", - "amount": "1080241" - },{ - "owner": "BTSPXiPyPZQ4dAzxAM3ktPCUhn4kJZmLUpAc", - "asset_symbol": "PPY", - "amount": "16811695" - },{ - "owner": "BTStJtxeZ8q4xCPJgmiAQqoX9f6qMkZv6Qk", - "asset_symbol": "PPY", - "amount": "4124181" - },{ - "owner": "BTS69xyCzfjGTvnUoYrd1VzvDZhhAKJtBNGz", - "asset_symbol": "PPY", - "amount": "1977325" - },{ - "owner": "BTSASRhy4qMrNXEQh7apNy3ZWXZj2GTpc4QK", - "asset_symbol": "PPY", - "amount": "167504" - },{ - "owner": "BTSG54D6JoZnu6Wam892D1kBTyd4CUMepkLp", - "asset_symbol": "PPY", - "amount": "3827345" - },{ - "owner": "BTSM2hK46Pk9UANo9Cx2GvqXtncgmvHTRX6n", - "asset_symbol": "PPY", - "amount": "6837426" - },{ - "owner": "BTSDiUKRfVT29MT3rDAo7aBzYXWoRQsQwN8m", - "asset_symbol": "PPY", - "amount": "27216327" - },{ - "owner": "BTS8guVfUWT33qGvokn299aVCNK7NEXiy8in", - "asset_symbol": "PPY", - "amount": "23259021" - },{ - "owner": "BTS6PMF2rjBoCe4yTM4BaVPpwMDjV7ZCdPW6", - "asset_symbol": "PPY", - "amount": "16600099" - },{ - "owner": "BTSEH7bFvaP3AqBdyiZ6nLwVzmqsvC9CizbY", - "asset_symbol": "PPY", - "amount": "20628568" - },{ - "owner": "BTS3KJycevpfKiQ47HtADAWWESwU5Pbk3XPL", - "asset_symbol": "PPY", - "amount": "4032244" - },{ - "owner": "BTS6Jh7p22viruuaUEFq3pNiLWWBJndjoseU", - "asset_symbol": "PPY", - "amount": "3325140" - },{ - "owner": "BTSNCceMWXH4rZmSEBq6wd4YGdBfrgExFmM7", - "asset_symbol": "PPY", - "amount": "505127" - },{ - "owner": "BTS7BYqPBzNibmemGVjXdUiKPTcp434kazmb", - "asset_symbol": "PPY", - "amount": "6122975" - },{ - "owner": "BTS5ggDw23UMNCEyW1z4bJPEiMxxjfXENFPs", - "asset_symbol": "PPY", - "amount": "19507865" - },{ - "owner": "BTSNgcZKMZhBdW6TEMxs1VpDcUy9dxHzsWqd", - "asset_symbol": "PPY", - "amount": "24382876" - },{ - "owner": "BTSP8pJuq3Dqw5svv36PZn6H1pUraMkxgwrx", - "asset_symbol": "PPY", - "amount": "16444157" - },{ - "owner": "BTSNw6qnbesW2j4zhLjRjs1uJTj2Q9CzzHBK", - "asset_symbol": "PPY", - "amount": "16811695" - },{ - "owner": "BTSKpFE6gHVnvSX89Vu5cGmqN6LKJT9o9Khz", - "asset_symbol": "PPY", - "amount": "2054550" - },{ - "owner": "BTSJ3kUGJGzosNA8HyBZwBPudggTqaCmoeUS", - "asset_symbol": "PPY", - "amount": "11696513" - },{ - "owner": "BTSNX7wsMgE73wERCgEaN9tot7idUnVKnwLQ", - "asset_symbol": "PPY", - "amount": "4203477" - },{ - "owner": "BTSEnvbcf7G2yuDyHxTxprkzmJ46is28Nz9s", - "asset_symbol": "PPY", - "amount": "251308" - },{ - "owner": "BTSGYy3DFoo5ky8Cc9PnKyYZqLGGH3ck1Ljn", - "asset_symbol": "PPY", - "amount": "515420" - },{ - "owner": "BTSAQgcBB2nJce7mUbogdg47vXztPrNhDCot", - "asset_symbol": "PPY", - "amount": "1182485" - },{ - "owner": "BTSJT5eBaRN2ARwfdVbMagZKFWdWvf3QNXrV", - "asset_symbol": "PPY", - "amount": "3281394" - },{ - "owner": "BTSN8XxVEETbTg8zFR8BxVeZ4xQ9EENrzFRG", - "asset_symbol": "PPY", - "amount": "34898683" - },{ - "owner": "BTSBdemimct6FWyPHwAMKwkxh1GcMgEmsGNJ", - "asset_symbol": "PPY", - "amount": "132148819" - },{ - "owner": "BTSM4L2yzpjKKCaeSrG4KuqR9ZZp9BmaRRYp", - "asset_symbol": "PPY", - "amount": "22901777" - },{ - "owner": "BTSDLdmWXBH41EA5Za1aShqrLoY1hdAqLgEu", - "asset_symbol": "PPY", - "amount": "1009773" - },{ - "owner": "BTS7ufbfHfHnHyJNfAUJww7jaEqKJwH1Nmw2", - "asset_symbol": "PPY", - "amount": "39526566" - },{ - "owner": "BTSNiiUNHV9JPJkYUY5FshTP3kmdkUTSyEgK", - "asset_symbol": "PPY", - "amount": "34514197" - },{ - "owner": "BTS3cV4U62iPLJD8GiPcdy8qxFfVE89HKn5C", - "asset_symbol": "PPY", - "amount": "518358" - },{ - "owner": "BTS6NhLWaFMLoWT41zVssSskzHumQ6mv7swA", - "asset_symbol": "PPY", - "amount": "094776" - },{ - "owner": "BTSPXSFiyEgCqW8H3vhutiNXU7cJscDcvCir", - "asset_symbol": "PPY", - "amount": "17025116" - },{ - "owner": "BTS5nPbPSZcVh7A9xyybyoXKhSkpwYhAERL5", - "asset_symbol": "PPY", - "amount": "3388828" - },{ - "owner": "BTSHbQMkpXDWq8ey4oeuEjJnupsrBJLMzXRY", - "asset_symbol": "PPY", - "amount": "1666082" - },{ - "owner": "BTSNeBTgTWj7A9S9Ebj1ayaQvjCZJBaz36f6", - "asset_symbol": "PPY", - "amount": "4064870" - },{ - "owner": "BTSGuXfZQqkpcyBMUbKb5ntVdEftVyD1S2yL", - "asset_symbol": "PPY", - "amount": "64649054" - },{ - "owner": "BTSLvJ284mcv5EkjCEM4dApszQHHCYvC3DdM", - "asset_symbol": "PPY", - "amount": "3998508" - },{ - "owner": "BTS2TQe7WTY2CJs4H9FoyJ2ZvdXe196LUx2L", - "asset_symbol": "PPY", - "amount": "152101" - },{ - "owner": "BTS3c3NdG1uctjjhxkUfcMxi7PV8RWCSSMLQ", - "asset_symbol": "PPY", - "amount": "18300590" - },{ - "owner": "BTSPbMTQ9UU8tbptNgSjaAahhini84CPyjS", - "asset_symbol": "PPY", - "amount": "5165650" - },{ - "owner": "BTS8eSFc5nmZuhHvg3HL6sNCSJ1srqKA7MSj", - "asset_symbol": "PPY", - "amount": "2859013" - },{ - "owner": "BTS8vNVkFD8CPDbEtbsFPPJME7ECrtpBY6DW", - "asset_symbol": "PPY", - "amount": "482709" - },{ - "owner": "BTSFF1n1zMZu7dTyZZFXzWbzeAktRG5HEbe6", - "asset_symbol": "PPY", - "amount": "123457597" - },{ - "owner": "BTSGjS5STFYR1RJuBSLLGV8oG5uMmgksHT4R", - "asset_symbol": "PPY", - "amount": "448897" - },{ - "owner": "BTSL3CNjTs9jdx3n17xHA6aEEB1SGbUyQHQw", - "asset_symbol": "PPY", - "amount": "3410343" - },{ - "owner": "BTS6SDHgFAGzDeaFdabJLAs6YvWJokNMnDUn", - "asset_symbol": "PPY", - "amount": "34775810" - },{ - "owner": "BTSNxX7DuWFPwkhc7aQgCutFgR4becLYEemS", - "asset_symbol": "PPY", - "amount": "1433988" - },{ - "owner": "BTSBYvyvBcEaEYdURfVJPjbku6zSPX59cpds", - "asset_symbol": "PPY", - "amount": "87752053" - },{ - "owner": "BTSFLwpXAJezEm4Rie5MDapqEFSWXbejATap", - "asset_symbol": "PPY", - "amount": "1743855" - },{ - "owner": "BTSDD8pDmDEN17YTrTPidXwctpXgPo5CD7N1", - "asset_symbol": "PPY", - "amount": "14785649" - },{ - "owner": "BTSCnGGDtJeeMUQ1tFQvbwZpP4C42xDnsiFf", - "asset_symbol": "PPY", - "amount": "2058610" - },{ - "owner": "BTS5f1QJZ2rfhSzR7jFmJyJxUveAwM2PtgRT", - "asset_symbol": "PPY", - "amount": "4630657" - },{ - "owner": "BTSKnHRuqs9vgy4gJwaFGP47KM8K4btKZeW4", - "asset_symbol": "PPY", - "amount": "164803143" - },{ - "owner": "BTS4gfeiDxFdV33FfkG8my3nBq91Ap8XDD2W", - "asset_symbol": "PPY", - "amount": "9585150" - },{ - "owner": "BTSK6RpLL2wg7NTjJedWnMWTA57pbMnGHNAm", - "asset_symbol": "PPY", - "amount": "11606342" - },{ - "owner": "BTS3w8bWsBvockkU8nbmLDQMdnyEM7SpzvRs", - "asset_symbol": "PPY", - "amount": "3778100" - },{ - "owner": "BTS82R5CdnQL3zqNkwPKZuuCip919RiWuL5x", - "asset_symbol": "PPY", - "amount": "34072794" - },{ - "owner": "BTSJZnJoWPH6VzUHLY3fPFwjzZp1eKKkeufs", - "asset_symbol": "PPY", - "amount": "596456" - },{ - "owner": "BTS4eUjReJExVe8HniYVQkrqhktAnvjPp31i", - "asset_symbol": "PPY", - "amount": "20912965" - },{ - "owner": "BTSKX8Mqt7HLva7mpFVXaZ2ZZ1n4fXqWAm1c", - "asset_symbol": "PPY", - "amount": "160556" - },{ - "owner": "BTS9bxEVCyyTj3mrcqzPXtnYV6PdB5LdwmBa", - "asset_symbol": "PPY", - "amount": "4050581" - },{ - "owner": "BTSHj84qCztWd2ubQ4SRkyTVcZkebMZC6wBc", - "asset_symbol": "PPY", - "amount": "41560880" - },{ - "owner": "BTS73FiA27PokGpUmfj7f47KvV5PEyyRNEcg", - "asset_symbol": "PPY", - "amount": "10789703" - },{ - "owner": "BTS9oFAAh913wNxEjGgVpYLe2WcXsB4DKLpM", - "asset_symbol": "PPY", - "amount": "182159011" - },{ - "owner": "BTSPURiM5cofjhnroh15LwyH7vVJ8BNGces8", - "asset_symbol": "PPY", - "amount": "23684740" - },{ - "owner": "BTSM9E3AYMAjQDZmCfYGTxfFZXvnvhqnyUDU", - "asset_symbol": "PPY", - "amount": "1953344" - },{ - "owner": "BTSJo2oycNmv1cQKZsUidWTKiQ3dd1fXZcGh", - "asset_symbol": "PPY", - "amount": "3783218" - },{ - "owner": "BTSChMnfzhn321baPcAHxSn5RVVU8Yj9FjNS", - "asset_symbol": "PPY", - "amount": "26841219" - },{ - "owner": "BTS6mkYBa7uMZWMxHFb8umiBtPJLwR4SrsM1", - "asset_symbol": "PPY", - "amount": "2105981" - },{ - "owner": "BTSEt37a7RZs4rrLiQhDjhdCcSLAcbbakTPC", - "asset_symbol": "PPY", - "amount": "404401" - },{ - "owner": "BTS4RXQbZuKcCE9SF5znf2rELuVcDozK4BdH", - "asset_symbol": "PPY", - "amount": "6396251" - },{ - "owner": "BTSBeH9bqyTEbyGMtFQsGFnD8ttDj8UdxXvW", - "asset_symbol": "PPY", - "amount": "1122088" - },{ - "owner": "BTSwxgvw13qvkWRuYuuvfcpLvpHaq1Wsfic", - "asset_symbol": "PPY", - "amount": "5921690" - },{ - "owner": "BTSPwrBn1YuVrTjSB9UoUbP9zchBNBQPUmyy", - "asset_symbol": "PPY", - "amount": "535649" - },{ - "owner": "BTS7ouD13MDm5LdnYiiNyWNddUumSCA6vZAi", - "asset_symbol": "PPY", - "amount": "41580025" - },{ - "owner": "BTSQqiqHCmyaZf1tqvcJot5KsGeiN4xBFWj", - "asset_symbol": "PPY", - "amount": "6360000" - },{ - "owner": "BTSAEuAwUYSoortKuRCC7KYsgqopcxY43JcA", - "asset_symbol": "PPY", - "amount": "581634" - },{ - "owner": "BTS78CStZ5jot9AjJ33knnm5so9yuzdr9Jni", - "asset_symbol": "PPY", - "amount": "97673219" - },{ - "owner": "BTSAfkWckFnuUTpbPJzsPbhVK8neYDBvmYb1", - "asset_symbol": "PPY", - "amount": "236700" - },{ - "owner": "BTSNcXSNbixC2WZjQSZWnax6RTyUky7PWfQs", - "asset_symbol": "PPY", - "amount": "100987926" - },{ - "owner": "BTS2zGhboDHMnVSn3iQnW7xkbqEJNaxMJzi7", - "asset_symbol": "PPY", - "amount": "15239913" - },{ - "owner": "BTSKQpNrtdgXiJrd2dbFEJmdWYQ1NawXVC49", - "asset_symbol": "PPY", - "amount": "001780" - },{ - "owner": "BTSNs7Mq8nAX45igEQUApGpaAVqVw8sDVyrp", - "asset_symbol": "PPY", - "amount": "71251910" - },{ - "owner": "BTSFBqvQbamC4KubjhSvkYnRbgj97SV6xxtx", - "asset_symbol": "PPY", - "amount": "33083746" - },{ - "owner": "BTSGFQ88Nwjr4YSD213hfkKbAkcWZpYQ7f1K", - "asset_symbol": "PPY", - "amount": "8881333" - },{ - "owner": "BTSPBKTpMSpY3aNUSKNkVdrhYAetP6KWbcqB", - "asset_symbol": "PPY", - "amount": "574697" - },{ - "owner": "BTS7yFNwxHNwi9XPeK1Sy7hi7LeJzPGxEdAt", - "asset_symbol": "PPY", - "amount": "1172842" - },{ - "owner": "BTSLcP3b2eXQ2bQVCiKPmqscuH5WaXKkVVdy", - "asset_symbol": "PPY", - "amount": "2101985" - },{ - "owner": "BTSGPLBmJQruHPHskMd3Jbj5k4GKfksBrvFC", - "asset_symbol": "PPY", - "amount": "3055584" - },{ - "owner": "BTS9JFbYhJBpNpe2MksjLJxfSXY8XvoAMUQv", - "asset_symbol": "PPY", - "amount": "58179459" - },{ - "owner": "BTSG3nksyTr2TnuuLw9ae7f2VnpNPNiRfXta", - "asset_symbol": "PPY", - "amount": "1668593" - },{ - "owner": "BTS6KobLV94qR3GS4YgmabCiu1f2bDLazH5h", - "asset_symbol": "PPY", - "amount": "7037601" - },{ - "owner": "BTSHc6vpnKJ9K3Hb1YPJYRC4RzeHX3f6SqzR", - "asset_symbol": "PPY", - "amount": "35272637" - },{ - "owner": "BTSGXXmUpJ7i8opnKKr2gmWW3YFhHMpRB2rM", - "asset_symbol": "PPY", - "amount": "779099" - },{ - "owner": "BTS3UvD1CCz6mhm4H7F22VBGb1U2MTEocKU6", - "asset_symbol": "PPY", - "amount": "546487" - },{ - "owner": "BTS4RdNdsBR19wR4EEFgXFvZhSyzXegxpnj2", - "asset_symbol": "PPY", - "amount": "1713385" - },{ - "owner": "BTS3a6Wxxow429R82nMrRqrFbPiruZqTA8w2", - "asset_symbol": "PPY", - "amount": "172189571" - },{ - "owner": "BTSAtFqKW7nxUbonPSjG9GcKNvyw42TtovY1", - "asset_symbol": "PPY", - "amount": "988499" - },{ - "owner": "BTS9VmfWr4YBxndefh3KTvHv1yYHPCr37WFw", - "asset_symbol": "PPY", - "amount": "10810958" - },{ - "owner": "BTSAxbSSvhiDUvJkrxfuN7RRu8EYTgzNzxbT", - "asset_symbol": "PPY", - "amount": "103638857" - },{ - "owner": "BTS2U6LFsVN8Nhu5vRozWmgbvVNw4CBVtk9t", - "asset_symbol": "PPY", - "amount": "320000" - },{ - "owner": "BTSK8Ctsct51CE4ib3CJojt3Xb4fB49hx5QU", - "asset_symbol": "PPY", - "amount": "2226328" - },{ - "owner": "BTS674gCJe3jnBmh2kFRsPYgL9uqL3YfpZN3", - "asset_symbol": "PPY", - "amount": "496002" - },{ - "owner": "BTSVe7i4CuaYkJFuaCVerDubsCCcWQQMMDA", - "asset_symbol": "PPY", - "amount": "6437654" - },{ - "owner": "BTS7RpNtYXYxeULqeHfjA59fHsR29WEFWhpj", - "asset_symbol": "PPY", - "amount": "1413990" - },{ - "owner": "BTSDkwHYz6U3oLwnXuZqghXwbr5CJBqZX1Xi", - "asset_symbol": "PPY", - "amount": "3964725" - },{ - "owner": "BTSL9XpsYGMfBnKDQHHCMkH6PNaQUQA16JCV", - "asset_symbol": "PPY", - "amount": "124707845" - },{ - "owner": "BTS3DSqtHYRCCLQC2azn6CpmXkQ6oeJYp44x", - "asset_symbol": "PPY", - "amount": "1949407" - },{ - "owner": "BTSLJ2rCbtnLnpsK22nnB9LFNkdoS19dnMPv", - "asset_symbol": "PPY", - "amount": "24277852" - },{ - "owner": "BTS9zJMG5c7aULhfoFL8dXaeuKSX68vmR4V4", - "asset_symbol": "PPY", - "amount": "13458158" - },{ - "owner": "BTSKXWrZjyw19ZzDGSjqf8vMPxBifad35a1R", - "asset_symbol": "PPY", - "amount": "9367920" - },{ - "owner": "BTSHFYovDkZ9Zz2vEkef8YXdrNuxKk9DLcE4", - "asset_symbol": "PPY", - "amount": "2156682" - },{ - "owner": "BTS4oRxpVKurNwRLSL6dVDXbQWmrZXF985Uo", - "asset_symbol": "PPY", - "amount": "10720195" - },{ - "owner": "BTSNGkrsPk9f7XdkgeirHVmMJ1pYbuTLToK9", - "asset_symbol": "PPY", - "amount": "015957" - },{ - "owner": "BTSNkHpgK2aE6Tf3dCvaMC79dWo3NNYxH4v5", - "asset_symbol": "PPY", - "amount": "39338771" - },{ - "owner": "BTSP74FrSi7kSsqGNyCdsKxN79Atz2zZ435z", - "asset_symbol": "PPY", - "amount": "19695557" - },{ - "owner": "BTS6YQxzCKvmDq4TXmZhDCpZ9K9EoFuT8XpP", - "asset_symbol": "PPY", - "amount": "17190000" - },{ - "owner": "BTSMPtimnNRQaLKVkQQmsm8t4rRX17K2ctjP", - "asset_symbol": "PPY", - "amount": "052909" - },{ - "owner": "BTS544idHuP462i4wGqUFJcUeZCA3yaMSh2j", - "asset_symbol": "PPY", - "amount": "9826113" - },{ - "owner": "BTSGLu8wpSJktouYEtjMizWSGwgv1fMar7Qn", - "asset_symbol": "PPY", - "amount": "159722056" - },{ - "owner": "BTSP73gYCNWRdj2Lm6pM82LjwavNxUbsi5K8", - "asset_symbol": "PPY", - "amount": "48114389" - },{ - "owner": "BTS4p6m7qioUeS6gdDTQky5pXnoym2PDTVm1", - "asset_symbol": "PPY", - "amount": "3554935" - },{ - "owner": "BTSKD1jqiszT9ExH2HybXDtoe7nZRzAoV7Gg", - "asset_symbol": "PPY", - "amount": "979241" - },{ - "owner": "BTS4euT6konp3ysPTgAoqxX3xgdkXKyFbFYA", - "asset_symbol": "PPY", - "amount": "2400660" - },{ - "owner": "BTS5osm9uGtMTwVPte28k5dH1LnLtiWKQs2r", - "asset_symbol": "PPY", - "amount": "468396" - },{ - "owner": "BTSLiiUeEBZ2ZNfHZPnmmcXCQocHrwc196N4", - "asset_symbol": "PPY", - "amount": "3916577" - },{ - "owner": "BTSEcAUYkahh3MFFkaq54KNMZtBrcCys526w", - "asset_symbol": "PPY", - "amount": "144077760" - },{ - "owner": "BTS6YKsXvQihLtF675wvnbL2iaNW17ZvroGF", - "asset_symbol": "PPY", - "amount": "3005985" - },{ - "owner": "BTSLkD6Y5WNbS3515cuCy3sZUWgzE3QdQaGv", - "asset_symbol": "PPY", - "amount": "1261987" - },{ - "owner": "BTSGTbCvThqS8EQ4UPrDFyLzbBkLJ8b97kFV", - "asset_symbol": "PPY", - "amount": "9717169" - },{ - "owner": "BTSEiy9C3CsS6QoVuvm2fkXTSswvnTkJyvpP", - "asset_symbol": "PPY", - "amount": "347374" - },{ - "owner": "BTS9F4HbajDY1NH8whr1YGBHbBk2Z3u6Yse", - "asset_symbol": "PPY", - "amount": "15236963" - },{ - "owner": "BTSDCcQTx4AtaGLhZC2KGdPwb3Sy3nhVwQ7k", - "asset_symbol": "PPY", - "amount": "3133440" - },{ - "owner": "BTS4CXkXBrdJzKU1vGt5xeeLV3jbyMmvMr2W", - "asset_symbol": "PPY", - "amount": "49994593" - },{ - "owner": "BTS3WdouWrGPst9i6wEMVzQL6MBePAKkP7NE", - "asset_symbol": "PPY", - "amount": "3814338" - },{ - "owner": "BTSHyHbAeVdexNbHpnK4s7w3wQfwQxM2WK69", - "asset_symbol": "PPY", - "amount": "8586464" - },{ - "owner": "BTSBfw8i5LAw73RAEiKnt3gg616TGVsFLKWo", - "asset_symbol": "PPY", - "amount": "11445869" - },{ - "owner": "BTSPMenP5JrwXq24US3fTJJDDa9Yiq3yzM5q", - "asset_symbol": "PPY", - "amount": "9649159" - },{ - "owner": "BTS4ugpLdbVmd1h36JisofFtJC4H2vBfVGrh", - "asset_symbol": "PPY", - "amount": "3432362" - },{ - "owner": "BTSNhJaYptjxXDznCoXFKDYAvTz5byMdtMRz", - "asset_symbol": "PPY", - "amount": "110684760" - },{ - "owner": "BTSAK9p9tLKoMzxVebFoJ9REBWo3auQY7mbJ", - "asset_symbol": "PPY", - "amount": "19410980" - },{ - "owner": "BTS4ujcNuN9ttZzuTbP1DPS2jArfGpFsWf1j", - "asset_symbol": "PPY", - "amount": "4338605" - },{ - "owner": "BTS4vWRg7Wz9CnJ3LvquEac64fmfSNFwo35n", - "asset_symbol": "PPY", - "amount": "4706880" - },{ - "owner": "BTS4CVCPUqS9iLaaxP8bdffbXJJLcKenc8KP", - "asset_symbol": "PPY", - "amount": "15784806" - },{ - "owner": "BTS588rCGcMKgEmTjNRvaTrG1JH9JhjNZA4X", - "asset_symbol": "PPY", - "amount": "32119470" - },{ - "owner": "BTSAL4SoNhm2P1nuoixJMcYVAaYQavRE38Qy", - "asset_symbol": "PPY", - "amount": "4976465" - },{ - "owner": "BTSFkz1sBxYgwZDbnAWJ3xfbSdoKcL4q7g6T", - "asset_symbol": "PPY", - "amount": "9872334" - },{ - "owner": "BTS7zzFnubgPd1DsiAVDhBAYCUxiqUuE3iKh", - "asset_symbol": "PPY", - "amount": "4432135" - },{ - "owner": "BTSCQPgmcaMLwep2ee1cCUHLLXpC9sUuvA51", - "asset_symbol": "PPY", - "amount": "340169" - },{ - "owner": "BTSEyeZE4GRHinTznHcTQRNZSVSXdYWdrUoq", - "asset_symbol": "PPY", - "amount": "4932014" - },{ - "owner": "BTSBH2YUAwmf5iBUL1uoXPS3km1tqBaqp2eL", - "asset_symbol": "PPY", - "amount": "3388681" - },{ - "owner": "BTSLQVgZAsJrbSaSzrZZJEaa9Gt127RXuMtU", - "asset_symbol": "PPY", - "amount": "1326190" - },{ - "owner": "BTSNJDc3mGQ8qxMitaNujQW6gkyHMKkdXRLp", - "asset_symbol": "PPY", - "amount": "9781203" - },{ - "owner": "BTSDQb9MGWkv1h5DWeiSoBVRCiMVZN72H9iP", - "asset_symbol": "PPY", - "amount": "5573147" - },{ - "owner": "BTS4ZZVsczvfmgr3iKEXpDPexMvcr5bRZMBy", - "asset_symbol": "PPY", - "amount": "86297535" - },{ - "owner": "BTSJrZwy9UW6TzDF8ApRZepNrESEWk49yce9", - "asset_symbol": "PPY", - "amount": "184695" - },{ - "owner": "BTSDR1D942fepbBdXXbqkqW7J2eTGsJsyWyx", - "asset_symbol": "PPY", - "amount": "602368" - },{ - "owner": "BTSJqxhUTaby4rYydYCJH5NQwcq51XF8X2Q2", - "asset_symbol": "PPY", - "amount": "4516780" - },{ - "owner": "BTSHq8JCstPE3KKEQQwwarzrZMMYUJXypXnA", - "asset_symbol": "PPY", - "amount": "219529" - },{ - "owner": "BTSAomXuZySnr3GrmDMieGacco5PTafbMQJS", - "asset_symbol": "PPY", - "amount": "1803119" - },{ - "owner": "BTS7m5ZZiwXduzctc8uTVWcvggzP5pUeeJcn", - "asset_symbol": "PPY", - "amount": "5028065" - },{ - "owner": "BTS7zN7AW85NLVXHV3S9ERfW4BQQSqEfs2VT", - "asset_symbol": "PPY", - "amount": "4522425" - },{ - "owner": "BTS2YbRhGXimfJLJb7GJj9trEwhM7qJCMiUM", - "asset_symbol": "PPY", - "amount": "192061" - },{ - "owner": "BTSBU3BfimXssZKWM7GwpUwuycGoqxtLLyY5", - "asset_symbol": "PPY", - "amount": "663206" - },{ - "owner": "BTSGCHqqr7p1jPuFcRfXqeowFJ4Man2YKawm", - "asset_symbol": "PPY", - "amount": "4434857" - },{ - "owner": "BTSMxsk1GMdbxk5o4D2ywr8KPXaHwEJBah9y", - "asset_symbol": "PPY", - "amount": "179437202" - },{ - "owner": "BTSUujRLFxBVF4MHaGVKXCZT5GrPZQPYPgm", - "asset_symbol": "PPY", - "amount": "046375" - },{ - "owner": "BTS21sMy8WwRngapiWHpoqavqkgZLfeF4V3G", - "asset_symbol": "PPY", - "amount": "67635079" - },{ - "owner": "BTSGggmUD4k18pTkvZaMkUsbCFUbk4WT3yos", - "asset_symbol": "PPY", - "amount": "17569718" - },{ - "owner": "BTSPFiuwbreDkUas9Uijuj6fuKJ88BPGpnxJ", - "asset_symbol": "PPY", - "amount": "8797130" - },{ - "owner": "BTS6KCNk16SamDgHg2RxPHeEEjC1Eqbg4YPj", - "asset_symbol": "PPY", - "amount": "595026" - },{ - "owner": "BTSD3RnSiMJy1R4js27qFRMPaP7ZT5MSH2ru", - "asset_symbol": "PPY", - "amount": "23585203" - },{ - "owner": "BTSG4oa6BQbR6rumeHWi4jeWpgR4qm7kUxcr", - "asset_symbol": "PPY", - "amount": "4141797" - },{ - "owner": "BTSEYWq2sRaQrFYSPStqLqswFNo7YVbfdPUV", - "asset_symbol": "PPY", - "amount": "3413383" - },{ - "owner": "BTSMYELoVUFcrkTkUD26RYFec9vkPKuvYEhv", - "asset_symbol": "PPY", - "amount": "4421078" - },{ - "owner": "BTSJXWsaDYPnmwbqn5GT49GG5qHeD5cDnMYV", - "asset_symbol": "PPY", - "amount": "363140" - },{ - "owner": "BTSCpkD7dXKtB7hVz8Z6CjvdYrNZG8SEbH4T", - "asset_symbol": "PPY", - "amount": "524030041" - },{ - "owner": "BTSAVVYNbsJtY8kqmPJkpY5AU2JRupLHeBQf", - "asset_symbol": "PPY", - "amount": "35799663" - },{ - "owner": "BTS3Y7rdtUPsWHihkqdTQqm44AjdAkJ1JM3d", - "asset_symbol": "PPY", - "amount": "5986915" - },{ - "owner": "BTSLY9xMZCLwC7UvQcb9yu3zaTjk5YF1rfif", - "asset_symbol": "PPY", - "amount": "95322422" - },{ - "owner": "BTSEii8onNZwGKjvEVtf6wRwJvp5jDi8CRWK", - "asset_symbol": "PPY", - "amount": "188674" - },{ - "owner": "BTSAu38c5bZac5Z3Hgf9gFHE5WmMwWnnF9dw", - "asset_symbol": "PPY", - "amount": "520765588" - },{ - "owner": "BTS7SiHRXifTNmekZnHBniMBBHanRbHQ3hRT", - "asset_symbol": "PPY", - "amount": "4369362" - },{ - "owner": "BTS4AuT8nZ3tDS2m28seUFxzeYf1X4ksdjwu", - "asset_symbol": "PPY", - "amount": "1245580" - },{ - "owner": "BTSAei4SyaHWzKeWBkUKZQbaMQyupQtDZeDH", - "asset_symbol": "PPY", - "amount": "6236099" - },{ - "owner": "BTS67pFM3xcYxBGntEh8dnN1KgKpDdhfZ6DD", - "asset_symbol": "PPY", - "amount": "080653" - },{ - "owner": "BTSALgr7puSoHdyYZxzMKmZXDYK6Nay1ndaf", - "asset_symbol": "PPY", - "amount": "114111" - },{ - "owner": "BTSFfHvf6RAeZNW2GWBcehAUUJU1fW2urziB", - "asset_symbol": "PPY", - "amount": "11724900" - },{ - "owner": "BTSHM2ncAUWdpbxYhsWkTv2gdKJg7Zo3sxL2", - "asset_symbol": "PPY", - "amount": "912837" - },{ - "owner": "BTSH6nSmYhH9qkVGRYxH49QHtgh4J3a39bsR", - "asset_symbol": "PPY", - "amount": "3744572" - },{ - "owner": "BTS3HH7uuv3ZrAQAhe56pBzbPNcq7F5RP2ZW", - "asset_symbol": "PPY", - "amount": "14580567" - },{ - "owner": "BTS8jNN7BE2sjYVwAZhfBJVoikKEWAMhcBnp", - "asset_symbol": "PPY", - "amount": "1986542" - },{ - "owner": "BTSKfmhWQtYfLYRyzAsey1eignhBXr9Fgp1S", - "asset_symbol": "PPY", - "amount": "17313100" - },{ - "owner": "BTSDu7mzt8BRfsYdGh5oQD3P868utJWC9WQk", - "asset_symbol": "PPY", - "amount": "9544197" - },{ - "owner": "BTSDJXfQZTjGb5A1ezwnKu3p8vBz2NFLJqGA", - "asset_symbol": "PPY", - "amount": "82874489" - },{ - "owner": "BTSKDsKqnSiLJEKsG83U5JQ93eAj6wS77KBN", - "asset_symbol": "PPY", - "amount": "2282722" - },{ - "owner": "BTSCVnorbDA79rH3aMMW5XUbU5uoVkQ8xBLi", - "asset_symbol": "PPY", - "amount": "19135916" - },{ - "owner": "BTS2ULbHHv2icg4bhz2McPqekdvA7MMxGMY7", - "asset_symbol": "PPY", - "amount": "6264846" - },{ - "owner": "BTS3MeYawXFhmwxYJtjzComNovPwWxP1i2wh", - "asset_symbol": "PPY", - "amount": "6043660" - },{ - "owner": "BTSPXVBEanVEtRFn3uWSQBbtroXUu5SwJJKC", - "asset_symbol": "PPY", - "amount": "4880494" - },{ - "owner": "BTSPSSn8BAxaeVJZ8rT3tHKW8EMmb7Cmb9j2", - "asset_symbol": "PPY", - "amount": "45229699" - },{ - "owner": "BTS9YHGcrQk93RnomKVebZqTGFBouAB4aZo5", - "asset_symbol": "PPY", - "amount": "1882413" - },{ - "owner": "BTS374YSSFgjMjzZnH4kdkd87vpm4TRMHQQx", - "asset_symbol": "PPY", - "amount": "24012957" - },{ - "owner": "BTS3iNPee81ybbtgHVATpdx6qF3QD4u8Zsm9", - "asset_symbol": "PPY", - "amount": "5972915" - },{ - "owner": "BTSChzVoAzi8vinkGKkDvWxxGe1cLQrXBLRm", - "asset_symbol": "PPY", - "amount": "4537051" - },{ - "owner": "BTSKNxXVb4EMXmqcViJMMpN7yEyUBwQtRxSh", - "asset_symbol": "PPY", - "amount": "109557" - },{ - "owner": "BTS9Kbmses3Fv3o6dmUqAxXT8XjqweXXXub2", - "asset_symbol": "PPY", - "amount": "20127724" - },{ - "owner": "BTSLTHeDT77YJmMuaycTLLZzuCwdxhAH1tfM", - "asset_symbol": "PPY", - "amount": "2781772" - },{ - "owner": "BTSJpj2ybuvdY9WbnGk8cJg56AoDTFTMCQpd", - "asset_symbol": "PPY", - "amount": "11820879" - },{ - "owner": "BTS2ARg813SjqwUmNxtUwUGQQs5pZpw3kbjh", - "asset_symbol": "PPY", - "amount": "948496" - },{ - "owner": "BTSHQ2CjczCKYrbuU2DLRyFwKXQQkZ7JhvpT", - "asset_symbol": "PPY", - "amount": "1403943" - },{ - "owner": "BTSCSrwbKXhfM1YUT5HFnRLLL2N53hawpSte", - "asset_symbol": "PPY", - "amount": "848918" - },{ - "owner": "BTS3kTJfsWDh6HcMFuPHNpdirPHLDHFgaEQ2", - "asset_symbol": "PPY", - "amount": "10491368" - },{ - "owner": "BTSEvRGoqLNceHYQtWGFtKWipJidunP2zEgT", - "asset_symbol": "PPY", - "amount": "10281864" - },{ - "owner": "BTSE8JardNAvE58n8p8SWG7PrqaH7mkknPRQ", - "asset_symbol": "PPY", - "amount": "17834508" - },{ - "owner": "BTSFynDtiQqJtk3PU3a1NqVS9aUmwEn515My", - "asset_symbol": "PPY", - "amount": "81537079" - },{ - "owner": "BTS8iww4n615i6T6qgroRJdxH4j7wGmV5vK9", - "asset_symbol": "PPY", - "amount": "6578400" - },{ - "owner": "BTSNEZx2aKXxeZRphzaEXbEc1GGZXLwwwgaB", - "asset_symbol": "PPY", - "amount": "1779396" - },{ - "owner": "BTSHAbWh1SggaZtkv81UjEJ8wC7x4PQuWdan", - "asset_symbol": "PPY", - "amount": "15373980" - },{ - "owner": "BTSey126HoyuAYMno5NwDZcpboYPra1SM25", - "asset_symbol": "PPY", - "amount": "34046476" - },{ - "owner": "BTSCD18G14M6P7zMhE6pbcTVYUCPynDEh5D2", - "asset_symbol": "PPY", - "amount": "084150" - },{ - "owner": "BTSQ8zzAomE1F27dQdDBdNy2ajsyVuYEZPtw", - "asset_symbol": "PPY", - "amount": "344173" - },{ - "owner": "BTSL5djC9x1RXBex5BxbUrKsgRJGvzThKk95", - "asset_symbol": "PPY", - "amount": "74363549" - },{ - "owner": "BTSHJom9CCUm4qkAvhBgR58vASfBxYaBMsxY", - "asset_symbol": "PPY", - "amount": "12550000" - },{ - "owner": "BTS7nnmNr91762o1ZxnY4D5WCrjcQ3HDokxt", - "asset_symbol": "PPY", - "amount": "45104060" - },{ - "owner": "BTS4xSufY1ME2m2V4y2vxQJYexTq8NcQLrGw", - "asset_symbol": "PPY", - "amount": "6905129" - },{ - "owner": "BTS7rLaNgmti8eCpYThT2MEzUphB6QQ2bgMX", - "asset_symbol": "PPY", - "amount": "23396680" - },{ - "owner": "BTSCaSDmbjyG2wUVn6JGB15mdNcLSGXwquas", - "asset_symbol": "PPY", - "amount": "6309978" - },{ - "owner": "BTSMaXqrRZwTZ35VWDyj1s4uTx5GoRDiQK4i", - "asset_symbol": "PPY", - "amount": "49908507" - },{ - "owner": "BTS7WYWZ2opzquEogX6UYU2af7KsxBWnjURy", - "asset_symbol": "PPY", - "amount": "23599833" - },{ - "owner": "BTS9N2nduAkrRmjZdHb8NXdeDFsLPV36p2t1", - "asset_symbol": "PPY", - "amount": "2415621" - },{ - "owner": "BTS3Yb9YPZoEkbAVhFgT5FL2tS2bNb2uJsCe", - "asset_symbol": "PPY", - "amount": "253488" - },{ - "owner": "BTSGb38doR2taRREMUt5GD6hsirn2TCj1fco", - "asset_symbol": "PPY", - "amount": "94555520" - },{ - "owner": "BTSCZQ29qQzk3mdQ9Nk9rWBXDVzUGygYbUHc", - "asset_symbol": "PPY", - "amount": "17136424" - },{ - "owner": "BTSJBvTCCKEhT7xv21krRhJZVyogBRM9oLJY", - "asset_symbol": "PPY", - "amount": "111284674" - },{ - "owner": "BTSGtgoifgN1guLW9KMiRsbDXeYCVw2Lku6N", - "asset_symbol": "PPY", - "amount": "3723539" - },{ - "owner": "BTSFpCqbkkPFTAL4KCat34MAVPgUen577DQs", - "asset_symbol": "PPY", - "amount": "2503480" - },{ - "owner": "BTS8t6RXK6SA4MUy3FJ3GVgQG7xVquyWVjzc", - "asset_symbol": "PPY", - "amount": "35569926" - },{ - "owner": "BTSFNCV1rCjRNAXasHHYrB915LB3LiwrV4QK", - "asset_symbol": "PPY", - "amount": "17640360" - },{ - "owner": "BTSGot12iZuggkvnCVKXnvXwpM3TJYwsxa7R", - "asset_symbol": "PPY", - "amount": "5150717" - },{ - "owner": "BTSFtvvwyzvVyw9jpxWbDcgnuFJ986gTCYsA", - "asset_symbol": "PPY", - "amount": "950425" - },{ - "owner": "BTSJngTfuLcXNVDYpR4FpsVHz3YvavCiHqxB", - "asset_symbol": "PPY", - "amount": "10316748" - },{ - "owner": "BTSEhJYSPBW1UxJmEucqZwbVP3YxVjwS3wv", - "asset_symbol": "PPY", - "amount": "107939247" - },{ - "owner": "BTSBBDwGgFmNbq56P5r8RW2HheEoaXqogbQs", - "asset_symbol": "PPY", - "amount": "4769364" - },{ - "owner": "BTSMbFxFoqK88rCgtdafWP8RLZgmVT6EsD1o", - "asset_symbol": "PPY", - "amount": "17379205" - },{ - "owner": "BTSCPHcLnSrr1YmQsL38VeFaWG133VHmGs1s", - "asset_symbol": "PPY", - "amount": "1250960" - },{ - "owner": "BTS782oaViNVsiTbmXp9RteTKiB66DzrgbJg", - "asset_symbol": "PPY", - "amount": "010989" - },{ - "owner": "BTS6irccAdw3HgCfac6s4Mf5Tb3fohXv7Uij", - "asset_symbol": "PPY", - "amount": "473105491" - },{ - "owner": "BTSN5ygAsHJBxWrpixcuW9egm6oqaFH4Z1ro", - "asset_symbol": "PPY", - "amount": "6693136" - },{ - "owner": "BTS2tkyjM9ESruqvR91U3GTr6bTbKmS4gsM9", - "asset_symbol": "PPY", - "amount": "6691392" - },{ - "owner": "BTSEvJxzqPSCVvmK7XbJRo6THkrBaoyMy6sK", - "asset_symbol": "PPY", - "amount": "12566772" - },{ - "owner": "BTSLtTS7PTMGtLLXdYMpNzKVhzB6P59bFjnq", - "asset_symbol": "PPY", - "amount": "12123564" - },{ - "owner": "BTSJtkNWXim1GRSTmDNLhx1YyDM7eE8ygPzY", - "asset_symbol": "PPY", - "amount": "5578443" - },{ - "owner": "BTSNKsEDL9LJNQH3kbsS4CuQLmUd5WeJMHdY", - "asset_symbol": "PPY", - "amount": "66608068" - },{ - "owner": "BTS7jQiyE7hSfsdoNJa7YqVyj1rEeBwZpZBA", - "asset_symbol": "PPY", - "amount": "4168987" - },{ - "owner": "BTS2EC4gPi2WebbWJeXj4yjjYEbXhXDWkWTq", - "asset_symbol": "PPY", - "amount": "983908" - },{ - "owner": "BTSGs2HNeuPWnELqcEXho9n92QTr72oC3JkR", - "asset_symbol": "PPY", - "amount": "11699895" - },{ - "owner": "BTSCyHtwLCks1Nr3uyXPHk2irbxZEEDkwqqs", - "asset_symbol": "PPY", - "amount": "005008" - },{ - "owner": "BTSAgf8GZGndoi3Dgue1e1iHCnmgbYzLzrp7", - "asset_symbol": "PPY", - "amount": "88416274" - },{ - "owner": "BTSMjvnL8vLvp1tRvWvd9yBTrZqm2iBqMScj", - "asset_symbol": "PPY", - "amount": "7618519" - },{ - "owner": "BTSB3xmoCoGypxwwsfpZM7LhMpys3Tae2xKN", - "asset_symbol": "PPY", - "amount": "18894838" - },{ - "owner": "BTSAYKDM99VxZZgrKTcGRu18vqswbxZVyEg2", - "asset_symbol": "PPY", - "amount": "3149567" - },{ - "owner": "BTSHDqfJ7WSL2uDZ9ALEgM7QeJujfKhqBc2T", - "asset_symbol": "PPY", - "amount": "67889143" - },{ - "owner": "BTS3X1SgwQacYUT3Jx8LpvCQ6LGk9eTCcQvL", - "asset_symbol": "PPY", - "amount": "9522919" - },{ - "owner": "BTS6Zy9JiZXb7KqzYToi7oqF3JW6ZTmdQXMX", - "asset_symbol": "PPY", - "amount": "62987862" - },{ - "owner": "BTSEbFQexkEMmWGzgD5xFPKMPPsVJ9SuiJyD", - "asset_symbol": "PPY", - "amount": "1392003" - },{ - "owner": "BTS4r3jj9EVNbemi6oJ5VNeUEoVnQKevzSxT", - "asset_symbol": "PPY", - "amount": "104240" - },{ - "owner": "BTSQEPGHUzDr7o1HAL2cwczEbBYzmnVXQeWo", - "asset_symbol": "PPY", - "amount": "49944218" - },{ - "owner": "BTSM1Wy5bMymDiavL6Us7QR6n8CbM5BipRfv", - "asset_symbol": "PPY", - "amount": "785054" - },{ - "owner": "BTSGeVUy3HvkuZ7agMLQTA8WtcNHdY6vXLo1", - "asset_symbol": "PPY", - "amount": "33081384" - },{ - "owner": "BTS5nddVGPYZPEAmbSoDdf5qSXJoLPhBJfEg", - "asset_symbol": "PPY", - "amount": "999777" - },{ - "owner": "BTS8Mjrch1Xo6jUj3AY5UTLpudGjzXHcwdF7", - "asset_symbol": "PPY", - "amount": "6795486" - },{ - "owner": "BTS2FCXdFyGGov4ibqSUGs3XTjiN5HLTPS8M", - "asset_symbol": "PPY", - "amount": "351563" - },{ - "owner": "BTSDR6pb2QrvN6DesK8v3hL733CTTsRRrra", - "asset_symbol": "PPY", - "amount": "51599600" - },{ - "owner": "BTSKQhBgShmUSPZnMZS5oxWdKqJ2Ge7jEsTM", - "asset_symbol": "PPY", - "amount": "5263168" - },{ - "owner": "BTS9TArsqXZSTa9MkP7MEYcCCxZ4MApB986U", - "asset_symbol": "PPY", - "amount": "11691813" - },{ - "owner": "BTSCsXEnqNasVF2zF2HDKKQQktXEL4ixFwGJ", - "asset_symbol": "PPY", - "amount": "49466683" - },{ - "owner": "BTSMX2X4WfgqdrPF1Nw18DVK3dmwPww5ZNX5", - "asset_symbol": "PPY", - "amount": "43727290" - },{ - "owner": "BTSC5NRLm9CwoccLQocgCqQdbYvo1VBJqrSP", - "asset_symbol": "PPY", - "amount": "8066119" - },{ - "owner": "BTSB2hwRXMu3yaxNnefGgWE2R6mrzoQ83y4N", - "asset_symbol": "PPY", - "amount": "1029072" - },{ - "owner": "BTS77FE3zpkd37W8LsEBRa3uBLTg42XZMYGg", - "asset_symbol": "PPY", - "amount": "33616305" - },{ - "owner": "BTSANzNtHECsPnPo13SHdJTNbNPGebq244ZH", - "asset_symbol": "PPY", - "amount": "1933236" - },{ - "owner": "BTS5VzYbdVXvY88jpuCePomymf8zp99LbugY", - "asset_symbol": "PPY", - "amount": "40474221" - },{ - "owner": "BTSFHTz6WzJfhiNmYZMgmoqwgxVp67B4ALAD", - "asset_symbol": "PPY", - "amount": "6894873" - },{ - "owner": "BTSD83uKvM3s5s48b147obx1XiGxA7Dkoa4c", - "asset_symbol": "PPY", - "amount": "17438700" - },{ - "owner": "BTSC1KebJqN5spLTc4kbAAmLg1rX4YcmAdKy", - "asset_symbol": "PPY", - "amount": "1549026" - },{ - "owner": "BTSBA7V67T7fYkDkW6K62jyyLoBsTo1RnrZN", - "asset_symbol": "PPY", - "amount": "346454" - },{ - "owner": "BTSHjvUfQ5jzK5eEwMQ8jTWDqBXgz4jNfjac", - "asset_symbol": "PPY", - "amount": "1624964" - },{ - "owner": "BTSG63NKAKju5ejQ4JaM1QU3CwkL7tnJqngB", - "asset_symbol": "PPY", - "amount": "104337037" - },{ - "owner": "BTSAKFZTnY3MErRgkogaHuVKffiXyNhZ6f87", - "asset_symbol": "PPY", - "amount": "7525333" - },{ - "owner": "BTSJef4cNLofBz4KcYZt6xhvhZ6ya32HjboH", - "asset_symbol": "PPY", - "amount": "4566095" - },{ - "owner": "BTSJ2iWXb3wk8wbEuWDRUALxyzBpBJjCSenM", - "asset_symbol": "PPY", - "amount": "15460243" - },{ - "owner": "BTSNy2yb45X21GfyCS6DfBfbBcDq9RM86xud", - "asset_symbol": "PPY", - "amount": "1909932" - },{ - "owner": "BTS6WW2zvjSTdtxjqtzyeAtsdUNeXLiJZn8M", - "asset_symbol": "PPY", - "amount": "673049" - },{ - "owner": "BTSMroso5u7EwojMnzMpdzvLSfwPi7afhFWV", - "asset_symbol": "PPY", - "amount": "1913660" - },{ - "owner": "BTSH6xHRs7MFBstc9brCahjbf27bCYWYA945", - "asset_symbol": "PPY", - "amount": "6113905" - },{ - "owner": "BTSLLHffF9LyLQu2wYYqZLUctt5f3bbuhdyF", - "asset_symbol": "PPY", - "amount": "34801200" - },{ - "owner": "BTSHuGUaN5q5b4JEkdzwTyoJxJatNUe9fyFe", - "asset_symbol": "PPY", - "amount": "000473" - },{ - "owner": "BTSJa6kPEZ8AJgPPTMaSX6osjboVJmaNDEAk", - "asset_symbol": "PPY", - "amount": "4200975" - },{ - "owner": "BTSCZH9iarwGMeTofZJBJnNX1dGDAdSaWq4V", - "asset_symbol": "PPY", - "amount": "4191035" - },{ - "owner": "BTSAPjpve4yJ3Tri7TZe4GhZGHFocofWHF5X", - "asset_symbol": "PPY", - "amount": "2007301" - },{ - "owner": "BTS79MHuw5yXgEYNEsto8Vz1KHkPzNzdzDqA", - "asset_symbol": "PPY", - "amount": "3087381" - },{ - "owner": "BTS642uE4T8hB7oQe9Din7psycBX4CUJktTP", - "asset_symbol": "PPY", - "amount": "6234900" - },{ - "owner": "BTSPFiqzQsnb8DnTLZu3u6jFV2rVm3gKEUUZ", - "asset_symbol": "PPY", - "amount": "5271099" - },{ - "owner": "BTS9aBZX18qjy68Wvkre2XgofxLJVZPJq4Yu", - "asset_symbol": "PPY", - "amount": "13361635" - },{ - "owner": "BTS7zUgLoMr42TW1BHAgWdLKCEG7NhY1zbCa", - "asset_symbol": "PPY", - "amount": "102970857" - },{ - "owner": "BTS66zo4zoL5mQHNWzmCSkcpxXSuW4jZaP2s", - "asset_symbol": "PPY", - "amount": "378785" - },{ - "owner": "BTSAjJNsaTPtqKxa2EA6rDat34gzB7SvA69F", - "asset_symbol": "PPY", - "amount": "1671500" - },{ - "owner": "BTS4Y2TbuZZEpwxPDEZHypoReh9TrVfjb1Ds", - "asset_symbol": "PPY", - "amount": "13165307" - },{ - "owner": "BTSB8qZpJayBUbVzDYmrs2iF9U6jr3bHox2H", - "asset_symbol": "PPY", - "amount": "343800" - },{ - "owner": "BTS2WtM4i49nF3mXZ4hsJtCqbrxJRLwFeqrD", - "asset_symbol": "PPY", - "amount": "033938" - },{ - "owner": "BTSPuYmotAsHFGmCKwBsR9X6MXZjdVzr2qhU", - "asset_symbol": "PPY", - "amount": "7185135" - },{ - "owner": "BTSNScsahGixPxjEZqLkQyxHqoRwe4TEu1Zd", - "asset_symbol": "PPY", - "amount": "330502754" - },{ - "owner": "BTS5bHLd5bLGdkCWM4BKmv3bwFtke5WJvCc1", - "asset_symbol": "PPY", - "amount": "1955120000" - },{ - "owner": "BTSJ632W1S9aFAQdCGFAkY7bfifcMBz8Rdth", - "asset_symbol": "PPY", - "amount": "31884927" - },{ - "owner": "BTSDsGLjNjMATiT518v9La7BwrZR8MMKwdzS", - "asset_symbol": "PPY", - "amount": "7944469" - },{ - "owner": "BTS5fNJxV62zWXfpZhcvuCZrM7BNFWU1jjHR", - "asset_symbol": "PPY", - "amount": "208586693" - },{ - "owner": "BTSFEy5HFEeUaBwb38QMHLcejTWXsqBw6Kmv", - "asset_symbol": "PPY", - "amount": "25306569" - },{ - "owner": "BTSEpjswo7sXAetj1Prbvd6h4W7XYzG6TXeM", - "asset_symbol": "PPY", - "amount": "31961413" - },{ - "owner": "BTSHtFRp6ct3TkERZWJG1wDRFXoQfW8jADX1", - "asset_symbol": "PPY", - "amount": "20571425" - },{ - "owner": "BTSPMHm5v2tTVaL4xPZcUzXiGVzbx9aQLspt", - "asset_symbol": "PPY", - "amount": "817662" - },{ - "owner": "BTS2CvWgLb3vocc1dSLZqSN4thZnyZoa4heb", - "asset_symbol": "PPY", - "amount": "190057" - },{ - "owner": "BTS2pdTzShVkqWnEkAHLtncn9Cp17MvpyHL7", - "asset_symbol": "PPY", - "amount": "1105170" - },{ - "owner": "BTSPT9NjM7VoP9XepAjXXuthMimwxmcskiMr", - "asset_symbol": "PPY", - "amount": "1030567" - },{ - "owner": "BTSN3Pn7LFRXJGDQTCYdTDyk2BkUBe8cSVD3", - "asset_symbol": "PPY", - "amount": "10176568" - },{ - "owner": "BTS65jr2NnAz7szmXEjwXHCKrDFWQyaqFu2p", - "asset_symbol": "PPY", - "amount": "451213" - },{ - "owner": "BTSYccHVAEhNMDWtaf4At55yiQwNtMeMx5G", - "asset_symbol": "PPY", - "amount": "033137" - },{ - "owner": "BTS4cubVf1xHcuDYmggj1DA1fZBGtHMHjtgh", - "asset_symbol": "PPY", - "amount": "423541" - },{ - "owner": "BTS7p7tXKXZaGjw8BznWUtsdTwxPUPYeZ5vk", - "asset_symbol": "PPY", - "amount": "4010124" - },{ - "owner": "BTSFjWpbpUqCbPEArNWnD8puLAaju8NhjEaQ", - "asset_symbol": "PPY", - "amount": "6640403" - },{ - "owner": "BTS2nonAPKtWnXmoo1zrUTWkQTQnaZvB79Cg", - "asset_symbol": "PPY", - "amount": "6613680" - },{ - "owner": "BTSHfxASWSwPhAGAA76rYU2JuB8WLZFzKhaB", - "asset_symbol": "PPY", - "amount": "33370982" - },{ - "owner": "BTSG9LqUkcQt9niVmCkyUt3uvA4eXSMzyw6Z", - "asset_symbol": "PPY", - "amount": "1210106" - },{ - "owner": "BTSJdHFQJQzzds4eYiA1shzFYi7jQ4cjpdF7", - "asset_symbol": "PPY", - "amount": "1263776" - },{ - "owner": "BTSH9HoZgUa1iRSFEf72wBvgG8Li3ZGxyQEQ", - "asset_symbol": "PPY", - "amount": "1418257" - },{ - "owner": "BTSAZ4R2X6Bt31JcvCxXdzGtcYAo8AWDPkny", - "asset_symbol": "PPY", - "amount": "611564" - },{ - "owner": "BTSQDv3zxP9qjtQTDzk2Q9Qcd29sy2ZLEK62", - "asset_symbol": "PPY", - "amount": "814213" - },{ - "owner": "BTS4jwJZJMP6ypjXm9ha8TnBy8Uo3MJt8NpH", - "asset_symbol": "PPY", - "amount": "7583087" - },{ - "owner": "BTSBJtzaWEAAs65TyM8YapzxQTjCtBnPEu1m", - "asset_symbol": "PPY", - "amount": "46532160" - },{ - "owner": "BTSNF2gSJ76MRX6w6eLYwNvqzEy3Et9Na3pL", - "asset_symbol": "PPY", - "amount": "30018492" - },{ - "owner": "BTS89GXmtERWu29MBRgNPraUZPeHM1YqQsef", - "asset_symbol": "PPY", - "amount": "175291" - },{ - "owner": "BTSE29LprgZ5LUP7ZaRMZhyjS9P5UHahqZeH", - "asset_symbol": "PPY", - "amount": "352237" - },{ - "owner": "BTSDjucvRDe1xpUUq6LaT32YmnENkQ1cdNsV", - "asset_symbol": "PPY", - "amount": "5168935" - },{ - "owner": "BTSN1aS4FijFRs4Fzt9goJKKtRJCz12suegy", - "asset_symbol": "PPY", - "amount": "323051" - },{ - "owner": "BTSPCCMJbr1L4MU6XdGJogey6j48Wsk9GKqg", - "asset_symbol": "PPY", - "amount": "168313102" - },{ - "owner": "BTSaR4QsmQDYVrQ5mg86R8MKzcpEjkNUsia", - "asset_symbol": "PPY", - "amount": "563680" - },{ - "owner": "BTS2cBucS7MNxmUZzLjTGbjRYEZYDNbyMeoY", - "asset_symbol": "PPY", - "amount": "8328234" - },{ - "owner": "BTSK5NUjH7fRfrgFBAeEvct3AxmJWTs2v7k", - "asset_symbol": "PPY", - "amount": "13713422" - },{ - "owner": "BTSLYpd68mmSP3dkF1wbpk7WFGPgSNWML48V", - "asset_symbol": "PPY", - "amount": "13295178" - },{ - "owner": "BTSJQXeNagFCB1q4iniUQMA7oDBGvkn4KcsL", - "asset_symbol": "PPY", - "amount": "143149440" - },{ - "owner": "BTSH8P4xdZzK5mtgUUFXnnm123ASFnBA8Cam", - "asset_symbol": "PPY", - "amount": "7157612" - },{ - "owner": "BTS2UdDcZmQN2tN9er6WuCpzFeD7yKfvgpNQ", - "asset_symbol": "PPY", - "amount": "35738820" - },{ - "owner": "BTSDRD3uGnztqhEY2PreFvQc7keTMeGsfdET", - "asset_symbol": "PPY", - "amount": "2016719" - },{ - "owner": "BTS77EP8FKsctsiobNinriK5kEAFrdPWzsdC", - "asset_symbol": "PPY", - "amount": "3160560" - },{ - "owner": "BTS8r4Xa1ieBn3Hfn66uUbsux5N4TfjD8CQ6", - "asset_symbol": "PPY", - "amount": "17546383" - },{ - "owner": "BTS5LqeaaY8Bixf9zL9MU55ekmn3uQkRHeEG", - "asset_symbol": "PPY", - "amount": "15845015" - },{ - "owner": "BTS13VuVyCeaKhSoBCevEv96fnhdamRrAGNs", - "asset_symbol": "PPY", - "amount": "2578370" - },{ - "owner": "BTSFKsvueR5T4imGs2uf11mbaz28oiBSADw8", - "asset_symbol": "PPY", - "amount": "2409090" - },{ - "owner": "BTSM5oH6xnoa8rqrPgibhHZms354RLe5Y3LW", - "asset_symbol": "PPY", - "amount": "044934" - },{ - "owner": "BTSCzPgPHZe2jfTAng1Xs7g84qZWo4pj9cFz", - "asset_symbol": "PPY", - "amount": "6379107" - },{ - "owner": "BTSC3jA7WM1wmrjsnbEQQikg5US5Gq6XznPp", - "asset_symbol": "PPY", - "amount": "6319880" - },{ - "owner": "BTSHiSBeDfKW1WCksd1cb3Sb7r3tPawJRJVu", - "asset_symbol": "PPY", - "amount": "2316903" - },{ - "owner": "BTSEgaV92kFQFQgDJiQ92Yzddy5ihSygqkfb", - "asset_symbol": "PPY", - "amount": "35227782" - },{ - "owner": "BTSLDduDe1c3sVRw6GfMYsT1kZXN8fopZtCL", - "asset_symbol": "PPY", - "amount": "847866" - },{ - "owner": "BTSGAAHfo347NsKJFbDFJbE7PshKAuRi4Hao", - "asset_symbol": "PPY", - "amount": "5792041" - },{ - "owner": "BTSJnREd5jT9WMnyzhbMeavRD9JXWmUJjCkK", - "asset_symbol": "PPY", - "amount": "3319406" - },{ - "owner": "BTS5i1WJmhPwWumQAsKmUXmZKnx6NASjVhzg", - "asset_symbol": "PPY", - "amount": "161096" - },{ - "owner": "BTS26Jv5uKw4RTRU9Ux4sCVWfFgCMeXfFEx8", - "asset_symbol": "PPY", - "amount": "7998941" - },{ - "owner": "BTS2yKtjbQgVBrzidCZ8U2tdiMrE91KwW5C3", - "asset_symbol": "PPY", - "amount": "67796889" - },{ - "owner": "BTSF3V39dvCNPBoSdD9D5YBsrDRpuku8QCQS", - "asset_symbol": "PPY", - "amount": "1478227" - },{ - "owner": "BTSPrBjab8Bp8MFNofbFvL4DJUFgxwDewzRX", - "asset_symbol": "PPY", - "amount": "21861186" - },{ - "owner": "BTS5VcSFJ8ZBz3H43Gos55XdssKBVzWeqU9g", - "asset_symbol": "PPY", - "amount": "163925684" - },{ - "owner": "BTS4AJqwKjAbm2nA9ZZfP6pzr53XQBnJTwDg", - "asset_symbol": "PPY", - "amount": "2955285" - },{ - "owner": "BTSLsSwdsZz4DMbn5RT8HXQB9WHe4YrL3ivP", - "asset_symbol": "PPY", - "amount": "6482860" - },{ - "owner": "BTSNsFhYoeKemv1JpzRrJNsA84NoTzL3jX83", - "asset_symbol": "PPY", - "amount": "5664992" - },{ - "owner": "BTS3rWRaiRBabbzpsaPvTtUeMC8vJZBd91fH", - "asset_symbol": "PPY", - "amount": "845191" - },{ - "owner": "BTSNqHLvLh9AXQsQho6ZzBXoHbN2tWUQXrW", - "asset_symbol": "PPY", - "amount": "343048800" - },{ - "owner": "BTS3twfEDA5EqJKQsg8mHwCVH9XhxtMbD3JD", - "asset_symbol": "PPY", - "amount": "7445544" - },{ - "owner": "BTSNvBQZDDmk4CWcospqHycM8Crnf6hThYLq", - "asset_symbol": "PPY", - "amount": "15124881" - },{ - "owner": "BTS38aY7DkBCrE52QGxtYt7RkwXsdahCd84Q", - "asset_symbol": "PPY", - "amount": "187868" - },{ - "owner": "BTS7dZwhT4FZQB4SC9qXPgusr8yqJAKUdxCn", - "asset_symbol": "PPY", - "amount": "2515896" - },{ - "owner": "BTSCn43dtQZpsDn56w8ayZ9RxPFtrVYB9mSj", - "asset_symbol": "PPY", - "amount": "3285120" - },{ - "owner": "BTSHKo46vWUPH7D22jiu242cE8AFKezaLWM4", - "asset_symbol": "PPY", - "amount": "34743213" - },{ - "owner": "BTS3GCxW3NL5t27AoJ8jB23QoekdB4ETMeYp", - "asset_symbol": "PPY", - "amount": "1815346" - },{ - "owner": "BTSHNur4VxtY3Vc4AQdxm7W87jCrWwStKrft", - "asset_symbol": "PPY", - "amount": "117664" - },{ - "owner": "BTS2sXnAkRNE9rW3mqVt3PVBrzNGyQLzS8yn", - "asset_symbol": "PPY", - "amount": "4852106" - },{ - "owner": "BTSNzFnPvR2x59GbAS87bter4qZyqkQ2TGRT", - "asset_symbol": "PPY", - "amount": "4003092" - },{ - "owner": "BTSG6XbRRTfnSJ5KLhSA6RmQY1RhQs7PEmz4", - "asset_symbol": "PPY", - "amount": "5009192" - },{ - "owner": "BTS3FiQnNe4FYckvqiahGBMwBSU4P5G54A5U", - "asset_symbol": "PPY", - "amount": "1281532" - },{ - "owner": "BTS5XwqZvN5SdbcxTQLYHYmjuyFgmHAVKPuM", - "asset_symbol": "PPY", - "amount": "4831243" - },{ - "owner": "BTS9KTsFrL1pqD5HkKTKA6dveNxpGyWdWAXc", - "asset_symbol": "PPY", - "amount": "70254819" - },{ - "owner": "BTS2UrsiWp2bothQEoLQ1wZEUeFr155ZMkgQ", - "asset_symbol": "PPY", - "amount": "213663" - },{ - "owner": "BTSDNy9yLupjChs5gMkzmn2ZbU7TN97rqwGS", - "asset_symbol": "PPY", - "amount": "15225745" - },{ - "owner": "BTS2FVW1y7xWtEbFuKZJgEkQ1NHvj7x9K64J", - "asset_symbol": "PPY", - "amount": "6693685" - },{ - "owner": "BTSKSLPSLyD7b5UfCAnRQR8C7jS4oamCmift", - "asset_symbol": "PPY", - "amount": "29312550" - },{ - "owner": "BTSKGKquebubCFhrUzU8wauAEP2PqEhj9xvT", - "asset_symbol": "PPY", - "amount": "1027073" - },{ - "owner": "BTSEVtmVnniaRLP3cp4Pi8DTi4cSEHJhpGf6", - "asset_symbol": "PPY", - "amount": "82259048" - },{ - "owner": "BTSauxPdV5hyWhMeoRRLCvmEjwBJRwZNMfp", - "asset_symbol": "PPY", - "amount": "1713385" - },{ - "owner": "BTSNrnM3hVvr9JFBcQczKVoRAHdqQvKXfFox", - "asset_symbol": "PPY", - "amount": "3414974" - },{ - "owner": "BTSFaefg9p8TU3bqBnfNSgf8oY3hXbitSBB5", - "asset_symbol": "PPY", - "amount": "200555" - },{ - "owner": "BTS79VTRRpUHS7FgSNTFovUdhiipQcEqtx6b", - "asset_symbol": "PPY", - "amount": "1215468" - },{ - "owner": "BTSL93FFGF8ZVxjA9oD7EYtMp1BPXV8nC5WG", - "asset_symbol": "PPY", - "amount": "685139" - },{ - "owner": "BTSAWLh7SCJBaNDDhSLkowfxxXmtsSBpj67S", - "asset_symbol": "PPY", - "amount": "2413376" - },{ - "owner": "BTS6HxsXYnJMLDQbF1w5mmawJGEpDBtY5bNJ", - "asset_symbol": "PPY", - "amount": "6970562" - },{ - "owner": "BTSCVrFGcicZXr2DkcDwWhoaYjEXfHDxpdfZ", - "asset_symbol": "PPY", - "amount": "096297" - },{ - "owner": "BTSGds3qr5ddvivEC5AXWQ1ycUGjHmwUCq4Q", - "asset_symbol": "PPY", - "amount": "25500000" - },{ - "owner": "BTS5sZkLg8rX6fyZGC6S2J9rNc7GvPnC7rJW", - "asset_symbol": "PPY", - "amount": "2025143" - },{ - "owner": "BTSPUzztnHdXz1TcNq7AoQA6YTUxji2H8GEH", - "asset_symbol": "PPY", - "amount": "7085605" - },{ - "owner": "BTSMbLh787oTmsixFDqVAPTak8VgFofRKzGG", - "asset_symbol": "PPY", - "amount": "13607734" - },{ - "owner": "BTSEz8oiVYSkwLcoNgdkbhnyaiomdw1a9RTg", - "asset_symbol": "PPY", - "amount": "60345865" - },{ - "owner": "BTSBnEUWa4nhtt2STEmHn8ZJmWPiDXR5Eocy", - "asset_symbol": "PPY", - "amount": "8549892" - },{ - "owner": "BTS5FnisKNJojViPVFWoT4aQkCCB193toihq", - "asset_symbol": "PPY", - "amount": "2492050" - },{ - "owner": "BTSPQbxeFSHtpvkegDERNLjuZo7MapDyHCkP", - "asset_symbol": "PPY", - "amount": "1070994" - },{ - "owner": "BTSDRCYkt3SA7zxAYhBqLmHUFP3JCxNMJnLv", - "asset_symbol": "PPY", - "amount": "3247778" - },{ - "owner": "BTSPruoF8BTDcrfMYu2oub8QEz71PUYwfqtz", - "asset_symbol": "PPY", - "amount": "42791960" - },{ - "owner": "BTS7ruXTNoZrKKdwvW92KnVbjS2WsKsLso2v", - "asset_symbol": "PPY", - "amount": "37847613" - },{ - "owner": "BTSFeSV8LZrqicfUyTUkgzdA8QdRoLhbbCfa", - "asset_symbol": "PPY", - "amount": "914004" - },{ - "owner": "BTSJ5GXEQQvexenZXuVJTvypgrq8KHuCTR2Z", - "asset_symbol": "PPY", - "amount": "023890" - },{ - "owner": "BTSKFjUEfZQvcZNiCatovughWERgRRfBAyie", - "asset_symbol": "PPY", - "amount": "3620948" - },{ - "owner": "BTSLMapWhBdgaAkJ9B38nJwAvqGFGvnkvWmR", - "asset_symbol": "PPY", - "amount": "7713442" - },{ - "owner": "BTSJTQt2AeGiEd8uFsgvvwVn8acgSfXYYEdV", - "asset_symbol": "PPY", - "amount": "6584317" - },{ - "owner": "BTSFNLzce2uKAwpczZvp4jdcDj12avXYRGpc", - "asset_symbol": "PPY", - "amount": "1926368" - },{ - "owner": "BTSJcX4wpCM61hPeFwNkQStT1PCtTyCz6Qu1", - "asset_symbol": "PPY", - "amount": "37237934" - },{ - "owner": "BTSM9cWp4jJDjiG4ikks67Q5iV66uaLN1HsS", - "asset_symbol": "PPY", - "amount": "2053794" - },{ - "owner": "BTSBKronKKese5YHVUfRHL8zshGUPD6VVjar", - "asset_symbol": "PPY", - "amount": "814301" - },{ - "owner": "BTSHP597M9WdSqvMLVxDgCDtqqGLVk4WWUGj", - "asset_symbol": "PPY", - "amount": "93599160" - },{ - "owner": "BTSLzqb4oyDiGCYFwLk4BYY74TwngGJcYQy3", - "asset_symbol": "PPY", - "amount": "2050912" - },{ - "owner": "BTSHfzQjuMbhJ8AfLwR4t7t4ppraCaC5csuX", - "asset_symbol": "PPY", - "amount": "698055" - },{ - "owner": "BTSPPPmbdCzjMnhxefjAd3vKLXs4XCHDU2V6", - "asset_symbol": "PPY", - "amount": "1329117" - },{ - "owner": "BTS8FeVGmzbjGeyCRtNh4kL3ZQSc4ZAcv7PZ", - "asset_symbol": "PPY", - "amount": "2429417" - },{ - "owner": "BTS7JMDM2VNSP7hbjeRytG2F2MC6pHRJksKi", - "asset_symbol": "PPY", - "amount": "6474299" - },{ - "owner": "BTSak32tc6zV5XyivK9rbE1ajCT7SHsPY3C", - "asset_symbol": "PPY", - "amount": "5730838" - },{ - "owner": "BTS8YorubhogWU4tY9UazYPq7Y95aea4PCoP", - "asset_symbol": "PPY", - "amount": "713671" - },{ - "owner": "BTSoGiNJrRjCi5nwK5pKJN8Yg2NC7aE5s1Y", - "asset_symbol": "PPY", - "amount": "4687508" - },{ - "owner": "BTSFjASAGjABUGunKeFVHxoxovSFgGoSG1ho", - "asset_symbol": "PPY", - "amount": "092863" - },{ - "owner": "BTSNCNeMM3WWWHJDWAmGV5C9HMz2QjZFnEhj", - "asset_symbol": "PPY", - "amount": "13876587" - },{ - "owner": "BTSH5c7YkFa2m3Vz79KjiikDDAw54abzKuY1", - "asset_symbol": "PPY", - "amount": "51542000" - },{ - "owner": "BTS6niTQKK3Lk9a7RkUqBDkb7qexTFKnvEoD", - "asset_symbol": "PPY", - "amount": "1956599" - },{ - "owner": "BTSLydZDnCZfPcQ3pDzHN8YTw4RzGqzesuV8", - "asset_symbol": "PPY", - "amount": "23860160" - },{ - "owner": "BTSAvvTf3rrZfM12T1qSsCdbruz7AfR988JD", - "asset_symbol": "PPY", - "amount": "4046425" - },{ - "owner": "BTSFVFC3GEpBpmaSLTR8uj5tjx9cZWnjNFmQ", - "asset_symbol": "PPY", - "amount": "34009000" - },{ - "owner": "BTS76Xx6PQbSQ5jvqbTHSpQDBbAFXpgu2n4S", - "asset_symbol": "PPY", - "amount": "1215312" - },{ - "owner": "BTSBLC14sxDoxyNAaaT9SonM4nKNXomdXNUN", - "asset_symbol": "PPY", - "amount": "318406" - },{ - "owner": "BTS3ymn9oMjFiP5X8B61JCRvJPrp8qqU1NdW", - "asset_symbol": "PPY", - "amount": "8855413" - },{ - "owner": "BTS9geGyQch71W1YtzuBghsfUZWoL3TXPgv3", - "asset_symbol": "PPY", - "amount": "748920701" - },{ - "owner": "BTSEguvgmcMS2pHsBua7q3EvqXbPEkheGN8c", - "asset_symbol": "PPY", - "amount": "7096973" - },{ - "owner": "BTSPW2k5a47JrV6sSVjjf8KfDUZsuwCs7Vo2", - "asset_symbol": "PPY", - "amount": "50226573" - },{ - "owner": "BTSKQfhNsxAfEiPYLuuiFgDVdSHB7Kfdeb4M", - "asset_symbol": "PPY", - "amount": "8181920" - },{ - "owner": "BTS5EXbKrpCW3NZxTp1xqcCAMkP8PXLbtv1G", - "asset_symbol": "PPY", - "amount": "72199580" - },{ - "owner": "BTSDNXFFFPKyZxPrvs5dLNzwncsw6xQ4C92Z", - "asset_symbol": "PPY", - "amount": "1660581" - },{ - "owner": "BTS3Ufe3Vz2btQffK4TS8VhtsPKLvEkLtKe8", - "asset_symbol": "PPY", - "amount": "075416" - },{ - "owner": "BTS75WVfUnwUhQCmL261L6DZWc2xdwhipJ5U", - "asset_symbol": "PPY", - "amount": "032592" - },{ - "owner": "BTSDDe2J8HM1bH1nkE7jTbmvzMx2mdAoYo9d", - "asset_symbol": "PPY", - "amount": "20370926" - },{ - "owner": "BTSNjBRBMrRosH3nT9D9xf8VjxLxk293NezD", - "asset_symbol": "PPY", - "amount": "16766357" - },{ - "owner": "BTSGheBHTHvZnqgHZ2fZhCCz9X1Zv9FK2XDG", - "asset_symbol": "PPY", - "amount": "6582653" - },{ - "owner": "BTSPnTvHuiiVDSMLKfHyCtuvjp9uWqZ7vSMa", - "asset_symbol": "PPY", - "amount": "119332583" - },{ - "owner": "BTSDA2FSSgCDBZiAGmnQibWDHU8C9bw3iPkS", - "asset_symbol": "PPY", - "amount": "2776788" - },{ - "owner": "BTS7RCnfJcAwVXXFJdLVXnsRnys7PQxG7R1w", - "asset_symbol": "PPY", - "amount": "20800534" - },{ - "owner": "BTSE7XTmigJ9Rnb8tpUD5DCTJSkxQ8rMdnG6", - "asset_symbol": "PPY", - "amount": "3263324" - },{ - "owner": "BTSArYn4HaStavrmWTnTAPFmxqdDWTBWVuCo", - "asset_symbol": "PPY", - "amount": "32609140" - },{ - "owner": "BTSDmh7jfghSzuADVL9kJt36bV5dgCCkzt5u", - "asset_symbol": "PPY", - "amount": "347532" - },{ - "owner": "BTSNnM9PHGrDAbgp49HJnsnGtBPnbYDtpMjS", - "asset_symbol": "PPY", - "amount": "16647520" - },{ - "owner": "BTS8NuXzp15wLf6GYX2h7JFHU88nnwdxwZ1b", - "asset_symbol": "PPY", - "amount": "36085135" - },{ - "owner": "BTSEQ9bxCjaiMw9KWXoBwaRAr6bQgDfEbug3", - "asset_symbol": "PPY", - "amount": "6096248" - },{ - "owner": "BTSpR5YJgucxq3oTBe1KhEdpr3bekVnDsVA", - "asset_symbol": "PPY", - "amount": "1682722" - },{ - "owner": "BTSBpcfJQh9ErMSUvXmHMzFudK9jedrpJ7A7", - "asset_symbol": "PPY", - "amount": "1841905" - },{ - "owner": "BTSDHeu8CF5yMNtVv13ViyK8jd51impGeVyc", - "asset_symbol": "PPY", - "amount": "1475222" - },{ - "owner": "BTS5YRAEZ7oT6ZEMovYTM7P7bzGvyE2iZfAi", - "asset_symbol": "PPY", - "amount": "43043756" - },{ - "owner": "BTSJ1UUbFsaUYxBRKBqjHnbF9uTLA8t2upj8", - "asset_symbol": "PPY", - "amount": "3146220" - },{ - "owner": "BTSAb6iJ6CjEtuFMp1F2hZV1ASZReEqm8rx9", - "asset_symbol": "PPY", - "amount": "477664" - },{ - "owner": "BTSGfu3JHZEVh1xNpJ859LkpWWvkKq3uNCSM", - "asset_symbol": "PPY", - "amount": "49984308" - },{ - "owner": "BTSPRMWk3zzq6bzqbAaQJRrTuDnGycNaMtYB", - "asset_symbol": "PPY", - "amount": "3388967" - },{ - "owner": "BTSL6za2Gz9YEn7yHbFEohZrXCZ76pZGpUA1", - "asset_symbol": "PPY", - "amount": "6768782" - },{ - "owner": "BTSHPaY1Nd26E84eJbxu37ET7MvHPdiUjdBH", - "asset_symbol": "PPY", - "amount": "4745391" - },{ - "owner": "BTSMcjuozavQXDnp5Qoggxwoy8WYaYrhB925", - "asset_symbol": "PPY", - "amount": "103749504" - },{ - "owner": "BTSA4dgXo8sN4EiP5tTDB77wqqebdYx3RTLL", - "asset_symbol": "PPY", - "amount": "6801800" - },{ - "owner": "BTSMRJ3CKLMsVA8aYmT5EvRMyWjMYEiwHCnP", - "asset_symbol": "PPY", - "amount": "24667880" - },{ - "owner": "BTS3nk3q8mDBbu74h35hqapbF91bwpAAZWJe", - "asset_symbol": "PPY", - "amount": "19307514" - },{ - "owner": "BTStDQLFUAziQktFH7Juxo5erbmQ2kaS3ZV", - "asset_symbol": "PPY", - "amount": "470190" - },{ - "owner": "BTS4Ka5VtNRr9cEfD9XxnFgt2Nn3toZhn5Xm", - "asset_symbol": "PPY", - "amount": "148284857" - },{ - "owner": "BTS49crmb1S38eyQ6ZbCuEtaCJXANGX1GWbo", - "asset_symbol": "PPY", - "amount": "070498" - },{ - "owner": "BTSA6hwJJh5osCsVpU11F5xqEdBzZVjHBbxD", - "asset_symbol": "PPY", - "amount": "4059374" - },{ - "owner": "BTSZ3TSdygHDE56GBt4fy6QxPmkavq7wRLG", - "asset_symbol": "PPY", - "amount": "7454988" - },{ - "owner": "BTS6ZWfvFkiCgoGybAeGvTEBdZetgHCZrd42", - "asset_symbol": "PPY", - "amount": "055485" - },{ - "owner": "BTSMpnfzqsFHsi19dfwkNJeQmfjARmYCuyJr", - "asset_symbol": "PPY", - "amount": "34046895" - },{ - "owner": "BTSKT2pM2DJu64kS9PZGTRvqqmv3SwLQqJfs", - "asset_symbol": "PPY", - "amount": "1850166" - },{ - "owner": "BTSQDCo7vmvb8X4tetQLbU7CyYNhjRt6hv9a", - "asset_symbol": "PPY", - "amount": "423361" - },{ - "owner": "BTSHynzSiPWcH38asGpD4CMLqPGMfwskJKhk", - "asset_symbol": "PPY", - "amount": "3459118" - },{ - "owner": "BTSNpJ8WcTs8FV5uTJvgtNDeduZ4hLdd6JQM", - "asset_symbol": "PPY", - "amount": "5143859" - },{ - "owner": "BTS8qfqci5qim6LEf2pXjWB5AfPVRLuybP9b", - "asset_symbol": "PPY", - "amount": "11512646" - },{ - "owner": "BTS7YLJSmVgcxE9GQXcJbmmfKUaNqaqaYV9o", - "asset_symbol": "PPY", - "amount": "102872340" - },{ - "owner": "BTS9CUzt31koPGZAGuffRD4kHTgK8sjdTQ7q", - "asset_symbol": "PPY", - "amount": "578012" - },{ - "owner": "BTSQ1vK6X2oSAD78iWZAgn81r17ZaYbg9W4b", - "asset_symbol": "PPY", - "amount": "35286556" - },{ - "owner": "BTSKx7HHXNV6JXHpsRiR3vRkbGkaXNjRGugV", - "asset_symbol": "PPY", - "amount": "344753" - },{ - "owner": "BTSCn4Jcf6XbvVPHz3CtnMAQMBzshsoRHx2X", - "asset_symbol": "PPY", - "amount": "303042" - },{ - "owner": "BTSHvuALsxqih9sJrRagtPVKz5H9BLkgyuLn", - "asset_symbol": "PPY", - "amount": "2565070" - },{ - "owner": "BTSD7ed9eXqHw5KvkVEa7u8n12y5ktswiiu", - "asset_symbol": "PPY", - "amount": "5108381" - },{ - "owner": "BTSNTrL6p8d9o69QgJQwvNCDM8XrTKNtpyiF", - "asset_symbol": "PPY", - "amount": "3401105" - },{ - "owner": "BTSCDiFFAphts9HZoHr19bKJKjc2L1dciNZG", - "asset_symbol": "PPY", - "amount": "9685022" - },{ - "owner": "BTS3XK2Cj7WGhnCGKdJb1dqfWhtL7ZC5zaRH", - "asset_symbol": "PPY", - "amount": "5365990" - },{ - "owner": "BTSBqxGHyA27LV4XgE5md5BCigVxnQtw6mvY", - "asset_symbol": "PPY", - "amount": "453500" - },{ - "owner": "BTSCFSxDNoxC7mYgZmtJwnA6JSAgA3xWpuSh", - "asset_symbol": "PPY", - "amount": "11600207" - },{ - "owner": "BTS4XMgxF6brnNxK9QEte7b4VfhyB62YEtSm", - "asset_symbol": "PPY", - "amount": "1500884" - },{ - "owner": "BTSMsEkhAgSCqoaSQR45kXHabKZ4inY7UnAT", - "asset_symbol": "PPY", - "amount": "7665104" - },{ - "owner": "BTS6DU7fxx84M4hzo5Ysd4NpFGMfMofYT7Sb", - "asset_symbol": "PPY", - "amount": "910159" - },{ - "owner": "BTS6oUj4CgbSz1cHBfWeg3Ws6BYoRpZD2UYX", - "asset_symbol": "PPY", - "amount": "21468294" - },{ - "owner": "BTSD1zLvmfBncCRDvx1fqerpuAVyK8VEFCh1", - "asset_symbol": "PPY", - "amount": "5567599" - },{ - "owner": "BTSDNmdK8RbBwwKT9Y99xJqBaiLGxx8f9ph5", - "asset_symbol": "PPY", - "amount": "7083264" - },{ - "owner": "BTSDLn35c9fvQjtAspZR7CEdKrQs4mGgPHds", - "asset_symbol": "PPY", - "amount": "2844147" - },{ - "owner": "BTS2i5SyuK6KmLaLysWSMMLnSXDZ1v6bW6BC", - "asset_symbol": "PPY", - "amount": "105049524" - },{ - "owner": "BTS3g2T9BMDhjsSu1TzGDu1CHziWPL6UKXzm", - "asset_symbol": "PPY", - "amount": "57507336" - },{ - "owner": "BTSDVuse8pTcDezJponRsy5rnf1p95Q7vdCF", - "asset_symbol": "PPY", - "amount": "65126922" - },{ - "owner": "BTS6osmN17d6KTGUraMfr87h6HWKYadL8hp2", - "asset_symbol": "PPY", - "amount": "117940856" - },{ - "owner": "BTS9oXK4SDhWyT2uQ7NaKnvsNBxFZV5EByAa", - "asset_symbol": "PPY", - "amount": "20698873" - },{ - "owner": "BTS3Lf1Ynq7rDG5zS2PVv9xFXy2Pb9WFteMP", - "asset_symbol": "PPY", - "amount": "4346497" - },{ - "owner": "BTSG3mKGxrC88UHh3uezoJpCEmSPw3REMZGu", - "asset_symbol": "PPY", - "amount": "4577055" - },{ - "owner": "BTSMxx8mMLuCUveG3zpWdfr84pzqKCYuFCNk", - "asset_symbol": "PPY", - "amount": "34072021" - },{ - "owner": "BTS4KUN9ybWWodrJrLceaGDYeJbvLURB3zB1", - "asset_symbol": "PPY", - "amount": "381469" - },{ - "owner": "BTSKiXrDCdKMd5LNB6h8L23KxNVdJMFRb5up", - "asset_symbol": "PPY", - "amount": "521204" - },{ - "owner": "BTSJ4y6wK1oXNgJYMDdkJSDeJApPL8ychbwz", - "asset_symbol": "PPY", - "amount": "5646311" - },{ - "owner": "BTSQB8Dz8qhw4fkJCBNZvPtzV5yHsJ2Xh8ZB", - "asset_symbol": "PPY", - "amount": "644002" - },{ - "owner": "BTS4PHURF12xTNACFQq3dakwAEDQaTVDDH1w", - "asset_symbol": "PPY", - "amount": "34127467" - },{ - "owner": "BTSDU1Lop3S72iB2Y2nqZ1yYCi7cWWQn1aA", - "asset_symbol": "PPY", - "amount": "065215" - },{ - "owner": "BTSP3tKhFEfcxMRQA1qS38b2MNSm5vdVPeLX", - "asset_symbol": "PPY", - "amount": "14039874" - },{ - "owner": "BTS23NrKGG15SX7AqD8hEp75r88RPktxx3CR", - "asset_symbol": "PPY", - "amount": "16571074" - },{ - "owner": "BTSAHCgW7Crd4JDbNziryPommzPEaPoNnYxs", - "asset_symbol": "PPY", - "amount": "197908" - },{ - "owner": "BTSEpYrk1LtxqU2DCZBALwdbuC2qxo8Wn4bv", - "asset_symbol": "PPY", - "amount": "893254" - },{ - "owner": "BTSCeR6K1M8oZNVGxpVhUgxKWe5LARBWsrYE", - "asset_symbol": "PPY", - "amount": "627566" - },{ - "owner": "BTS91FAXruWNoekpjTbYvB9r4QmxuAWYZqKt", - "asset_symbol": "PPY", - "amount": "870000" - },{ - "owner": "BTSMXzpp6kJFf7LEVSYmBiEHS5u1eXkETvcw", - "asset_symbol": "PPY", - "amount": "3973114" - },{ - "owner": "BTSE4RNSNQvq1mzXohRVPsDeMrAGYkncfKxx", - "asset_symbol": "PPY", - "amount": "6317680" - },{ - "owner": "BTS74LvVTaZD1h7VMcsfbFa2KVLxHDzd46z9", - "asset_symbol": "PPY", - "amount": "1620000" - },{ - "owner": "BTSNsyPnkVhxeiYG5YmZwbqc9T9ifL6CXXst", - "asset_symbol": "PPY", - "amount": "4071132" - },{ - "owner": "BTS4b37Nu4gqTxgH2CkHwtGMy1B3f2YhZ325", - "asset_symbol": "PPY", - "amount": "2047593" - },{ - "owner": "BTSD3JFMyXAmSaNNSB6KVCJvsvACVrs7PvZg", - "asset_symbol": "PPY", - "amount": "1739682" - },{ - "owner": "BTSLbrhAewba5R4LfjbBuvhkkmArhUmj764i", - "asset_symbol": "PPY", - "amount": "1659505" - },{ - "owner": "BTSH2qg1FuZRhxvB2R2Z4SruExmWr24efcho", - "asset_symbol": "PPY", - "amount": "10994834" - },{ - "owner": "BTSENJuRAYmziJAygfYaFGXrBHU7njEERWxa", - "asset_symbol": "PPY", - "amount": "35045618" - },{ - "owner": "BTSBasLXkQbfbGDDcYN9ue6tCy5jGkKNcF35", - "asset_symbol": "PPY", - "amount": "247784" - },{ - "owner": "BTSPMiu1GT7AtuGszczDtEcbfpNWXmhd6b2k", - "asset_symbol": "PPY", - "amount": "6740003" - },{ - "owner": "BTS4oz8FkcXXtESTMR5BZSuJsG6vrA78aW3R", - "asset_symbol": "PPY", - "amount": "17130845" - },{ - "owner": "BTSLjuowrG1Ty1RgoKiJbhDtMkuBSF4RVqLN", - "asset_symbol": "PPY", - "amount": "5135208" - },{ - "owner": "BTSGsj7nGxzN5WCJuiDCVpRZLJYKvZTSpth3", - "asset_symbol": "PPY", - "amount": "1586208" - },{ - "owner": "BTSHWUyS8KJ7siUPDTCJJYyPkSacETkiix4", - "asset_symbol": "PPY", - "amount": "5041707" - },{ - "owner": "BTS9BLL8GAG6v8LwHzKGhbpBKsMiCgGvwaCR", - "asset_symbol": "PPY", - "amount": "1576086" - },{ - "owner": "BTS83FVETTSMsf8L8hDr4rn1DuBeBmoxMuYH", - "asset_symbol": "PPY", - "amount": "3624565" - },{ - "owner": "BTSAX62wGDT5pDJUwd9eRmJAg4eM27YVf7Xm", - "asset_symbol": "PPY", - "amount": "4742395" - },{ - "owner": "BTS8rMwEdp7QtREtpfShhqb9oLEEsvp3TKCU", - "asset_symbol": "PPY", - "amount": "1621036" - },{ - "owner": "BTSN9fmYABBKo3XBG2PND2m69VdK4URMAWGK", - "asset_symbol": "PPY", - "amount": "13577346" - },{ - "owner": "BTSJv98n9L6Kg9V6MNeFCMEgZURkma1DACzx", - "asset_symbol": "PPY", - "amount": "73160377" - },{ - "owner": "BTSAbDRjUkQh16PdaBmagYA65xVysjskkbsD", - "asset_symbol": "PPY", - "amount": "16996821" - },{ - "owner": "BTSMAbY2bysd5oyWBa8fJ1c6BW4RgDbZQ4Vx", - "asset_symbol": "PPY", - "amount": "3407872" - },{ - "owner": "BTS15oQNBZbAyd1NmqtcPfqx4YCW1xteMd6Q", - "asset_symbol": "PPY", - "amount": "3398283" - },{ - "owner": "BTSPzapdyxmLvsX5HZAwy6EuxCyDbm98EVgj", - "asset_symbol": "PPY", - "amount": "4163874" - },{ - "owner": "BTSbkvUPStuaN3SUQAyiMDYPjKNjk7ei2W8", - "asset_symbol": "PPY", - "amount": "3727230" - },{ - "owner": "BTS3ubuVEgYQWf8TGXrmWfFtMeTHTxVsZQQY", - "asset_symbol": "PPY", - "amount": "3283999" - },{ - "owner": "BTSBiN3RVWhWNs6UGXb3ugYsXHPKJJmyHH2k", - "asset_symbol": "PPY", - "amount": "3395768" - },{ - "owner": "BTSEvAFZ9LnFk21CtkGGhtnsfFPm9MxjQJ7E", - "asset_symbol": "PPY", - "amount": "011900" - },{ - "owner": "BTS6HMEmf5a2TwwWbsgYZY6yDkxBJtupGjQK", - "asset_symbol": "PPY", - "amount": "70212560" - },{ - "owner": "BTS7AKZSTiLvDGtUf8wg6tr5dvP8BadqTanJ", - "asset_symbol": "PPY", - "amount": "19936714" - },{ - "owner": "BTSJ3WoxhkXkbQ8UAmq24rpakG6KS19TTzb6", - "asset_symbol": "PPY", - "amount": "004303" - },{ - "owner": "BTSFf6MVPz36DhP7yDDMnLU1thVRJgSS1FXc", - "asset_symbol": "PPY", - "amount": "9948886" - },{ - "owner": "BTSE9vbHaJXq6tEe2ZFHvQjHDbU4Jyw2NoM1", - "asset_symbol": "PPY", - "amount": "2756793" - },{ - "owner": "BTS3pgMqyNsrMCPsCQMoDnMtQQkNkmXTKMBd", - "asset_symbol": "PPY", - "amount": "6848850" - },{ - "owner": "BTSF3xFAW2cRa63Lsvt1rgNtHcnFYdk6pSZS", - "asset_symbol": "PPY", - "amount": "309316" - },{ - "owner": "BTSJ4FLe1HVbUPeV26GncrV4X2iLi1Sgjcy8", - "asset_symbol": "PPY", - "amount": "005181" - },{ - "owner": "BTS5T54ETVqpymzYbUTyVG74sHyFeDvuW3fA", - "asset_symbol": "PPY", - "amount": "7993518" - },{ - "owner": "BTSQ6p6ybxue8s11M5r2PqXfdTDEY5wTAoF9", - "asset_symbol": "PPY", - "amount": "213716" - },{ - "owner": "BTSH9nu8NVuNbFCsw2Hj1LkgzJdFTp35GGa3", - "asset_symbol": "PPY", - "amount": "20553015" - },{ - "owner": "BTSPxLUELoBEFDffXTcBzsuhtkduSNqVHm5E", - "asset_symbol": "PPY", - "amount": "1458434" - },{ - "owner": "BTS48SLB4Atp8LhcwzRgmQSzk8Qcanhb85NS", - "asset_symbol": "PPY", - "amount": "030627" - },{ - "owner": "BTSL6nzdhgMbDT8xH3i3ZxsoiKLhoShQC39o", - "asset_symbol": "PPY", - "amount": "17927847" - },{ - "owner": "BTSCtPd1N1EBQNE9vihKbEAVTnRBVUap6527", - "asset_symbol": "PPY", - "amount": "7151964" - },{ - "owner": "BTSNLiGq6qbfJG7HTGd1BCALahsPsU2fmXUJ", - "asset_symbol": "PPY", - "amount": "2784583" - },{ - "owner": "BTSCj9hohdbhjiAWFgNZgP4gnzMKDdPJbsdd", - "asset_symbol": "PPY", - "amount": "1501342" - },{ - "owner": "BTSJF6xKJfUbZtxDm94P6ruGJFBSYn6Ez2U7", - "asset_symbol": "PPY", - "amount": "841461" - },{ - "owner": "BTSGc3o2YFue9i9KDh7GbEcfodc6y1jhmfzx", - "asset_symbol": "PPY", - "amount": "14081223" - },{ - "owner": "BTSLbbyF7gsrieqUSt1az9cUXDvj8x1X1zJg", - "asset_symbol": "PPY", - "amount": "344377" - },{ - "owner": "BTSgMvB5W7kbjaihu6vdxhvyc2unh3U1bw7", - "asset_symbol": "PPY", - "amount": "157532" - },{ - "owner": "BTS5849vMiJisJaP8GUAjs3VCvZctiiCn1FT", - "asset_symbol": "PPY", - "amount": "3469641" - },{ - "owner": "BTSKv3Ff92jfZ3dYsUqe8hqaVByBgGXryka7", - "asset_symbol": "PPY", - "amount": "1837229" - },{ - "owner": "BTS12S3LVBfmUPXiNccjwCi2xuv5V49JurUv", - "asset_symbol": "PPY", - "amount": "9063499" - },{ - "owner": "BTSLgB8qVBScQrtBYadiRFRoMSAo4eqGwb8b", - "asset_symbol": "PPY", - "amount": "10867715" - },{ - "owner": "BTS12NVx9E59btKPTu3RvMXdoQGZV3qsScy7", - "asset_symbol": "PPY", - "amount": "9964333" - },{ - "owner": "BTS7TLhtVtVPGzwrv6JbMHi1FB9HhaCdseN1", - "asset_symbol": "PPY", - "amount": "968254" - },{ - "owner": "BTS8n2eYcgaWZbZ9WuzU4a5cCp2hRA9hYH8n", - "asset_symbol": "PPY", - "amount": "882860" - },{ - "owner": "BTSAApzWdaCWoA9zgkL3g2HENMEq2g19NKT7", - "asset_symbol": "PPY", - "amount": "21370796" - },{ - "owner": "BTS32ak785fnK8ERihqZqFQ1Nw8CDXXU5DCW", - "asset_symbol": "PPY", - "amount": "3552501" - },{ - "owner": "BTS13MyG4vennD33L895XgQWEYH7La8vAajM", - "asset_symbol": "PPY", - "amount": "69790229" - },{ - "owner": "BTSERSXwCLhpoHzXBZpthiBr46KkEaCojNgo", - "asset_symbol": "PPY", - "amount": "5039215" - },{ - "owner": "BTSL9Fn4NzJCTsaMrUbtcMkbvKp7UUnbtd6s", - "asset_symbol": "PPY", - "amount": "2794527" - },{ - "owner": "BTS7zrpFLPjz2tgrsnTQHRYcyokfqyWU41Mw", - "asset_symbol": "PPY", - "amount": "404319" - },{ - "owner": "BTSHNbbWxLkqPTxzCjztrSgxduDqcgXPaWFc", - "asset_symbol": "PPY", - "amount": "2141561" - },{ - "owner": "BTSAjPfj6v32bmc7rQDhbQs1TYCDmmPwPZcr", - "asset_symbol": "PPY", - "amount": "8706910" - },{ - "owner": "BTSPysddpUsvaB2dCfzv3EkU85F7hUHueYqx", - "asset_symbol": "PPY", - "amount": "102150933" - },{ - "owner": "BTS2pTB8D2f57kopfSBpoA7nyvVJNhwUiQGJ", - "asset_symbol": "PPY", - "amount": "413399" - },{ - "owner": "BTSJQtf9cgAjXsTwoVZtBEnUGJ3k15rUymwv", - "asset_symbol": "PPY", - "amount": "10069400" - },{ - "owner": "BTSHzmAtG2GgwYb677zvzGdYB2cUQ6W91Jko", - "asset_symbol": "PPY", - "amount": "854388" - },{ - "owner": "BTSMPNjtzsvEcUmwcEaw3Fcoi7eGcWW2ua6A", - "asset_symbol": "PPY", - "amount": "1067358" - },{ - "owner": "BTS6UqjCTXg9z5pGhZMAYue9nVzhBtPxL29g", - "asset_symbol": "PPY", - "amount": "23833650" - },{ - "owner": "BTS3CcDGcDRf45jYGSVAXYYeMs5ewqgTsvyW", - "asset_symbol": "PPY", - "amount": "8356614" - },{ - "owner": "BTS4m9Am1xbynKKwfKfRPLT2juJSCuMhT1rC", - "asset_symbol": "PPY", - "amount": "66916730" - },{ - "owner": "BTSGe3mkYbF81gt7ywEGoP2RHwjRvf6iDBq1", - "asset_symbol": "PPY", - "amount": "32608309" - },{ - "owner": "BTSHKLCWNeYrYd4u8SH3ev3rTmXL5E6DPk5d", - "asset_symbol": "PPY", - "amount": "11824406" - },{ - "owner": "BTSEjKFVKujDABKHjS3uzKwvi41Y91wgeiuT", - "asset_symbol": "PPY", - "amount": "10478910" - },{ - "owner": "BTSFbSh4xRRv4bGmG28vQ4YPo85obTnvESrD", - "asset_symbol": "PPY", - "amount": "23867958" - },{ - "owner": "BTSMDxJteY4Gc5Psky4bSDTrJUxjfATHfLfm", - "asset_symbol": "PPY", - "amount": "1254185" - },{ - "owner": "BTS223tF8MmmbZ76gTxzAWEgvRKxTrpcNpGa", - "asset_symbol": "PPY", - "amount": "6633280" - },{ - "owner": "BTS3G8Rbu7Hhcf51B6m6asEidTYSPxRUyKnd", - "asset_symbol": "PPY", - "amount": "343236" - },{ - "owner": "BTSK1EyrzLRE7nAJHc3prdeTeA4Q1GPeeAqy", - "asset_symbol": "PPY", - "amount": "9926599" - },{ - "owner": "BTSPKyj8oe4ZpxieiLfkw4RNXs68QGPYvLcj", - "asset_symbol": "PPY", - "amount": "1818561" - },{ - "owner": "BTSN33EbhWc1VkmY4Y4CoYdCZixC9ZX6uF73", - "asset_symbol": "PPY", - "amount": "215025" - },{ - "owner": "BTSJFHrjhWrdyQhj8a8D2GeoXtmUsX39JhpH", - "asset_symbol": "PPY", - "amount": "13763083" - },{ - "owner": "BTS2QwzsjLycuP6aM8DKLR8vtbVGLFi2ikbs", - "asset_symbol": "PPY", - "amount": "1332304" - },{ - "owner": "BTSGDSxr9engtJw47JVyBHCWPvvawvyvvxPZ", - "asset_symbol": "PPY", - "amount": "64821051" - },{ - "owner": "BTS3cWuXWtCEB9HxdgcoUHNFwReP8N7GUDYQ", - "asset_symbol": "PPY", - "amount": "19091877" - },{ - "owner": "BTS9TH2oBRkjMofLJFJsv4wBmonY4Jugv9WA", - "asset_symbol": "PPY", - "amount": "12279411" - },{ - "owner": "BTS4zFJfFuRKHLc2srNx94GijCzci6FqaL1F", - "asset_symbol": "PPY", - "amount": "289532" - },{ - "owner": "BTSJpb5WsMwuSxx5u97BHPFcbtZzSQo7JoGu", - "asset_symbol": "PPY", - "amount": "13662246" - },{ - "owner": "BTSLjCbz3JjzjYTEhd8HMFJRSjZVLzRgpJR5", - "asset_symbol": "PPY", - "amount": "1268121" - },{ - "owner": "BTSD5RQE9YUeyQ33trGdEkdLe8gT735zW6je", - "asset_symbol": "PPY", - "amount": "7930716" - },{ - "owner": "BTSGbAiC9qUiYQ3pAyB9i2NfB4Na7t2sdjvn", - "asset_symbol": "PPY", - "amount": "1200367" - },{ - "owner": "BTSNwBy1TMHLUWoqNm5Wm8zWnkZmTU6wBpWN", - "asset_symbol": "PPY", - "amount": "5024837" - },{ - "owner": "BTSNhdGhLioFmYHjQuJ52HaQSzsBd4oaj6X3", - "asset_symbol": "PPY", - "amount": "527447" - },{ - "owner": "BTS89nveUMMKPErZhoWsJaZVVz25tjP6ubC3", - "asset_symbol": "PPY", - "amount": "859599" - },{ - "owner": "BTSCGWdfAFHVjV7LcYnWU3vitwuDYxri19mh", - "asset_symbol": "PPY", - "amount": "5142805" - },{ - "owner": "BTSFtzvmQjC4Jf98fLy2nzAeLiLLQi3ExYkR", - "asset_symbol": "PPY", - "amount": "1029568" - },{ - "owner": "BTSqYekwD69X9YLCrdoFArLX5jwWFswudsR", - "asset_symbol": "PPY", - "amount": "32380000" - },{ - "owner": "BTS6A96N9euEpdF8sHjzxE4avzGasSqk5SYX", - "asset_symbol": "PPY", - "amount": "067914" - },{ - "owner": "BTSNw3xZr1en2g1wrQtDaLi2HwkV4nR3UvKs", - "asset_symbol": "PPY", - "amount": "2171377" - },{ - "owner": "BTSKQH3jWa7vrM3eAMVUE6dR62Spj3RVAS1S", - "asset_symbol": "PPY", - "amount": "379887" - },{ - "owner": "BTSLYK1tZssE4bLWmwxPDxqVG52fdXAobh2f", - "asset_symbol": "PPY", - "amount": "1909984" - },{ - "owner": "BTSHz69iwQFQGp3ARNCL4dFfYCKUySRquze1", - "asset_symbol": "PPY", - "amount": "17465686" - },{ - "owner": "BTS41De1FSdNvQWg9mNxF15Kjm1Jhhsh4BL1", - "asset_symbol": "PPY", - "amount": "693907" - },{ - "owner": "BTSJDFBHNetfEnoBukRdVz7y3T27Qc6YC4LU", - "asset_symbol": "PPY", - "amount": "554071" - },{ - "owner": "BTSM5ggUx5cAUjRSPVgBF7LsAU4Dn1uWWjoJ", - "asset_symbol": "PPY", - "amount": "2817661" - },{ - "owner": "BTSLPLJEG3xyutw4D8tHu7UTfF9bqBvEPLcH", - "asset_symbol": "PPY", - "amount": "9743391" - },{ - "owner": "BTS2n5ChXWVFuj5xMFJt5UNxSR2VHncWHYM6", - "asset_symbol": "PPY", - "amount": "1739147" - },{ - "owner": "BTSA7hsvhQ9XG8rxyvUK5zZqEhkRfHDSoLXL", - "asset_symbol": "PPY", - "amount": "3926026" - },{ - "owner": "BTSGL6xBfJcg3B8o27Ya4G1r2iyuPqA9xATG", - "asset_symbol": "PPY", - "amount": "102546" - },{ - "owner": "BTSiKJC1Vv58CGkAVpi4t4yuRkS9qfUqvAQ", - "asset_symbol": "PPY", - "amount": "631992" - },{ - "owner": "BTSJFNvfz1BJ4NPNmgw6j7uzgfLkXEft9poM", - "asset_symbol": "PPY", - "amount": "4167789" - },{ - "owner": "BTSDocgFHyQakLUwmJtMU9krQNVnVtm4knjY", - "asset_symbol": "PPY", - "amount": "39007768" - },{ - "owner": "BTSB3JsfaKA5V2Zvz2sbA3MQVJdpKc8aHGsq", - "asset_symbol": "PPY", - "amount": "5165658" - },{ - "owner": "BTS2gRgkK3B1bqWxUCgem4Lrof8LyiG6YVWa", - "asset_symbol": "PPY", - "amount": "34546286" - },{ - "owner": "BTS6TehCU1KHsPPPt1WiurjSD9LP7VtnXHme", - "asset_symbol": "PPY", - "amount": "1364777" - },{ - "owner": "BTS9PfD2PNwoC6HsMhGTWfSqzxNcWMA2ntCw", - "asset_symbol": "PPY", - "amount": "1020478" - },{ - "owner": "BTSiLcBaEwEg21xN3ujWgJFpqVCZAuCUSkt", - "asset_symbol": "PPY", - "amount": "62723040" - },{ - "owner": "BTSLVAninKppP9wBwf6z968Eh4uKtr4NR4H3", - "asset_symbol": "PPY", - "amount": "3133655" - },{ - "owner": "BTSAMARmP3SMfKM4LFykG4qoBdtw6RLXV6b1", - "asset_symbol": "PPY", - "amount": "202165" - },{ - "owner": "BTSDfV7c2jzTTGo6vSTxLsdbGnGsu7QRX41J", - "asset_symbol": "PPY", - "amount": "11381781" - },{ - "owner": "BTS432X3X97DaLs2J1QX3RW1z6ju9am9cXgU", - "asset_symbol": "PPY", - "amount": "96426768" - },{ - "owner": "BTS8anDm3P8smHAXusV5LVLFim4HSj26Ew6k", - "asset_symbol": "PPY", - "amount": "913364" - },{ - "owner": "BTSJ6RuCgF4nZ4fH6PPrEcdbkXo2a1oJnTfU", - "asset_symbol": "PPY", - "amount": "3440667" - },{ - "owner": "BTS7ahDwskgrMzNkGae7whR1H2ZaFn4dB4bt", - "asset_symbol": "PPY", - "amount": "11563662" - },{ - "owner": "BTSJvN8RCcwTymJtKdyUV1XkE2pwVEbcCXSg", - "asset_symbol": "PPY", - "amount": "20739342" - },{ - "owner": "BTSLeoyAd3TPapsH3Xw7NKGo6A6ax78vFzou", - "asset_symbol": "PPY", - "amount": "2719881" - },{ - "owner": "BTSLcJR6ZHSELpLLsQotUfniWus3JRK9Fk1w", - "asset_symbol": "PPY", - "amount": "35292457" - },{ - "owner": "BTS8RZ74Z7KAin3u81Lw8UYHPL2pnBGZc1vW", - "asset_symbol": "PPY", - "amount": "12759613" - },{ - "owner": "BTSFFSFdp4HU3zMokuEZzP7UTsLEeJunrDgD", - "asset_symbol": "PPY", - "amount": "81178523" - },{ - "owner": "BTSNxpmaMYreTvzeH5ihtbDvyZV8xJ5N87ig", - "asset_symbol": "PPY", - "amount": "369179" - },{ - "owner": "BTSJhdWsueD4BwREAJ4SYjPxukYJNoeHr5x6", - "asset_symbol": "PPY", - "amount": "836795" - },{ - "owner": "BTS2M6izAQAtTAephxY2uA4Dp6zSyrHoLsVR", - "asset_symbol": "PPY", - "amount": "510495" - },{ - "owner": "BTSKvky7oZv4Z1ewjiMse8ws9VNiAxx3xnQo", - "asset_symbol": "PPY", - "amount": "477888" - },{ - "owner": "BTS4FEiiSSPLypznCG7jL615cPR8tt9ee6tF", - "asset_symbol": "PPY", - "amount": "1499281" - },{ - "owner": "BTS5kiQM6QjYVRa4aB5ESegYLEjYt8aos2wu", - "asset_symbol": "PPY", - "amount": "4440608" - },{ - "owner": "BTSEuAXdfuhSYPwu2VEvnE4LvQyCSzZTUoYT", - "asset_symbol": "PPY", - "amount": "380855" - },{ - "owner": "BTS63H1psHYRgqrpJGzjU57WgUyNyR4QxMsi", - "asset_symbol": "PPY", - "amount": "10085499" - },{ - "owner": "BTSM8BMKsFcBMkPgN4eZn7Qbsrs4NXVd4azB", - "asset_symbol": "PPY", - "amount": "6034311" - },{ - "owner": "BTSCh9jkPgdZU3nMJFbj1pz4sTc8UCDs2tx2", - "asset_symbol": "PPY", - "amount": "4005127" - },{ - "owner": "BTSL96jWRGeuveN91zERY3RJW66bHUsdpxoo", - "asset_symbol": "PPY", - "amount": "552106" - },{ - "owner": "BTSFx3vaGYx8ZusWa9iveVEgg7MazAm9g595", - "asset_symbol": "PPY", - "amount": "8037383" - },{ - "owner": "BTSKBghGKMnxBohchuFiyC3iZWxx5Z4tySeu", - "asset_symbol": "PPY", - "amount": "264243445" - },{ - "owner": "BTSGk1SpHG85edXkXQW7McpztzfpsFTdw1q6", - "asset_symbol": "PPY", - "amount": "1057227" - },{ - "owner": "BTS3HeecfHa1vD55GYLuRhGW2uXoGypETupr", - "asset_symbol": "PPY", - "amount": "2075822" - },{ - "owner": "BTS93ZLWjfnvZ9ciP9ZqtkWqiororNvedhWV", - "asset_symbol": "PPY", - "amount": "55716952" - },{ - "owner": "BTS6xJ855eG8pTXWSgV9GnTbYE4KBTLForGo", - "asset_symbol": "PPY", - "amount": "125656759" - },{ - "owner": "BTS9mALG8ZFWjFnHAJCak3yhwV2zm7xZCuUN", - "asset_symbol": "PPY", - "amount": "4804790" - },{ - "owner": "BTS37dbQDi7JPC1wywrafebXAEJpjTni2zG8", - "asset_symbol": "PPY", - "amount": "2192651" - },{ - "owner": "BTS2Sd6v5BfHE91J5aLca9MXAMRnpkDHsiER", - "asset_symbol": "PPY", - "amount": "3300762" - },{ - "owner": "BTS8ufyghbjrRDY7dPtn1Wo7aNjAu5bAwRTm", - "asset_symbol": "PPY", - "amount": "374770" - },{ - "owner": "BTS4BmRuPiaTtQUNwjWAYVETW7FnVyjxCXt6", - "asset_symbol": "PPY", - "amount": "94080626" - },{ - "owner": "BTSMCHS4gwDgTNgR5nHa4AnmcaNUBzq7spQM", - "asset_symbol": "PPY", - "amount": "1289663" - },{ - "owner": "BTSERjaTQGgwwsLtgzwPxpypnHEJX56XrPEB", - "asset_symbol": "PPY", - "amount": "34782933" - },{ - "owner": "BTSJ9McAswZgWuA65QcoqC9ofFWhbK6muPQu", - "asset_symbol": "PPY", - "amount": "16091457" - },{ - "owner": "BTSQ4EjAGSJniQmuY2tgzKqCa514Xc4vMRm3", - "asset_symbol": "PPY", - "amount": "49379466" - },{ - "owner": "BTS6w6nGQeCNZ3Wbr6F4KpXZBU3a8H65oyuY", - "asset_symbol": "PPY", - "amount": "9621173" - },{ - "owner": "BTSQ9731ejGXfgGCD5LXi8dKcTJXdN7TwZnP", - "asset_symbol": "PPY", - "amount": "179278467" - },{ - "owner": "BTS2xniLHVbiBGNTb48paERu229xGTQdXL4G", - "asset_symbol": "PPY", - "amount": "4024200" - },{ - "owner": "BTSJCreoqc7UF7kMhV8z5X3JmRtvWhMoPy1w", - "asset_symbol": "PPY", - "amount": "8449847" - },{ - "owner": "BTSQ87UQm81qwAmuNaEL4sRFmDanMfLNUFQj", - "asset_symbol": "PPY", - "amount": "8305162" - },{ - "owner": "BTS6UaSijMWfKeqmcFj9HTYkmkgxcoaBqQHx", - "asset_symbol": "PPY", - "amount": "151936364" - },{ - "owner": "BTSPq5gtatgNYUwy5k5y7M9F8TaLejW7dzfg", - "asset_symbol": "PPY", - "amount": "122060821" - },{ - "owner": "BTSAbXwKxeEjTpYxWXGDoHxT5PiWAD1VXtX1", - "asset_symbol": "PPY", - "amount": "46933801" - },{ - "owner": "BTS57A1gKmR7bGo9SAk2Z1xcPrkZYkHUeWSe", - "asset_symbol": "PPY", - "amount": "9746041" - },{ - "owner": "BTSA78vMSoZ1485vrPMhgHVuQPx6XKg6Ccnp", - "asset_symbol": "PPY", - "amount": "1256198" - },{ - "owner": "BTS5RbDrJD9E4oYYFsXss7XsioRndDVh4vhQ", - "asset_symbol": "PPY", - "amount": "18865263" - },{ - "owner": "BTSGc5BbgCTqSTtiqbUHuoouYgUcrCZezoRV", - "asset_symbol": "PPY", - "amount": "5213174" - },{ - "owner": "BTS4k5h7JqHcjDLNjCAd75v2NZq9ZxtYzuK8", - "asset_symbol": "PPY", - "amount": "381086708" - },{ - "owner": "BTSERsouVv6JDTstb4NZDBMCa9CGVcxkmVv", - "asset_symbol": "PPY", - "amount": "6465287" - },{ - "owner": "BTS31JZBRUmwac2sDL68BmA2RMPnyzDoVGVS", - "asset_symbol": "PPY", - "amount": "1590240" - },{ - "owner": "BTSDmriBMGcecc7S78frupvN7GtWWjgNSCvR", - "asset_symbol": "PPY", - "amount": "7349442" - },{ - "owner": "BTSGtAJXYtnD4nz7fEc82rBiTWvCrhU1VboG", - "asset_symbol": "PPY", - "amount": "675424" - },{ - "owner": "BTSKa5LzEj55HnkqF3P3XtN95hQkLaLdYctQ", - "asset_symbol": "PPY", - "amount": "6513695" - },{ - "owner": "BTS7PobQTrSkyPYdpQnNsd4rVhT5ys7nysee", - "asset_symbol": "PPY", - "amount": "325886444" - },{ - "owner": "BTS67W6ziQvzcH33zTjgRMwHBWfsWoZtwiNK", - "asset_symbol": "PPY", - "amount": "16469477" - },{ - "owner": "BTS6vhwTqygeod3rHFa2gasJA4Es5gsuA55a", - "asset_symbol": "PPY", - "amount": "248253018" - },{ - "owner": "BTSDVTg13L3zKPbpvg31qFqokxvHC7M1cyRA", - "asset_symbol": "PPY", - "amount": "101664838" - },{ - "owner": "BTSAhsQWGiUUsmiiwUhYsih1TKBKKJ2B5oUW", - "asset_symbol": "PPY", - "amount": "6573347" - },{ - "owner": "BTSBx8t9foQxorzXiLoj3sGxCKW2zgj8zPSy", - "asset_symbol": "PPY", - "amount": "840454" - },{ - "owner": "BTS5DX3pTL62oWJZoB3zLc4D6WnNptuMEZdY", - "asset_symbol": "PPY", - "amount": "19447281" - },{ - "owner": "BTSKnkwox6cwJdHaDk4GVUcQjrHYy4EuSsGc", - "asset_symbol": "PPY", - "amount": "17224049" - },{ - "owner": "BTSPbMFd4gJn9FCpwUH9mn6WFDyv1ZbDq5sD", - "asset_symbol": "PPY", - "amount": "8288969" - },{ - "owner": "BTS9FNbkju4ukLMCFmX9cevUR3BEvBFWFMiL", - "asset_symbol": "PPY", - "amount": "29847388" - },{ - "owner": "BTSccWYGgxp9z8V4nLNA2Zuow3E8C6oguQt", - "asset_symbol": "PPY", - "amount": "6085751" - },{ - "owner": "BTSHxphmXgikudnpyEpxTPn1CgV7ND4WK4qC", - "asset_symbol": "PPY", - "amount": "3374266" - },{ - "owner": "BTSECQkH35MYtEEGszVQrA63pYFzmmzXmqHX", - "asset_symbol": "PPY", - "amount": "1973643" - },{ - "owner": "BTSCxpRZiwvmeCNwKKXUjWwS2MA28LWMKRWH", - "asset_symbol": "PPY", - "amount": "964311" - },{ - "owner": "BTS2JFu1VH1X2WwAjBzhbNjMeTjntXcsmbn7", - "asset_symbol": "PPY", - "amount": "138910196" - },{ - "owner": "BTSHdjrYNWS99gCtd4tiY4rKH85RrTP8j8vY", - "asset_symbol": "PPY", - "amount": "3937954" - },{ - "owner": "BTS8HhtvkeqyfhRZJus9fFRk3rwH927uwrgn", - "asset_symbol": "PPY", - "amount": "4427352" - },{ - "owner": "BTSAVBrokm18LNnwzPPb2AKM9dvewGipWSpQ", - "asset_symbol": "PPY", - "amount": "275494787" - },{ - "owner": "BTSPkC5koTkniUetL8V3QrqfjN11skAjQp3t", - "asset_symbol": "PPY", - "amount": "225540710" - },{ - "owner": "BTSHTzGJrhe8WvhGJPSvMfNRLY497fRnsGDb", - "asset_symbol": "PPY", - "amount": "2075786" - },{ - "owner": "BTSDie7D8NLePGKUxKUvGrsP5F3HaQA4vNdK", - "asset_symbol": "PPY", - "amount": "2073952" - },{ - "owner": "BTS8EiscU1bDo15vi6BY9XgYfDWzeXi5Ag2z", - "asset_symbol": "PPY", - "amount": "1259347" - },{ - "owner": "BTSN4CzMTENQy1bat2bWuRcyB4Czu6nVMnHm", - "asset_symbol": "PPY", - "amount": "190078" - },{ - "owner": "BTSQExgsatd5xkYGn8YvR6yrkTsyqX52Zn3Q", - "asset_symbol": "PPY", - "amount": "7589881" - },{ - "owner": "BTSCSsxh8RbfUGVpLwv5yu2uRdUJbFeJGfDM", - "asset_symbol": "PPY", - "amount": "3551983" - },{ - "owner": "BTS3Lr4J29rL2CEUQjT7ghaK1X64LbpLfBkT", - "asset_symbol": "PPY", - "amount": "9277044" - },{ - "owner": "BTSLhN4R5KRPrWFedZLg1UArPchu6zTSXkBP", - "asset_symbol": "PPY", - "amount": "1221050" - },{ - "owner": "BTS3MQkQu5HVJU9fPEatskTpYoktFZQt59Ty", - "asset_symbol": "PPY", - "amount": "10528506" - },{ - "owner": "BTS4L3TLvsHg2awAZCQMMjj89KGWShejtvKJ", - "asset_symbol": "PPY", - "amount": "10969596" - },{ - "owner": "BTSMxfWhDWjtmcMcJ2SLcakBLgCcbNEKbcxn", - "asset_symbol": "PPY", - "amount": "479052" - },{ - "owner": "BTSJbzyRXZTCKcgw9J1WZTzXLaJVmAisoenS", - "asset_symbol": "PPY", - "amount": "2832894" - },{ - "owner": "BTSP4TuKyndiefGc1QZpdATRnNPzGS1DYYWk", - "asset_symbol": "PPY", - "amount": "20265527" - },{ - "owner": "BTSjNKUGsxSExG68fR8nSTtznRSaV7dY9Uv", - "asset_symbol": "PPY", - "amount": "32525708" - },{ - "owner": "BTS54uSkvMN4kHYpJrq5YZE6ABTthzVgZYHE", - "asset_symbol": "PPY", - "amount": "12549489" - },{ - "owner": "BTSDiwHMMu1mJeBikZiJDe3xsiAJ9x1Y1XZK", - "asset_symbol": "PPY", - "amount": "11811324" - },{ - "owner": "BTS85UN9kMsPFHWzhsE2Pprfvf8cCpRg6FMe", - "asset_symbol": "PPY", - "amount": "10398488" - },{ - "owner": "BTS3beFrHJfJ1DLLNJF4y6yFaw8M5AUVDNZr", - "asset_symbol": "PPY", - "amount": "1756149" - },{ - "owner": "BTSDA4EP3FBsmzohajEFNnPpWuzsrNV6BLra", - "asset_symbol": "PPY", - "amount": "27232456" - },{ - "owner": "BTSMbiitaQMRnrGSzUkuAUqxcZHrjXAcxRRS", - "asset_symbol": "PPY", - "amount": "2345579" - },{ - "owner": "BTS7dz9nEKJ9rfyvdpMmhg5a7ALAesNaD9QM", - "asset_symbol": "PPY", - "amount": "1885976" - },{ - "owner": "BTSCRFKLDFfRKKDQwHfaiXbtnSeyqD3Ftgor", - "asset_symbol": "PPY", - "amount": "23639767" - },{ - "owner": "BTSMk43ywSxbxmmCFZaUz1dZVmutZEvCGtP1", - "asset_symbol": "PPY", - "amount": "34979953" - },{ - "owner": "BTSMJuk4ZDRNUykkDdWHEMH8DVyrc3rvQ3JS", - "asset_symbol": "PPY", - "amount": "69492933" - },{ - "owner": "BTS6jrtCCeCfhTKobLfRW3BC9S6P5op5cMAm", - "asset_symbol": "PPY", - "amount": "446847" - },{ - "owner": "BTSNZjPqboaQdwMYKHWWjD4HWXgE81YyqQbs", - "asset_symbol": "PPY", - "amount": "8352381" - },{ - "owner": "BTSEtDfXgxzQjM8JYVUe9dAKLgzGyGR94XQ6", - "asset_symbol": "PPY", - "amount": "11364974" - },{ - "owner": "BTSBStyp58qgjG1pyinGCMKgVVe5YfiKwut8", - "asset_symbol": "PPY", - "amount": "6576859" - },{ - "owner": "BTS3FkAQXygnj653iBpYin1VpimzgMLZ5aZL", - "asset_symbol": "PPY", - "amount": "71292654" - },{ - "owner": "BTSAezr8pkPCNxDY9cQWoXRJZmwqbdbuPd9m", - "asset_symbol": "PPY", - "amount": "29196778" - },{ - "owner": "BTSLyki2Zm4V71JuQiosKvtdtavJeJVJeiDa", - "asset_symbol": "PPY", - "amount": "100505930" - },{ - "owner": "BTSFxyEqDniBpu6JywVxXXNWfk5krWidz7Wx", - "asset_symbol": "PPY", - "amount": "16067210" - },{ - "owner": "BTSHqrnmQ8vELuzyaMChSVfVabgook4fac58", - "asset_symbol": "PPY", - "amount": "399404" - },{ - "owner": "BTSKTYsqGKPcQboasZJwhsVGFGTAbaodcTck", - "asset_symbol": "PPY", - "amount": "74978181" - },{ - "owner": "BTSLCbqVBymQAnkWUdEUtktYP95m3XRGWUYR", - "asset_symbol": "PPY", - "amount": "395668" - },{ - "owner": "BTSFq2Wxi4bhv7dN8MNVJJrfCL4HWbX9x2Vi", - "asset_symbol": "PPY", - "amount": "11705303" - },{ - "owner": "BTSNx6y7e2QjdAe82QtX6JxtQJNCstd4tQSi", - "asset_symbol": "PPY", - "amount": "3576386" - },{ - "owner": "BTSD7NmRM5wShWyuyMhxqaVQsrtimz6bSxyz", - "asset_symbol": "PPY", - "amount": "1930673" - },{ - "owner": "BTSK5vgZW5mYsZHP4eQDyQxbNRiLn296B1Rc", - "asset_symbol": "PPY", - "amount": "1994335" - },{ - "owner": "BTS9DNhXnSQjnQhFBiQTj4S1CvYpC3FyVijM", - "asset_symbol": "PPY", - "amount": "10361422" - },{ - "owner": "BTSDEPksvjqnC8Em3r3MZrhcSjrcTmcz76LU", - "asset_symbol": "PPY", - "amount": "003278" - },{ - "owner": "BTS6bi6WRb1SxHN7x4HyCMqatsWSv9r1EEta", - "asset_symbol": "PPY", - "amount": "921410" - },{ - "owner": "BTS4mtJz7P247FfQkKviqyBtPYTWrbeGxfSu", - "asset_symbol": "PPY", - "amount": "9716205" - },{ - "owner": "BTSBojUP8im3Dee3VmbPYsZtqs6q4zqT2H8n", - "asset_symbol": "PPY", - "amount": "091060" - },{ - "owner": "BTS8CU7M71WxwyEscAFfFcHyLDnbrrKcRtAt", - "asset_symbol": "PPY", - "amount": "14117392" - },{ - "owner": "BTSBsTFNuFb8BXFnJ2yRfReqvsvJ3DjFLnXz", - "asset_symbol": "PPY", - "amount": "2150920" - },{ - "owner": "BTS3Habs3hyuEXK1m7eUv3GcNMRU3y3QSo63", - "asset_symbol": "PPY", - "amount": "214955091" - },{ - "owner": "BTSHzth7cdZFeTE7C6N4mJC4QettX6RzAGLv", - "asset_symbol": "PPY", - "amount": "3199842" - },{ - "owner": "BTSJXDVpqeemyWP5SGAy96sPTjz3asFrokjL", - "asset_symbol": "PPY", - "amount": "499940" - },{ - "owner": "BTS9fbLrNUtabbsKjrRFZZxqfbC7GT3LitJU", - "asset_symbol": "PPY", - "amount": "165884263" - },{ - "owner": "BTSBKXzc1CZQr3pMP5KFeXmfZeCohFYAujfC", - "asset_symbol": "PPY", - "amount": "32344987" - },{ - "owner": "BTS6SdmD9yHFndEhpsh1c8SkE3hAGKcFa7Ho", - "asset_symbol": "PPY", - "amount": "395189359" - },{ - "owner": "BTSBEH1LrhpDvzbFTyxwHXrrKYCFsnCQnQaM", - "asset_symbol": "PPY", - "amount": "2713205" - },{ - "owner": "BTS8Ha4it6MuArpNDjy8NNCKxrtWyEJgDYmf", - "asset_symbol": "PPY", - "amount": "1133256" - },{ - "owner": "BTSA67p8yJ8PzUy4pngjaShR7yEULYKpL7Nj", - "asset_symbol": "PPY", - "amount": "36961988" - },{ - "owner": "BTSGkSuo7KhxTdN5V2tudaAtKtb4HL9dRjV8", - "asset_symbol": "PPY", - "amount": "2872512" - },{ - "owner": "BTSHAj6ND5VubABDTSv8D3iWVfE3o1xk7Qpr", - "asset_symbol": "PPY", - "amount": "4926450" - },{ - "owner": "BTSKXzx6c6m3WJdog1VrYLcR8rB628EQHq8t", - "asset_symbol": "PPY", - "amount": "4979802" - },{ - "owner": "BTSNHWKRw6PkkKhkdhNAdYfQo9VLzQJx2fwc", - "asset_symbol": "PPY", - "amount": "295498" - },{ - "owner": "BTSEkhzzhm9YEpyzhnjLiQXUWyT6F5d3FMvc", - "asset_symbol": "PPY", - "amount": "37286641" - },{ - "owner": "BTS9NdzvyYo71jZXyRKfmccSDNey3kBrz3KQ", - "asset_symbol": "PPY", - "amount": "39964726" - },{ - "owner": "BTSBvthVWr2UFfDPKV1KCKbm7sA5HyXRnjXi", - "asset_symbol": "PPY", - "amount": "3465382" - },{ - "owner": "BTSG7amWKJ2gocMMrkPZGKfeajXUcNLwaB4t", - "asset_symbol": "PPY", - "amount": "5029836" - },{ - "owner": "BTSJ54Uqw2uKaFHjdusBSuS8hLehn4HM35Qb", - "asset_symbol": "PPY", - "amount": "99285894" - },{ - "owner": "BTS3eqCMpsxRm7Qz4PYW15hV9ngA7gR4y4Ci", - "asset_symbol": "PPY", - "amount": "12562911" - },{ - "owner": "BTSDAnDbTSiiNmyXL99tteRrgzDaoo96d1RA", - "asset_symbol": "PPY", - "amount": "4372228" - },{ - "owner": "BTS8ssPPfWMruLwnoTw5dav3tzQy1DMyaKDq", - "asset_symbol": "PPY", - "amount": "11709900" - },{ - "owner": "BTSHFW7DiPmmf63g8N9C8Emne4DduFtdyLxT", - "asset_symbol": "PPY", - "amount": "5577826" - },{ - "owner": "BTSGfv7p29MLmDLDmCJ5c6oD2hX3NPbNLHTr", - "asset_symbol": "PPY", - "amount": "8902865" - },{ - "owner": "BTSCk6v3WBU4VJVe66dCAjKz4bvrUCaCRRkp", - "asset_symbol": "PPY", - "amount": "32625820" - },{ - "owner": "BTSPreKQoxdgaGrsFUyeaZXTLQyZCtzHXopG", - "asset_symbol": "PPY", - "amount": "1120512" - },{ - "owner": "BTS4L1x1hwSpAPD3BdHtVnpvpyXJodJL6sTE", - "asset_symbol": "PPY", - "amount": "3444077" - },{ - "owner": "BTSdcfkMgmWLwVrs9yXTQBXQsENTThRaEHU", - "asset_symbol": "PPY", - "amount": "2869386" - },{ - "owner": "BTSAXB8WDc2mXhL32ytBKTZz6F5EqyytruCd", - "asset_symbol": "PPY", - "amount": "030275" - },{ - "owner": "BTS9TPJedPcnMwePdSPWWabEoLNmFVqmnbcF", - "asset_symbol": "PPY", - "amount": "113142" - },{ - "owner": "BTSPShfjVW2ps8MB1BMjGJTcHwXdP3EvM6Mq", - "asset_symbol": "PPY", - "amount": "39352622" - },{ - "owner": "BTSKjVnW9VJzc29WA2pYd2B7MfPJ5jBRRMJz", - "asset_symbol": "PPY", - "amount": "8618817" - },{ - "owner": "BTSCWQpJJKKTiuoFhSWfGCh8b569awhsGt9k", - "asset_symbol": "PPY", - "amount": "32884381" - },{ - "owner": "BTSByTMb8DY5GrC2zusGDQUXXLh4PuphzQcX", - "asset_symbol": "PPY", - "amount": "32956773" - },{ - "owner": "BTSMga9YWokQg56QUnxbhsNzh5Eh3p5WmeJ1", - "asset_symbol": "PPY", - "amount": "499389" - },{ - "owner": "BTSBiSDnSqqNP9LL7E57MBRgKLCaiARJZq61", - "asset_symbol": "PPY", - "amount": "13641837" - },{ - "owner": "BTSNhEZbzJJ5rNgHNk1PDYenXFJ1EHQUeada", - "asset_symbol": "PPY", - "amount": "5153854" - },{ - "owner": "BTSNV5W1tBY6j9mj7bssXnS2HrTcG6AGEQqC", - "asset_symbol": "PPY", - "amount": "672237" - },{ - "owner": "BTSDriRThaEPtLfgDAh4WVQHh1gZWUrovdLk", - "asset_symbol": "PPY", - "amount": "2088859" - },{ - "owner": "BTSD1pozXFRz4tcmNuzwMJtmoetJ9SzLm3Kx", - "asset_symbol": "PPY", - "amount": "6523960" - },{ - "owner": "BTSG3b9EfXujNw4AQ2yqbivpugYmULpPkrqw", - "asset_symbol": "PPY", - "amount": "1795305" - },{ - "owner": "BTSM6uWTxjErH7hwMAGKWHNMYHJ9sKtRe2p8", - "asset_symbol": "PPY", - "amount": "6563391" - },{ - "owner": "BTS3t6ASKC9tFTJfBoWEJ33VMBZUMB4cyCYC", - "asset_symbol": "PPY", - "amount": "34388777" - },{ - "owner": "BTSDippUXSZoVR7Gbrk2QM9RNCAWV88YVaSK", - "asset_symbol": "PPY", - "amount": "3231587" - },{ - "owner": "BTS4icjnF1DsHobwYBzy9J7FS9zRsxGWCQQT", - "asset_symbol": "PPY", - "amount": "845891" - },{ - "owner": "BTS98y17cgAp4du7kgGeRVPq3L2HTvqSWLA4", - "asset_symbol": "PPY", - "amount": "3269953" - },{ - "owner": "BTSAKho9gZutuiocRaffoAZfg7x5KXkTbniK", - "asset_symbol": "PPY", - "amount": "3460641" - },{ - "owner": "BTSLChZHQVwrihwNmDapDiHYmn3TCX2uuoem", - "asset_symbol": "PPY", - "amount": "10087252" - },{ - "owner": "BTS9GS82nqnh1y1cFVWq5tvHZtLvr1pJPhcp", - "asset_symbol": "PPY", - "amount": "6353795" - },{ - "owner": "BTS44H811jTg1kwVZVThj8g3SmQF87JLTCdJ", - "asset_symbol": "PPY", - "amount": "12038343" - },{ - "owner": "BTSAhoGsjobE4LRYRrnZ3LGXRnF3QqCYffSy", - "asset_symbol": "PPY", - "amount": "1190509" - },{ - "owner": "BTS6rgarBoe8nkScpc493wW14APcc5ywmnTa", - "asset_symbol": "PPY", - "amount": "1604292" - },{ - "owner": "BTSCxnANemdYeJfUKBRihZs27stPtpdnnFya", - "asset_symbol": "PPY", - "amount": "2053959" - },{ - "owner": "BTSDmSqLUfQ7xRN7RnR4GhJbRqCN9iGYAQnP", - "asset_symbol": "PPY", - "amount": "3346071" - },{ - "owner": "BTS8LadywANgxaGGsPcjESPXfUdU8y2vkFsF", - "asset_symbol": "PPY", - "amount": "71366154" - },{ - "owner": "BTS6o72tsyaqq6pfgUK1ewzJmYWmg3uDxTV9", - "asset_symbol": "PPY", - "amount": "1115512" - },{ - "owner": "BTSC1Jvhmv5X2dNPgLRK1eJfeHZUJCJdDwWh", - "asset_symbol": "PPY", - "amount": "1721604" - },{ - "owner": "BTSEtPHXr6CHhusSnVc9VGWHvkvxnLG1aobv", - "asset_symbol": "PPY", - "amount": "1660581" - },{ - "owner": "BTSACebjW6Cfp4v45UpCB3kcRVBYUig3KeRA", - "asset_symbol": "PPY", - "amount": "35250858" - },{ - "owner": "BTS7YK4ABqsPiFf26dtLSctMGwVegE3UExAY", - "asset_symbol": "PPY", - "amount": "25723372" - },{ - "owner": "BTSCSPUfydg34hNBPf6ouB2TUqAyxnwL7CvE", - "asset_symbol": "PPY", - "amount": "2520097" - },{ - "owner": "BTS25iAwGpKbmQixD3NoUPV1z7wAFaa1BiMa", - "asset_symbol": "PPY", - "amount": "2088475" - },{ - "owner": "BTSCkY1yVLbrTUpyq7A92TvnC5GaqWyQwcpU", - "asset_symbol": "PPY", - "amount": "3326196" - },{ - "owner": "BTSCL1orCs1gJ5WPG5TYpH6DRDrpAEv36t8d", - "asset_symbol": "PPY", - "amount": "105022" - },{ - "owner": "BTS5atYGpEoGSAP97Wr3zwRNsnyFLmoF2onX", - "asset_symbol": "PPY", - "amount": "66654407" - },{ - "owner": "BTSBn4CCb71fW7VqLXz41k28obTKi89Suc9n", - "asset_symbol": "PPY", - "amount": "206042" - },{ - "owner": "BTS4L3CDTfx3f7Sxm4rKQRizddhkMHKjPZz1", - "asset_symbol": "PPY", - "amount": "34380000" - },{ - "owner": "BTS13bPCp663vizwu1VbM2j51Qh2pEjKjMTh", - "asset_symbol": "PPY", - "amount": "2378174" - },{ - "owner": "BTS9QJuMMjkFwAAsK4AN1wgUhQw2ete2Lt7v", - "asset_symbol": "PPY", - "amount": "136405" - },{ - "owner": "BTSCHrqMoFvzhvQgxqr5v9Us6ng6a7LU1dNm", - "asset_symbol": "PPY", - "amount": "10053333" - },{ - "owner": "BTSKAjoKA8uBJe3JJ5HPVbHQFuezGRwVhPw5", - "asset_symbol": "PPY", - "amount": "18815812" - },{ - "owner": "BTSCZ2v4kYgBjH3spFNUaAvejSCaf874bfPo", - "asset_symbol": "PPY", - "amount": "11740336" - },{ - "owner": "BTSKYGdhjFwSeGrHeZAVdGcexJAeMsYuuU4", - "asset_symbol": "PPY", - "amount": "8298516" - },{ - "owner": "BTSLusZA2uxznS7RrfFFiX5vnji6W9gKBSD8", - "asset_symbol": "PPY", - "amount": "1650289" - },{ - "owner": "BTSCs6jLYypz5rBJP23ts8SfxA3eswuCn1x7", - "asset_symbol": "PPY", - "amount": "17380667" - },{ - "owner": "BTSvwfzSiUjBwSQArtuQSepRdCa8BJs9cLH", - "asset_symbol": "PPY", - "amount": "4102147" - },{ - "owner": "BTSBoig7BZbkSTF2Sj2AwkcNsLDdRmBcyatU", - "asset_symbol": "PPY", - "amount": "409370415" - },{ - "owner": "BTSFm1HRdDWb9tsovYQ13iWZHLAtdZD7LgZK", - "asset_symbol": "PPY", - "amount": "24888493" - },{ - "owner": "BTSMW3RSnLfDotxNV9xDqQDhfD2YMUocWzBq", - "asset_symbol": "PPY", - "amount": "4248693" - },{ - "owner": "BTS4AourZtspG5zKangfTtt7DoisVNAfJ7CJ", - "asset_symbol": "PPY", - "amount": "32971238" - },{ - "owner": "BTS2syNgmQBs8pChRi6eZXv5TrK3txadiBf3", - "asset_symbol": "PPY", - "amount": "503826" - },{ - "owner": "BTSBN8ZpC3emsCAkiDiMcTe1tp17vWhHL17w", - "asset_symbol": "PPY", - "amount": "2015486" - },{ - "owner": "BTSDvKYM2BY1Vea8RWyppuXCvQiPPLtGvbcq", - "asset_symbol": "PPY", - "amount": "7520909" - },{ - "owner": "BTSHCCq1xZhy7sqSpPwxYgV4hNQUwt7rPX5", - "asset_symbol": "PPY", - "amount": "949252" - },{ - "owner": "BTS2EH69zBbkxVJbMtUhu2gvJKBUWFv8ckLm", - "asset_symbol": "PPY", - "amount": "21312000" - },{ - "owner": "BTSGED8AMA9NaGfhJYDsBuZFdUNutN1za5wQ", - "asset_symbol": "PPY", - "amount": "301603" - },{ - "owner": "BTSNWmKFhtT2Ei2miujS4pjzAdijsn35MM49", - "asset_symbol": "PPY", - "amount": "36709113" - },{ - "owner": "BTS5GkRyrwXv7BMwq4F69nv3reHrwpdFAbPE", - "asset_symbol": "PPY", - "amount": "687227" - },{ - "owner": "BTSMoc94CbgPWmHXPwcim1cwHBCFskUjGmD5", - "asset_symbol": "PPY", - "amount": "119931367" - },{ - "owner": "BTS7REhX1bPfoD4UKG7NxVUf6MUqZtHqeWSf", - "asset_symbol": "PPY", - "amount": "9359997" - },{ - "owner": "BTS3eeYKmrefzMHqK6qbCnkcdVbmSHUFWwm2", - "asset_symbol": "PPY", - "amount": "704107" - },{ - "owner": "BTSKTkksTb6ATAyRauBkSMVF3cLH2721A6TD", - "asset_symbol": "PPY", - "amount": "231957" - },{ - "owner": "BTSEiPYGbNdhs3pwDWHSYYMioAkxuQK3wnAx", - "asset_symbol": "PPY", - "amount": "125456430" - },{ - "owner": "BTS2XQoiWRkavksk7WLnqquHaHGGcHfS9Ldj", - "asset_symbol": "PPY", - "amount": "069308" - },{ - "owner": "BTSGSkkLWXRuDWjuqSUm18X4hqt2Q7BmHYsg", - "asset_symbol": "PPY", - "amount": "2711821" - },{ - "owner": "BTS7NckQSMXQcskmzGGZtAALRiCS9ddWaXVy", - "asset_symbol": "PPY", - "amount": "448843" - },{ - "owner": "BTS8uRhPeyv8KfbJqaKR5gu8REymzFk11iw2", - "asset_symbol": "PPY", - "amount": "200430" - },{ - "owner": "BTSHEUJwZaZX3Lo6nawowq5npe5N3Qpw9vGD", - "asset_symbol": "PPY", - "amount": "6462854" - },{ - "owner": "BTSNpPGYXJXnPAG625azH9Dio894aqcyP5D4", - "asset_symbol": "PPY", - "amount": "336757" - },{ - "owner": "BTSDaU4awoz36R4sLmofge1gZSaHK9VQZyJf", - "asset_symbol": "PPY", - "amount": "19717286" - },{ - "owner": "BTS2FxAR8HScqbsXfE2pf5tXxRRA3AXVT1aq", - "asset_symbol": "PPY", - "amount": "44450720" - },{ - "owner": "BTS4bDJKYX6xo3ojQH43xCLdtFQvMzgMFETa", - "asset_symbol": "PPY", - "amount": "27225135" - },{ - "owner": "BTSMX8qCk2hFgGNrLarCvK9E56XqXPiamTFW", - "asset_symbol": "PPY", - "amount": "23399790" - },{ - "owner": "BTSEgLGBu3uGC487QeYmaLfYzX8ZsbL85ujc", - "asset_symbol": "PPY", - "amount": "1660045" - },{ - "owner": "BTSJPJD5x4kFYDJmGVf22U5c8TCePetjiRnE", - "asset_symbol": "PPY", - "amount": "6809379" - },{ - "owner": "BTS3NsE9DHAkQ9Ftd8VSVBzUtHPRNfSUmXog", - "asset_symbol": "PPY", - "amount": "35502352" - },{ - "owner": "BTS56ycLjxj2ZSgnrxjAii1bNPG4wnSQeJ1N", - "asset_symbol": "PPY", - "amount": "1668995" - },{ - "owner": "BTS2XB7oVHLCc5AFpA2a4Lrx141AXJnsRLYi", - "asset_symbol": "PPY", - "amount": "199314772" - },{ - "owner": "BTSB27h4wJaPDQesDFXYfzayAyVFbaUE2JnV", - "asset_symbol": "PPY", - "amount": "8218859" - },{ - "owner": "BTSKKGjLMpFMgx7iW53pBTqg2DM2Gz7GYSjM", - "asset_symbol": "PPY", - "amount": "17474013" - },{ - "owner": "BTSBYimW4xfLNhop4obwFvCRUysWoJhMQY1W", - "asset_symbol": "PPY", - "amount": "184455" - },{ - "owner": "BTSQ9hLYRbCCgioRuC6WpGMZgPCprRfSscyo", - "asset_symbol": "PPY", - "amount": "34255644" - },{ - "owner": "BTSEa4WCrfuW19gpXfBHmN3EDx8haXgDJQ6c", - "asset_symbol": "PPY", - "amount": "16599460" - },{ - "owner": "BTSAGKaoKWkf92k4qkmNAnP95mcZJ8yafEqx", - "asset_symbol": "PPY", - "amount": "41497389" - },{ - "owner": "BTSNSdYUaMc9uUeJDrf7fqNonrVj1gFNzuC3", - "asset_symbol": "PPY", - "amount": "8374960" - },{ - "owner": "BTSCTVSujB7mbbFQ4cCwNYBuZPkEhZ9aA6yS", - "asset_symbol": "PPY", - "amount": "001780" - },{ - "owner": "BTSGYTBp25K4wrMtSVtEYrjwRuUFFDK7yUg7", - "asset_symbol": "PPY", - "amount": "11164361" - },{ - "owner": "BTSP1p2uyPLu57Wa7Y78mw6AhRNq39YBBQAs", - "asset_symbol": "PPY", - "amount": "258858" - },{ - "owner": "BTSBGVJB3bkkt1311VHfpib2nDPfaFZhK1is", - "asset_symbol": "PPY", - "amount": "156477" - },{ - "owner": "BTSMXYqhJCfxroBoJGvshvD7XMEAiZSqZT3o", - "asset_symbol": "PPY", - "amount": "2711384" - },{ - "owner": "BTSArJycJCoxZ1J6uzFWEHfSsgNBZw522Ex4", - "asset_symbol": "PPY", - "amount": "16560093" - },{ - "owner": "BTSP4L2TZgUfUygWXhRMq2DxQYsWZuUoDNYC", - "asset_symbol": "PPY", - "amount": "6500000000" - },{ - "owner": "BTSkR98maLVSEVADXFCQwWXErqNBAdAPyTz", - "asset_symbol": "PPY", - "amount": "7500000000" - },{ - "owner": "BTSEzgbiv2zJTZL4X2Hq3M1iLXGSvtgPPucR", - "asset_symbol": "PPY", - "amount": "1500000000" - },{ - "owner": "BTSPHyywpLzCSABbUqZUmLofp9GjLcv7scCd", - "asset_symbol": "PPY", - "amount": "2500000000" - },{ - "owner": "BTS14iBMhdrctR6cfW1rX5i7Bhk92iniHvoY", - "asset_symbol": "PPY", - "amount": "1000000000" - },{ - "owner": "BTS6JXXcHVKxRGaxSKr1oqF1RsgwnSxGMuGa", - "asset_symbol": "PPY", - "amount": "10000000000" - },{ - "owner": "BTS9f19o77k1YZzm7wdxEuRJAwV5HTC7YKXc", - "asset_symbol": "PPY", - "amount": "6000000000" - },{ - "owner": "BTS13rqVQffrRv5Zd8XDNTAMUtWREd9qbsqb", - "asset_symbol": "PPY", - "amount": "5000000000" - },{ - "owner": "BTS2XADrvirTPRxjbTJNkTHwYEMhf57tirDh", - "asset_symbol": "PPY", - "amount": "1500000000" - },{ - "owner": "BTSNasFGAwaB82P26vsnxjSsbNzbsVsYj3Ao", - "asset_symbol": "PPY", - "amount": "650000000" - },{ - "owner": "BTS3cjPk3jRcK8kdzfdxbikmGLd5nuTz2ZoH", - "asset_symbol": "PPY", - "amount": "2000000000" - },{ - "owner": "BTSMvmgou7vqTZhhukKbUMVZchq3nNGUomBL", - "asset_symbol": "PPY", - "amount": "4730000000" - },{ - "owner": "BTSMQTYtcyc7KBhP8BHXo8tohVL9r9Ghhc7x", - "asset_symbol": "PPY", - "amount": "490000000" - },{ - "owner": "BTS2MP2bRi6SQf4eBaZNWzjnQF5FqDSFvpce", - "asset_symbol": "PPY", - "amount": "290000000" - },{ - "owner": "BTSH98SSEVXid5aJVatqwVpNCbEbVFkgyu2B", - "asset_symbol": "PPY", - "amount": "230000000" - },{ - "owner": "BTSA7JT75Q8bZ3DpY4BptYKcmeU49TETZRJm", - "asset_symbol": "PPY", - "amount": "260000000" - },{ - "owner": "BTSCjSR22LdXd4rkQ7UJnxvA8aziNFybMoZg", - "asset_symbol": "PPY", - "amount": "420000000" - },{ - "owner": "BTSf3jVUN1i2cZXigXbVy3s6zv2QBAEuWm2", - "asset_symbol": "PPY", - "amount": "320000000" - },{ - "owner": "BTS3ZGrVdAEeb12cX69eDMf7MwHxoFjJGDXT", - "asset_symbol": "PPY", - "amount": "210000000" - },{ - "owner": "BTS8544Wkyyu6kAeRk7MjTr6xgT1MdYYXGSf", - "asset_symbol": "PPY", - "amount": "470000000" - },{ - "owner": "BTSBGfdiCDfE6sNhv4QQnaDWcr5UtuCYwQc8", - "asset_symbol": "PPY", - "amount": "160000000" - },{ - "owner": "BTSNS9YcWTFctzie2vmqaBxcErMZBCbo6LXi", - "asset_symbol": "PPY", - "amount": "180000000" - },{ - "owner": "BTSGLk867ZhbYSLoxdD8ZD7jFw8EBApPmDii", - "asset_symbol": "PPY", - "amount": "180000000" - },{ - "owner": "BTS6twWEUEqiSBFDVQErZkRWNh51FkrzEdCn", - "asset_symbol": "PPY", - "amount": "250000000" - },{ - "owner": "BTSPJNDsrahCyHhj1dFQ8ahAvByERtjeFHCn", - "asset_symbol": "PPY", - "amount": "260000000" - },{ - "owner": "BTSijhPr7ijDUKJKsNyHSSvbYJ4Vvbjhsu1", - "asset_symbol": "PPY", - "amount": "200000000" - },{ - "owner": "BTSC1PPdFsCqFcxjzYp11iX8vWnFXED33ChK", - "asset_symbol": "PPY", - "amount": "140000000" - },{ - "owner": "BTSL6EWguE8TMZL6go93jSdgLXfrANJLHts6", - "asset_symbol": "PPY", - "amount": "230000000" - },{ - "owner": "BTS2jDXydZgwjcxJ9Q6Kfxww8pCuEtyZfFFQ", - "asset_symbol": "PPY", - "amount": "330000000" - },{ - "owner": "BTSLUaNjmJeCx3S7csqWdCifWKyPvXhTgz6p", - "asset_symbol": "PPY", - "amount": "150000000" - },{ - "owner": "BTS4h1JN97YLu79YaokD46qijVcPUj1VNbxD", - "asset_symbol": "PPY", - "amount": "270000000" - },{ - "owner": "BTSWcKVxDUnJ9TQQwpqtuPG5bZeE7BwPJt4", - "asset_symbol": "PPY", - "amount": "340000000" - },{ - "owner": "BTSFz7U1fqch4HLr54Xc18JmPWkBTewNHMcs", - "asset_symbol": "PPY", - "amount": "140000000" - },{ - "owner": "BTSAVEeBM3Djm2UMjC2GUgw8xChiqexZkpJU", - "asset_symbol": "PPY", - "amount": "240000000" - },{ - "owner": "BTSPj1GpUh95hfeTGdZdyRG1Z4UU93Pyxuqe", - "asset_symbol": "PPY", - "amount": "400000000" - },{ - "owner": "BTSTfvbta7ZgfffATKPHQpfvw7koULfH1mJ", - "asset_symbol": "PPY", - "amount": "440000000" - },{ - "owner": "BTS5KZ9ZUDtPfsyU86W9CpL85YbyLaYx4cpt", - "asset_symbol": "PPY", - "amount": "400000000" - },{ - "owner": "BTS6m7WYCESyusgv8b8ctHAm3UzxiDUHzQCE", - "asset_symbol": "PPY", - "amount": "500000000" - },{ - "owner": "BTS7qQoug1CVDrHkB5R8FPZu5TXS1ZphKHxJ", - "asset_symbol": "PPY", - "amount": "350000000" - },{ - "owner": "BTS3w52mznwX8inMk5xGTtYo2SGNtharDH8o", - "asset_symbol": "PPY", - "amount": "300000000" - },{ - "owner": "BTS7sthRdtAF6S2fWqtQ6DxU8KWDYGPVgqw1", - "asset_symbol": "PPY", - "amount": "340000000" - },{ - "owner": "BTS4Fdfot558xHawvw1qLqU94wYaBvgVCaqZ", - "asset_symbol": "PPY", - "amount": "230000000" - },{ - "owner": "BTS6hd8TggqgK54ZkVctEb9JwNEQ2NgubN1R", - "asset_symbol": "PPY", - "amount": "330000000" - },{ - "owner": "BTS3ZQHZ3xypSGTwDTtLyP7iZJQSRaAzaNrP", - "asset_symbol": "PPY", - "amount": "480000000" - },{ - "owner": "BTSDxb36wgyEHDmZcbtbnn4PuJyGeN9STcrG", - "asset_symbol": "PPY", - "amount": "290000000" - },{ - "owner": "BTS47npGDxnLo3ir9Wv4NMEi2xccT2yGwPb4", - "asset_symbol": "PPY", - "amount": "180000000" - },{ - "owner": "BTSERcc1t6Vq9tG7LaU4EbPmbArAmZAs7Dr8", - "asset_symbol": "PPY", - "amount": "300000000" - },{ - "owner": "BTSERaNVbeWtTTuEuppoBokupFgPfxGMqwt6", - "asset_symbol": "PPY", - "amount": "1900000000" - },{ - "owner": "BTSGfnKLkGwjciAWZhVVBRGrHCmpQBod3ff8", - "asset_symbol": "PPY", - "amount": "80000000" - },{ - "owner": "BTSJ5s6gMpWXwcpWexwn3cYpMZfbEk5TbfJz", - "asset_symbol": "PPY", - "amount": "1000000000" - },{ - "owner": "BTSBpuGahFA9pMVkFsYfGsGbpYFr7RWMzosN", - "asset_symbol": "PPY", - "amount": "720000000" - },{ - "owner": "BTS3ewQw6r5Jv1UU7WyzyPTWV3b41bxTWmMn", - "asset_symbol": "PPY", - "amount": "629200000" - },{ - "owner": "BTSLTaqTGVjDaStPBGSmQw21ZN7GzDB5X5Cz", - "asset_symbol": "PPY", - "amount": "1432000000" - },{ - "owner": "BTSLg9yhhxb7SiE4Uu4EvYx2hmMvSFusyjMc", - "asset_symbol": "PPY", - "amount": "1200000000" - },{ - "owner": "BTSBabRnBMjWhkoeVrg7TVPFUTkZDS3zn25V", - "asset_symbol": "PPY", - "amount": "3680000000" - },{ - "owner": "BTS9YYYKPvpPM4uDwqHwZaN3LCms5rpr3Rz2", - "asset_symbol": "PPY", - "amount": "616000000" - },{ - "owner": "BTSC5DWo5kNYWPbu44yW4YBnm5qTEAjLP6z5", - "asset_symbol": "PPY", - "amount": "1760000000" - },{ - "owner": "BTSEUChVsY5JXUDmnGig8QhfycF63CXn5jdu", - "asset_symbol": "PPY", - "amount": "1760000000" - },{ - "owner": "BTSKUpEazuoz9bu1at57QjMXc8H6rFsvw26v", - "asset_symbol": "PPY", - "amount": "1760000000" - },{ - "owner": "BTSCwXrSakaUWF2sU75nknLvLfX926i6nR6G", - "asset_symbol": "PPY", - "amount": "480000000" - },{ - "owner": "BTSGvDT4czKtSLNfjCDJsCs2saaRobNyTuLu", - "asset_symbol": "PPY", - "amount": "480000000" - },{ - "owner": "BTSLs8SNSAyecfDWxp5Dbc4geCrLpk3oCp5h", - "asset_symbol": "PPY", - "amount": "480000000" - },{ - "owner": "BTS4Wh5Br8KUmj3FWGZnSHpj6Xq6Cx6ehdkV", - "asset_symbol": "PPY", - "amount": "640000000" - },{ - "owner": "BTS59CwH9qvpSriRbqaW5bN4e18gdmWBYfV6", - "asset_symbol": "PPY", - "amount": "640000000" - },{ - "owner": "BTSDFamq3RAiybQouBSD1frXtQx5ivNPWkL", - "asset_symbol": "PPY", - "amount": "2560000000" - },{ - "owner": "BTS2fEznaxLEbRJ4AUrtebpomHcb6Lyk3n8w", - "asset_symbol": "PPY", - "amount": "640000000" - },{ - "owner": "BTS47VrTVn4Tzipe4AmLy2xWWd8MBPUYjYdX", - "asset_symbol": "PPY", - "amount": "1520000000" - },{ - "owner": "BTSEjE1D9btW9AjwqS11LCX9xhCzTCZ2x8Mz", - "asset_symbol": "PPY", - "amount": "1520000000" - },{ - "owner": "BTS5srz2ooKi9CvpNj5ozdXQcHzkdAjxq9wL", - "asset_symbol": "PPY", - "amount": "1520000000" - },{ - "owner": "BTS87UN19jy2UntDPyfS8GQt5nxbY48ibQn8", - "asset_symbol": "PPY", - "amount": "1200000000" - },{ - "owner": "BTSPqziQhNthdUtUPaZBnzDS52X57HUnweFW", - "asset_symbol": "PPY", - "amount": "1200000000" - },{ - "owner": "BTSMP3DesBZdf4nqSMa1qhFaBceQ5VRNuDyB", - "asset_symbol": "PPY", - "amount": "1200000000" - },{ - "owner": "BTSEXGLptBHyZFw1MRTzPtTHPjTz51hQjA1W", - "asset_symbol": "PPY", - "amount": "52800000" - },{ - "owner": "BTSK8upvutxXao7exFnQa38Wopouv4PxTMUc", - "asset_symbol": "PPY", - "amount": "1200000000" - },{ - "owner": "BTSFePh3fvmRrGJkzzEt1ib3CvqX7vJpM4LD", - "asset_symbol": "PPY", - "amount": "1600000000" - },{ - "owner": "BTSEY72dSF9hnUuftzSKPMszLJUf6YJwFxKr", - "asset_symbol": "PPY", - "amount": "2086000000" - },{ - "owner": "BTS2J5iLznc8TCEwFgLXk8j3aKEXZHmoP6rA", - "asset_symbol": "PPY", - "amount": "1280000000" - },{ - "owner": "BTSFm4tcuySR2qqLbYrzELekNpxsW7okgonP", - "asset_symbol": "PPY", - "amount": "1440000000" - },{ - "owner": "BTSGtBMitWcGbzf7tZnvT1BHQJY7KcyFQhHA", - "asset_symbol": "PPY", - "amount": "400000000" - },{ - "owner": "BTS277eCDueHDDzUnYVHnDW3BBkqrd64956N", - "asset_symbol": "PPY", - "amount": "160000000" - },{ - "owner": "BTSEffstjdh5dBHndpFa3cwDeR6VvHvdmCwf", - "asset_symbol": "PPY", - "amount": "720000000" - },{ - "owner": "BTSBVGJf6pjEto2LaY51cevqbMbFYBkTVq7f", - "asset_symbol": "PPY", - "amount": "1160000000" - },{ - "owner": "BTSKfiRoSK2cvFXMvvzudcUh7Zj9YwdcLKc4", - "asset_symbol": "PPY", - "amount": "1496000000" - },{ - "owner": "BTS2ExirtYhbMtXvxng8YyPDrem67hPHSz62", - "asset_symbol": "PPY", - "amount": "3760000000" - },{ - "owner": "BTS2JhKPG7VTKC5u6t9FuWqdAprLk4Pe4QXN", - "asset_symbol": "PPY", - "amount": "5480000000" - },{ - "owner": "BTS6sBvTyf9w4n8gFL93e7o46kDR3Dj43z7e", - "asset_symbol": "PPY", - "amount": "2480000000" - },{ - "owner": "BTS5YaZDK6d7nr3reQaezN98A1BH5mxwwUAS", - "asset_symbol": "PPY", - "amount": "14880000000" - },{ - "owner": "BTSG7zMyEyNCBjLFSokwVW7U2XnR9kZmhMbX", - "asset_symbol": "PPY", - "amount": "960000000" - },{ - "owner": "BTSKrAUaFDhWGr4T9GKGiTxMiVugjQbVT8Mb", - "asset_symbol": "PPY", - "amount": "4184000000" - },{ - "owner": "BTSL1VV4e1LPsiuuSbHYqcS4qD1iBUzAEy7h", - "asset_symbol": "PPY", - "amount": "3360000000" - },{ - "owner": "BTSKM1tJpFdMgBfgKZ2XVJNyDSoWX1eytCY1", - "asset_symbol": "PPY", - "amount": "1280000000" - },{ - "owner": "BTSDJUBARg2RmVqsmzTgUgPN9vtwGKfCeUMv", - "asset_symbol": "PPY", - "amount": "1120000000" - } - ], - "initial_vesting_balances": [ - { - "owner": "BTSERcc1t6Vq9tG7LaU4EbPmbArAmZAs7Dr8", - "asset_symbol": "PPY", - "amount": "75000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSERaNVbeWtTTuEuppoBokupFgPfxGMqwt6", - "asset_symbol": "PPY", - "amount": "475000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSGfnKLkGwjciAWZhVVBRGrHCmpQBod3ff8", - "asset_symbol": "PPY", - "amount": "20000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSJ5s6gMpWXwcpWexwn3cYpMZfbEk5TbfJz", - "asset_symbol": "PPY", - "amount": "250000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSBpuGahFA9pMVkFsYfGsGbpYFr7RWMzosN", - "asset_symbol": "PPY", - "amount": "160000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS3ewQw6r5Jv1UU7WyzyPTWV3b41bxTWmMn", - "asset_symbol": "PPY", - "amount": "157300000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSLTaqTGVjDaStPBGSmQw21ZN7GzDB5X5Cz", - "asset_symbol": "PPY", - "amount": "358000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSLg9yhhxb7SiE4Uu4EvYx2hmMvSFusyjMc", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSBabRnBMjWhkoeVrg7TVPFUTkZDS3zn25V", - "asset_symbol": "PPY", - "amount": "920000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS9YYYKPvpPM4uDwqHwZaN3LCms5rpr3Rz2", - "asset_symbol": "PPY", - "amount": "154000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSC5DWo5kNYWPbu44yW4YBnm5qTEAjLP6z5", - "asset_symbol": "PPY", - "amount": "440000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSEUChVsY5JXUDmnGig8QhfycF63CXn5jdu", - "asset_symbol": "PPY", - "amount": "440000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSKUpEazuoz9bu1at57QjMXc8H6rFsvw26v", - "asset_symbol": "PPY", - "amount": "440000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSCwXrSakaUWF2sU75nknLvLfX926i6nR6G", - "asset_symbol": "PPY", - "amount": "120000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSGvDT4czKtSLNfjCDJsCs2saaRobNyTuLu", - "asset_symbol": "PPY", - "amount": "120000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSLs8SNSAyecfDWxp5Dbc4geCrLpk3oCp5h", - "asset_symbol": "PPY", - "amount": "120000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS4Wh5Br8KUmj3FWGZnSHpj6Xq6Cx6ehdkV", - "asset_symbol": "PPY", - "amount": "160000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS59CwH9qvpSriRbqaW5bN4e18gdmWBYfV6", - "asset_symbol": "PPY", - "amount": "160000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSDFamq3RAiybQouBSD1frXtQx5ivNPWkL", - "asset_symbol": "PPY", - "amount": "640000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS2fEznaxLEbRJ4AUrtebpomHcb6Lyk3n8w", - "asset_symbol": "PPY", - "amount": "160000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS47VrTVn4Tzipe4AmLy2xWWd8MBPUYjYdX", - "asset_symbol": "PPY", - "amount": "380000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSEjE1D9btW9AjwqS11LCX9xhCzTCZ2x8Mz", - "asset_symbol": "PPY", - "amount": "380000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS5srz2ooKi9CvpNj5ozdXQcHzkdAjxq9wL", - "asset_symbol": "PPY", - "amount": "380000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS87UN19jy2UntDPyfS8GQt5nxbY48ibQn8", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSPqziQhNthdUtUPaZBnzDS52X57HUnweFW", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSMP3DesBZdf4nqSMa1qhFaBceQ5VRNuDyB", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSEXGLptBHyZFw1MRTzPtTHPjTz51hQjA1W", - "asset_symbol": "PPY", - "amount": "13200000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSK8upvutxXao7exFnQa38Wopouv4PxTMUc", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSFePh3fvmRrGJkzzEt1ib3CvqX7vJpM4LD", - "asset_symbol": "PPY", - "amount": "400000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSEY72dSF9hnUuftzSKPMszLJUf6YJwFxKr", - "asset_symbol": "PPY", - "amount": "521500000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS2J5iLznc8TCEwFgLXk8j3aKEXZHmoP6rA", - "asset_symbol": "PPY", - "amount": "320000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSFm4tcuySR2qqLbYrzELekNpxsW7okgonP", - "asset_symbol": "PPY", - "amount": "360000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSGtBMitWcGbzf7tZnvT1BHQJY7KcyFQhHA", - "asset_symbol": "PPY", - "amount": "100000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS277eCDueHDDzUnYVHnDW3BBkqrd64956N", - "asset_symbol": "PPY", - "amount": "40000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSEffstjdh5dBHndpFa3cwDeR6VvHvdmCwf", - "asset_symbol": "PPY", - "amount": "180000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSBVGJf6pjEto2LaY51cevqbMbFYBkTVq7f", - "asset_symbol": "PPY", - "amount": "290000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSKfiRoSK2cvFXMvvzudcUh7Zj9YwdcLKc4", - "asset_symbol": "PPY", - "amount": "374000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS2ExirtYhbMtXvxng8YyPDrem67hPHSz62", - "asset_symbol": "PPY", - "amount": "940000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS2JhKPG7VTKC5u6t9FuWqdAprLk4Pe4QXN", - "asset_symbol": "PPY", - "amount": "1370000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS6sBvTyf9w4n8gFL93e7o46kDR3Dj43z7e", - "asset_symbol": "PPY", - "amount": "620000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTS5YaZDK6d7nr3reQaezN98A1BH5mxwwUAS", - "asset_symbol": "PPY", - "amount": "3720000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSG7zMyEyNCBjLFSokwVW7U2XnR9kZmhMbX", - "asset_symbol": "PPY", - "amount": "240000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSKrAUaFDhWGr4T9GKGiTxMiVugjQbVT8Mb", - "asset_symbol": "PPY", - "amount": "1046000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSL1VV4e1LPsiuuSbHYqcS4qD1iBUzAEy7h", - "asset_symbol": "PPY", - "amount": "840000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSKM1tJpFdMgBfgKZ2XVJNyDSoWX1eytCY1", - "asset_symbol": "PPY", - "amount": "320000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSDJUBARg2RmVqsmzTgUgPN9vtwGKfCeUMv", - "asset_symbol": "PPY", - "amount": "280000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "15724800" - },{ - "owner": "BTSDqcq1yC1TdS4oSkjsTU8tVB4Ke8Wonq74", - "asset_symbol": "PPY", - "amount": "2000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTS9P3MYDgBc5256Fsh3q3CvCzk1rRpQ4tRr", - "asset_symbol": "PPY", - "amount": "700000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTS3E7Z3Tk78MJEYySyNEwNnC4X2TnuaGQ4B", - "asset_symbol": "PPY", - "amount": "900000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSQ3BagzUdj88PLGrGSR3MsBMNhTwA69Dbt", - "asset_symbol": "PPY", - "amount": "8000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSFT8nqfKopf3tAi1Vt9kABdQRNpcGJFxJW", - "asset_symbol": "PPY", - "amount": "10000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSGJ29pkBVXRF6N9yepiiLFWs1s6nUhaLps", - "asset_symbol": "PPY", - "amount": "8000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSMPMJDkQzc5cE5m7CXfNWSZdHUr1L9SERC", - "asset_symbol": "PPY", - "amount": "500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSHQEHHs6BnTGbVeLUdbT8mfG2RN4nu6dgZ", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSJh2b9XunZ4mH2mBykM1PhcunzzuxwFove", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSGMs3nuskgbvx4xF43rftiKBz3SEsvKCXt", - "asset_symbol": "PPY", - "amount": "1500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSDJYjvdRymAo7nnE5sihpVuH5N8tGMctD9", - "asset_symbol": "PPY", - "amount": "1500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSGtjY8pianN4QHyGkr13swQZiosNEKXidD", - "asset_symbol": "PPY", - "amount": "3000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSJEJtLsUXnKfQbBcrSWCPc5JrtmaVM62Sz", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTS9nVc7MAHoeGirxJd924VKE9bZ6ymA3SRk", - "asset_symbol": "PPY", - "amount": "3000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSNQB7nfJXhzwh4sbFZNQ6hmMXNHMiC6wWg", - "asset_symbol": "PPY", - "amount": "200000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSDneeHxuuEkbPjspr81kaQWamaWvdvDS3Y", - "asset_symbol": "PPY", - "amount": "500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSNb2QZTPYjmKhnAjBQJ3qbowcWgftVi5mX", - "asset_symbol": "PPY", - "amount": "1000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTS8fNQxeTVBMcfjqas5ApLn11du6mqE9Z1G", - "asset_symbol": "PPY", - "amount": "10000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSJHYnesZEmPqqKbiewKt9CjeEfTh7eyNGc", - "asset_symbol": "PPY", - "amount": "1400000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSKrPJPWaNcGmY4BUC7aVoPjiKtqem3JAPW", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSGkGv87C7tNiSgt5phFRxtBhKooVLA6Dan", - "asset_symbol": "PPY", - "amount": "300000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSB1Udjm6Xn1KhLrYYiYLyuGFRisRhErKLk", - "asset_symbol": "PPY", - "amount": "20000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSLcbeEHsndg9WbmAm3no3XNHZWiCqUPBwA", - "asset_symbol": "PPY", - "amount": "3000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSBf5CTcpm4ayRvesA98LCTDDgCpNE81Qn7", - "asset_symbol": "PPY", - "amount": "3000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSA2bmUacXaaCt8zoynHzHUYCyPUdKXYSrq", - "asset_symbol": "PPY", - "amount": "3000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTS46YW6bev4D1E3uh8eB5gmE7HLwSsNCC2s", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSPWfE8Wr61JCExtKuZzzoKzZa7RFY83o8h", - "asset_symbol": "PPY", - "amount": "2000000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSK9U5MR6cZqzp5qUy7ueH74HfDtvqSbHVv", - "asset_symbol": "PPY", - "amount": "1500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - },{ - "owner": "BTSCpfu5ggBQNrnUnV5g5SKHggodHS2dmgqY", - "asset_symbol": "PPY", - "amount": "2500000000", - "begin_timestamp": "2017-05-30T08:09:05", - "vesting_duration_seconds": "31536000" - } - ], - "initial_active_witnesses": 11, - "initial_witness_candidates": [{ - "owner_name": "init0", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init1", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init2", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init3", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init4", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init5", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init6", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init7", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init8", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init9", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - },{ - "owner_name": "init10", - "block_signing_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - } - ], - "initial_committee_candidates": [{ - "owner_name": "init0" - },{ - "owner_name": "init1" - },{ - "owner_name": "init2" - },{ - "owner_name": "init3" - },{ - "owner_name": "init4" - },{ - "owner_name": "init5" - },{ - "owner_name": "init6" - } - ], - "initial_worker_candidates": [], - "initial_chain_id": "aa34045518f1469a28fa4578240d5f039afa9959c0b95ce3b39674efa691fb21", - "immutable_parameters": { - "min_committee_member_count": 11, - "min_witness_count": 11, - "num_special_accounts": 0, - "num_special_assets": 0 - } -} \ No newline at end of file diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 12ab5a5d..3941d890 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1803,7 +1803,7 @@ vector database_api_impl::get_tournaments(tournament_id_type { vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); - for ( auto elem : boost::make_iterator_range(tournament_idx.rbegin(), tournament_idx.rend())) { + for (auto elem: tournament_idx) { if( result.size() >= limit ) break; if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) && ( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type())) @@ -1829,7 +1829,7 @@ vector database_api_impl::get_tournaments_by_state(tournament { vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); - for ( auto elem : boost::make_iterator_range(tournament_idx.rbegin(), tournament_idx.rend())) { + for (auto elem: tournament_idx) { if( result.size() >= limit ) break; if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) && ( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()) && diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e2735c32..59c5f0cf 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -688,7 +688,7 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) ); const witness_object& witness = next_block.witness(*this); //DLN: TODO: Temporarily commented out to test shuffle vs RNG scheduling algorithm for witnesses, this was causing shuffle agorithm to fail during create_witness test. This should be re-enabled for RNG, and maybe for shuffle too, don't really know for sure. -// FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "", +// FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "", // ("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash)("null_secret_hash", secret_hash_type::hash( secret_hash_type()))); if( !(skip&skip_witness_signature) ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1d344fc6..19d32e8c 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -641,15 +641,45 @@ void database::init_genesis(const genesis_state_type& genesis_state) } // Create balances for all bts accounts - for( const auto& account : genesis_state.initial_bts_accounts ) + for( const auto& account : genesis_state.initial_bts_accounts ) { if (account.core_balance != share_type()) { total_supplies[asset_id_type()] += account.core_balance; create([&](account_balance_object& b) { - b.owner = get_account_id(account.name); - b.balance = account.core_balance; + b.owner = get_account_id(account.name); + b.balance = account.core_balance; }); } + + // create any vesting balances for this account + if (account.vesting_balances) + for (const auto& vesting_balance : *account.vesting_balances) { + create([&](vesting_balance_object& vbo) { + vbo.owner = get_account_id(account.name); + vbo.balance = asset(vesting_balance.amount, get_asset_id(vesting_balance.asset_symbol)); + if (vesting_balance.policy_type == "linear") { + auto initial_linear_vesting_policy = vesting_balance.policy.as(); + linear_vesting_policy new_vesting_policy; + new_vesting_policy.begin_timestamp = initial_linear_vesting_policy.begin_timestamp; + new_vesting_policy.vesting_cliff_seconds = initial_linear_vesting_policy.vesting_cliff_seconds; + new_vesting_policy.vesting_duration_seconds = initial_linear_vesting_policy.vesting_duration_seconds; + new_vesting_policy.begin_balance = initial_linear_vesting_policy.begin_balance; + vbo.policy = new_vesting_policy; + } else if (vesting_balance.policy_type == "cdd") { + auto initial_cdd_vesting_policy = vesting_balance.policy.as(); + cdd_vesting_policy new_vesting_policy; + new_vesting_policy.vesting_seconds = initial_cdd_vesting_policy.vesting_seconds; + new_vesting_policy.coin_seconds_earned = initial_cdd_vesting_policy.coin_seconds_earned; + new_vesting_policy.start_claim = initial_cdd_vesting_policy.start_claim; + new_vesting_policy.coin_seconds_earned_last_update = initial_cdd_vesting_policy.coin_seconds_earned_last_update; + vbo.policy = new_vesting_policy; + } + total_supplies[get_asset_id(vesting_balance.asset_symbol)] += vesting_balance.amount; + }); + } + } + + // Create initial balances share_type total_allocation; for( const auto& handout : genesis_state.initial_balances ) @@ -673,7 +703,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) linear_vesting_policy policy; policy.begin_timestamp = vest.begin_timestamp; - policy.vesting_cliff_seconds = 0; + policy.vesting_cliff_seconds = vest.vesting_cliff_seconds ? *vest.vesting_cliff_seconds : 0; policy.vesting_duration_seconds = vest.vesting_duration_seconds; policy.begin_balance = vest.begin_balance; @@ -724,7 +754,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) } ++it; } +// @romek +#if 0 FC_ASSERT( !has_imbalanced_assets ); +#endif // Save tallied supplies for( const auto& item : total_supplies ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index d14d5cf2..c45acd97 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -56,32 +56,32 @@ void database::update_global_dynamic_data( const signed_block& b ) assert( missed_blocks != 0 ); #endif // bad if-condition, this code needs to execute for both shuffled and rng algorithms -// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) -// { - missed_blocks--; - for( uint32_t i = 0; i < missed_blocks; ++i ) { - const auto& witness_missed = get_scheduled_witness( i+1 )(*this); - if( witness_missed.id != b.witness ) { - /* - const auto& witness_account = witness_missed.witness_account(*this); - if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) ) - wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) ); - */ +// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) +// { + missed_blocks--; + for( uint32_t i = 0; i < missed_blocks; ++i ) { + const auto& witness_missed = get_scheduled_witness( i+1 )(*this); + if( witness_missed.id != b.witness ) { + /* + const auto& witness_account = witness_missed.witness_account(*this); + if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) ) + wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) ); + */ - modify( witness_missed, [&]( witness_object& w ) { - w.total_missed++; - }); - } - } + modify( witness_missed, [&]( witness_object& w ) { + w.total_missed++; + }); + } + } // } #ifdef DIRTY_TRICK } #endif // dynamic global properties updating modify( _dgp, [&]( dynamic_global_property_object& dgp ){ - secret_hash_type::encoder enc; - fc::raw::pack( enc, dgp.random ); - fc::raw::pack( enc, b.previous_secret ); + secret_hash_type::encoder enc; + fc::raw::pack( enc, dgp.random ); + fc::raw::pack( enc, b.previous_secret ); dgp.random = enc.result(); _random_number_generator = fc::hash_ctr_rng(dgp.random.data()); @@ -258,7 +258,7 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s } auto least_collateral = call_itr->collateralization(); - if( ~least_collateral >= highest ) + if( ~least_collateral >= highest ) { elog( "Black Swan detected: \n" " Least collateralized call: ${lc} ${~lc}\n" @@ -272,7 +272,7 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s FC_ASSERT( enable_black_swan, "Black swan was detected during a margin update which is not allowed to trigger a blackswan" ); globally_settle_asset(mia, ~least_collateral ); return true; - } + } return false; } @@ -429,8 +429,8 @@ void database::clear_expired_orders() } try { settled += match(*itr, order, settlement_price, max_settlement); - } - catch ( const black_swan_exception& e ) { + } + catch ( const black_swan_exception& e ) { wlog( "black swan detected: ${e}", ("e", e.to_detail_string() ) ); cancel_order( order ); break; @@ -553,7 +553,7 @@ void start_fully_registered_tournaments(database& db) // find the first tournament waiting to start; if its start time has arrived, start it auto start_iter = start_time_index.lower_bound(boost::make_tuple(tournament_state::awaiting_start)); if (start_iter != start_time_index.end() && - start_iter->get_state() == tournament_state::awaiting_start && + start_iter->get_state() == tournament_state::awaiting_start && *start_iter->start_time <= db.head_block_time()) { db.modify(*start_iter, [&](tournament_object& t) { @@ -576,7 +576,7 @@ void initiate_next_games(database& db) auto& next_timeout_index = db.get_index_type().indices().get(); while (1) { - // empty time_points are sorted to the beginning, so upper_bound takes us to the first + // empty time_points are sorted to the beginning, so upper_bound takes us to the first // non-empty time_point auto start_iter = next_timeout_index.upper_bound(boost::make_tuple(optional())); if (start_iter != next_timeout_index.end() && diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 156d03a0..d1c70b6f 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -23,8 +23,10 @@ */ #pragma once -#define GRAPHENE_SYMBOL "PPY2T" -#define GRAPHENE_ADDRESS_PREFIX "PPY" +#define GRAPHENE_SYMBOL "TEST" +#define GRAPHENE_ADDRESS_PREFIX "TEST" +//#define GRAPHENE_SYMBOL "PPY" +//#define GRAPHENE_ADDRESS_PREFIX "PPY" #define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1 #define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63 diff --git a/libraries/chain/include/graphene/chain/genesis_state.hpp b/libraries/chain/include/graphene/chain/genesis_state.hpp index 53561485..ebd153b6 100644 --- a/libraries/chain/include/graphene/chain/genesis_state.hpp +++ b/libraries/chain/include/graphene/chain/genesis_state.hpp @@ -59,6 +59,24 @@ struct genesis_state_type { flat_map key_auths; flat_map address_auths; }; + struct initial_cdd_vesting_policy { + uint32_t vesting_seconds; + fc::uint128_t coin_seconds_earned; + fc::time_point_sec start_claim; + fc::time_point_sec coin_seconds_earned_last_update; + }; + struct initial_linear_vesting_policy { + fc::time_point_sec begin_timestamp; + uint32_t vesting_cliff_seconds; + uint32_t vesting_duration_seconds; + share_type begin_balance; + }; + struct initial_vesting_balance { + string asset_symbol; + share_type amount; + std::string policy_type; // either "linear" or "cdd" + fc::variant policy; // either an initial_cdd_vesting_policy or initial_linear_vesting_policy + }; initial_bts_account_type(const string& name = string(), const initial_authority& owner_authority = initial_authority(), const initial_authority& active_authority = initial_authority(), @@ -72,6 +90,7 @@ struct genesis_state_type { initial_authority owner_authority; initial_authority active_authority; share_type core_balance; + fc::optional > vesting_balances; }; struct initial_asset_type { struct initial_collateral_position { @@ -102,6 +121,7 @@ struct genesis_state_type { string asset_symbol; share_type amount; time_point_sec begin_timestamp; + fc::optional vesting_cliff_seconds; uint32_t vesting_duration_seconds = 0; share_type begin_balance; }; @@ -161,7 +181,7 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type, (owner)(asset_symbol)(amount)) FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type, - (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance)) + (owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance)) FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)) @@ -174,12 +194,27 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initia (account_auths) (key_auths) (address_auths)) - +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy, + (vesting_seconds) + (coin_seconds_earned) + (start_claim) + (coin_seconds_earned_last_update)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy, + (begin_timestamp) + (vesting_cliff_seconds) + (vesting_duration_seconds) + (begin_balance)) +FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance, + (asset_symbol) + (amount) + (policy_type) + (policy)) FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type, (name) (owner_authority) (active_authority) - (core_balance)) + (core_balance) + (vesting_balances)) FC_REFLECT(graphene::chain::genesis_state_type, (initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances) diff --git a/libraries/chain/include/graphene/chain/protocol/witness.hpp b/libraries/chain/include/graphene/chain/protocol/witness.hpp index 27e40d60..b096e826 100644 --- a/libraries/chain/include/graphene/chain/protocol/witness.hpp +++ b/libraries/chain/include/graphene/chain/protocol/witness.hpp @@ -70,7 +70,7 @@ namespace graphene { namespace chain { optional< public_key_type > new_signing_key; /// The new secreat hash. optional new_initial_secret; - + account_id_type fee_payer()const { return witness_account; } void validate()const; }; diff --git a/libraries/plugins/generate_genesis/generate_genesis.cpp b/libraries/plugins/generate_genesis/generate_genesis.cpp index 2680d288..d369392a 100644 --- a/libraries/plugins/generate_genesis/generate_genesis.cpp +++ b/libraries/plugins/generate_genesis/generate_genesis.cpp @@ -116,9 +116,9 @@ bool is_special_account(const graphene::chain::account_id_type& account_id) return account_id.instance < 100; } -bool is_scam(const std::string& account_name) -{ - static std::set scam_accounts{ + bool is_scam(const std::string& account_name) + { + static std::set scam_accounts{ "polonie-wallet", "polonie-xwallet", "poloniewallet", @@ -150,14 +150,14 @@ bool is_scam(const std::string& account_name) "btc38.com", "yunbi.com", "coinbase.com", - "ripple.com" - }; - return scam_accounts.find(account_name) != scam_accounts.end(); -} - -bool is_exchange(const std::string& account_name) -{ - static std::set exchange_accounts{ + "ripple.com" + }; + return scam_accounts.find(account_name) != scam_accounts.end(); + } + + bool is_exchange(const std::string& account_name) + { + static std::set exchange_accounts{ "poloniexcoldstorage", "btc38-public-for-bts-cold", "poloniexwallet", @@ -166,233 +166,234 @@ bool is_exchange(const std::string& account_name) "btc38-btsx-octo-72722", "bittrex-deposit", "btc38btsxwithdrawal" - }; - return exchange_accounts.find(account_name) != exchange_accounts.end(); -} + }; + return exchange_accounts.find(account_name) != exchange_accounts.end(); + } -bool exclude_account_from_sharedrop(graphene::chain::database& d, const graphene::chain::account_id_type& account_id) -{ - if (is_special_account(account_id)) - return true; - const std::string& account_name = account_id(d).name; - return is_exchange(account_name) || is_scam(account_name); -} + bool exclude_account_from_sharedrop(graphene::chain::database& d, const graphene::chain::account_id_type& account_id) + { + if (is_special_account(account_id)) + return true; + const std::string& account_name = account_id(d).name; + return is_exchange(account_name) || is_scam(account_name); + } void generate_genesis_plugin::generate_snapshot() { try { - ilog("generate genesis plugin: generating snapshot now"); - graphene::chain::genesis_state_type new_genesis_state; - chain::database& d = database(); + ilog("generate genesis plugin: generating snapshot now"); + graphene::chain::genesis_state_type new_genesis_state; + chain::database& d = database(); - // we'll distribute 5% of (some amount of tokens), so: - graphene::chain::share_type total_amount_to_distribute(27302662972); + // we'll distribute 5% of (some amount of tokens), so: + graphene::chain::share_type total_amount_to_distribute(27302662972); - my_account_balance_object_index_type db_balances; - graphene::chain::share_type total_bts_balance; + my_account_balance_object_index_type db_balances; + graphene::chain::share_type total_bts_balance; - auto& balance_index = d.get_index_type().indices().get(); - for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end() && balance_iter->asset_type == graphene::chain::asset_id_type(); ++balance_iter) + auto& balance_index = d.get_index_type().indices().get(); + for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end() && balance_iter->asset_type == graphene::chain::asset_id_type(); ++balance_iter) + { + if (balance_iter->balance > 0 && !exclude_account_from_sharedrop(d, balance_iter->owner)) { - if (balance_iter->balance > 0 && !exclude_account_from_sharedrop(d, balance_iter->owner)) - { - total_bts_balance += balance_iter->balance; + total_bts_balance += balance_iter->balance; - my_account_balance_object new_balance_object; - new_balance_object.account_id = balance_iter->owner; - new_balance_object.balance = balance_iter->balance; - db_balances.insert(new_balance_object); + my_account_balance_object new_balance_object; + new_balance_object.account_id = balance_iter->owner; + new_balance_object.balance = balance_iter->balance; + db_balances.insert(new_balance_object); + } + } + + + // account for BTS tied up in market orders + auto limit_order_index = d.get_index_type().indices(); + for (const graphene::chain::limit_order_object& limit_order : limit_order_index) + if (limit_order.amount_for_sale().asset_id == graphene::chain::asset_id_type()) + { + graphene::chain::share_type limit_order_amount = limit_order.amount_for_sale().amount; + if (limit_order_amount > 0 && !exclude_account_from_sharedrop(d, limit_order.seller)) + { + total_bts_balance += limit_order_amount; + + auto my_balance_iter = db_balances.find(limit_order.seller); + if (my_balance_iter == db_balances.end()) + { + my_account_balance_object balance_object; + balance_object.account_id = limit_order.seller; + balance_object.orders = limit_order_amount; + db_balances.insert(balance_object); + } + else + { + db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { + balance_object.orders += limit_order_amount; + }); + } } } - - // account for BTS tied up in market orders - auto limit_order_index = d.get_index_type().indices(); - for (const graphene::chain::limit_order_object& limit_order : limit_order_index) - if (limit_order.amount_for_sale().asset_id == graphene::chain::asset_id_type()) - { - graphene::chain::share_type limit_order_amount = limit_order.amount_for_sale().amount; - if (limit_order_amount > 0 && !exclude_account_from_sharedrop(d, limit_order.seller)) - { - total_bts_balance += limit_order_amount; - - auto my_balance_iter = db_balances.find(limit_order.seller); - if (my_balance_iter == db_balances.end()) - { - my_account_balance_object balance_object; - balance_object.account_id = limit_order.seller; - balance_object.orders = limit_order_amount; - db_balances.insert(balance_object); - } - else - { - db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { - balance_object.orders += limit_order_amount; - }); - } - } - } - - // account for BTS tied up in collateral for SmartCoins - auto call_order_index = d.get_index_type().indices(); - for (const graphene::chain::call_order_object& call_order : call_order_index) - if (call_order.get_collateral().asset_id == graphene::chain::asset_id_type()) - { - graphene::chain::share_type call_order_amount = call_order.get_collateral().amount; - if (call_order_amount > 0 && !exclude_account_from_sharedrop(d, call_order.borrower)) - { - total_bts_balance += call_order_amount; - - auto my_balance_iter = db_balances.find(call_order.borrower); - if (my_balance_iter == db_balances.end()) - { - my_account_balance_object balance_object; - balance_object.account_id = call_order.borrower; - balance_object.collateral = call_order_amount; - db_balances.insert(balance_object); - } - else - { - db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { - balance_object.collateral += call_order_amount; - }); - } - } - } - - // account available-but-unclaimed BTS in vesting balances - auto vesting_balance_index = d.get_index_type().indices(); - for (const graphene::chain::vesting_balance_object& vesting_balance : vesting_balance_index) - if (vesting_balance.balance.asset_id == graphene::chain::asset_id_type()) - { - graphene::chain::share_type vesting_balance_amount = vesting_balance.get_allowed_withdraw(d.head_block_time()).amount; - if (vesting_balance_amount > 0 && !exclude_account_from_sharedrop(d, vesting_balance.owner)) - { - total_bts_balance += vesting_balance_amount; - - auto my_balance_iter = db_balances.find(vesting_balance.owner); - if (my_balance_iter == db_balances.end()) - { - my_account_balance_object balance_object; - balance_object.account_id = vesting_balance.owner; - balance_object.vesting = vesting_balance_amount; - db_balances.insert(balance_object); - } - else - { - db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { - balance_object.vesting += vesting_balance_amount; - }); - } - } - } - - graphene::chain::share_type total_shares_dropped; - - // Now, we assume we're distributing balances to all BTS holders proportionally, figure - // the smallest balance we can distribute and still assign the user a satoshi of the share drop - graphene::chain::share_type effective_total_bts_balance; - auto& by_effective_balance_index = db_balances.get(); - auto balance_iter = by_effective_balance_index.begin(); - for (; balance_iter != by_effective_balance_index.end(); ++balance_iter) + // account for BTS tied up in collateral for SmartCoins + auto call_order_index = d.get_index_type().indices(); + for (const graphene::chain::call_order_object& call_order : call_order_index) + if (call_order.get_collateral().asset_id == graphene::chain::asset_id_type()) { - fc::uint128 share_drop_amount = total_amount_to_distribute.value; - share_drop_amount *= balance_iter->get_effective_balance().value; - share_drop_amount /= total_bts_balance.value; - if (!share_drop_amount.to_uint64()) - break; // balances are decreasing, so every balance after will also round to zero - total_shares_dropped += share_drop_amount.to_uint64(); - effective_total_bts_balance += balance_iter->get_effective_balance(); + graphene::chain::share_type call_order_amount = call_order.get_collateral().amount; + if (call_order_amount > 0 && !exclude_account_from_sharedrop(d, call_order.borrower)) + { + total_bts_balance += call_order_amount; + + auto my_balance_iter = db_balances.find(call_order.borrower); + if (my_balance_iter == db_balances.end()) + { + my_account_balance_object balance_object; + balance_object.account_id = call_order.borrower; + balance_object.collateral = call_order_amount; + db_balances.insert(balance_object); + } + else + { + db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { + balance_object.collateral += call_order_amount; + }); + } + } } - // our iterator is just after the smallest balance we will process, - // walk it backwards towards the larger balances, distributing the sharedrop as we go - graphene::chain::share_type remaining_amount_to_distribute = total_amount_to_distribute; - graphene::chain::share_type bts_balance_remaining = effective_total_bts_balance; + // account available-but-unclaimed BTS in vesting balances + auto vesting_balance_index = d.get_index_type().indices(); + for (const graphene::chain::vesting_balance_object& vesting_balance : vesting_balance_index) + if (vesting_balance.balance.asset_id == graphene::chain::asset_id_type()) + { + graphene::chain::share_type vesting_balance_amount = vesting_balance.get_allowed_withdraw(d.head_block_time()).amount; + if (vesting_balance_amount > 0 && !exclude_account_from_sharedrop(d, vesting_balance.owner)) + { + total_bts_balance += vesting_balance_amount; - do { - --balance_iter; - fc::uint128 share_drop_amount = remaining_amount_to_distribute.value; - share_drop_amount *= balance_iter->get_effective_balance().value; - share_drop_amount /= bts_balance_remaining.value; - graphene::chain::share_type amount_distributed = share_drop_amount.to_uint64(); + auto my_balance_iter = db_balances.find(vesting_balance.owner); + if (my_balance_iter == db_balances.end()) + { + my_account_balance_object balance_object; + balance_object.account_id = vesting_balance.owner; + balance_object.vesting = vesting_balance_amount; + db_balances.insert(balance_object); + } + else + { + db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) { + balance_object.vesting += vesting_balance_amount; + }); + } + } + } - by_effective_balance_index.modify(balance_iter, [&](my_account_balance_object& balance_object) { - balance_object.sharedrop += amount_distributed; - }); + graphene::chain::share_type total_shares_dropped; - remaining_amount_to_distribute -= amount_distributed; - bts_balance_remaining -= balance_iter->get_effective_balance(); - } while (balance_iter != by_effective_balance_index.begin()); - assert(remaining_amount_to_distribute == 0); + // Now, we assume we're distributing balances to all BTS holders proportionally, figure + // the smallest balance we can distribute and still assign the user a satoshi of the share drop + graphene::chain::share_type effective_total_bts_balance; + auto& by_effective_balance_index = db_balances.get(); + auto balance_iter = by_effective_balance_index.begin(); + for (; balance_iter != by_effective_balance_index.end(); ++balance_iter) + { + fc::uint128 share_drop_amount = total_amount_to_distribute.value; + share_drop_amount *= balance_iter->get_effective_balance().value; + share_drop_amount /= total_bts_balance.value; + if (!share_drop_amount.to_uint64()) + break; // balances are decreasing, so every balance after will also round to zero + total_shares_dropped += share_drop_amount.to_uint64(); + effective_total_bts_balance += balance_iter->get_effective_balance(); + } - std::ofstream logfile; - logfile.open(_csvlog_filename); - assert(logfile.is_open()); - logfile << "name,balance+orders+collateral+vesting,balance,orders,collateral,vesting,sharedrop\n"; + // our iterator is just after the smallest balance we will process, + // walk it backwards towards the larger balances, distributing the sharedrop as we go + graphene::chain::share_type remaining_amount_to_distribute = total_amount_to_distribute; + graphene::chain::share_type bts_balance_remaining = effective_total_bts_balance; + + do { + --balance_iter; + fc::uint128 share_drop_amount = remaining_amount_to_distribute.value; + share_drop_amount *= balance_iter->get_effective_balance().value; + share_drop_amount /= bts_balance_remaining.value; + graphene::chain::share_type amount_distributed = share_drop_amount.to_uint64(); + + by_effective_balance_index.modify(balance_iter, [&](my_account_balance_object& balance_object) { + balance_object.sharedrop += amount_distributed; + }); + + remaining_amount_to_distribute -= amount_distributed; + bts_balance_remaining -= balance_iter->get_effective_balance(); + } while (balance_iter != by_effective_balance_index.begin()); + assert(remaining_amount_to_distribute == 0); + + std::ofstream logfile; + logfile.open(_csvlog_filename); + assert(logfile.is_open()); + logfile << "name,balance+orders+collateral+vesting,balance,orders,collateral,vesting,sharedrop\n"; + for (const my_account_balance_object& balance : by_effective_balance_index) + logfile << balance.account_id(d).name << "," << + balance.get_effective_balance().value << "," << + balance.balance.value << "," << + balance.orders.value << "," << + balance.collateral.value << "," << + balance.vesting.value << "," << + balance.sharedrop.value << "\n"; + ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename)); + logfile.close(); + + // remove all balance objects with zero sharedrops + by_effective_balance_index.erase(by_effective_balance_index.lower_bound(0), + by_effective_balance_index.end()); + + // inefficient way of crawling the graph, but we only do it once + std::set already_generated; + for (;;) + { + unsigned accounts_generated_this_round = 0; for (const my_account_balance_object& balance : by_effective_balance_index) - logfile << balance.account_id(d).name << "," << - balance.get_effective_balance().value << "," << - balance.balance.value << "," << - balance.orders.value << "," << - balance.collateral.value << "," << - balance.vesting.value << "," << - balance.sharedrop.value << "\n"; - ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename)); - logfile.close(); - - // remove all balance objects with zero sharedrops - by_effective_balance_index.erase(by_effective_balance_index.lower_bound(0), - by_effective_balance_index.end()); - - // inefficient way of crawling the graph, but we only do it once - std::set already_generated; - for (;;) { - unsigned accounts_generated_this_round = 0; - for (const my_account_balance_object& balance : by_effective_balance_index) + const graphene::chain::account_object& account_obj = balance.account_id(d); + if (already_generated.find(balance.account_id) == already_generated.end()) { - const graphene::chain::account_object& account_obj = balance.account_id(d); - if (already_generated.find(balance.account_id) == already_generated.end()) + graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner; + owner.weight_threshold = account_obj.owner.weight_threshold; + owner.key_auths = account_obj.owner.key_auths; + for (const auto& value : account_obj.owner.account_auths) { - graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner; - owner.weight_threshold = account_obj.owner.weight_threshold; - owner.key_auths = account_obj.owner.key_auths; - for (const auto& value : account_obj.owner.account_auths) - { - owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second)); - db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance - } - owner.key_auths = account_obj.owner.key_auths; - owner.address_auths = account_obj.owner.address_auths; + owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second)); + db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance + } + owner.key_auths = account_obj.owner.key_auths; + owner.address_auths = account_obj.owner.address_auths; - graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority active; - active.weight_threshold = account_obj.active.weight_threshold; - active.key_auths = account_obj.active.key_auths; - for (const auto& value : account_obj.active.account_auths) - { - active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second)); - db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance - } - active.key_auths = account_obj.active.key_auths; - active.address_auths = account_obj.active.address_auths; + graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority active; + active.weight_threshold = account_obj.active.weight_threshold; + active.key_auths = account_obj.active.key_auths; + for (const auto& value : account_obj.active.account_auths) + { + active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second)); + db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance + } + active.key_auths = account_obj.active.key_auths; + active.address_auths = account_obj.active.address_auths; - new_genesis_state.initial_bts_accounts.emplace_back( + new_genesis_state.initial_bts_accounts.emplace_back( graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name), owner, active, balance.sharedrop)); - already_generated.insert(balance.account_id); - ++accounts_generated_this_round; - } + already_generated.insert(balance.account_id); + ++accounts_generated_this_round; } - if (accounts_generated_this_round == 0) - break; } - fc::json::save_to_file(new_genesis_state, _genesis_filename); - ilog("New genesis state written to file ${filename}", ("filename", _genesis_filename)); - } FC_LOG_AND_RETHROW() } + if (accounts_generated_this_round == 0) + break; + } + fc::json::save_to_file(new_genesis_state, _genesis_filename); + ilog("New genesis state written to file ${filename}", ("filename", _genesis_filename)); +} FC_LOG_AND_RETHROW() } void generate_genesis_plugin::plugin_shutdown() { -} \ No newline at end of file +} + diff --git a/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp b/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp index 56f5e2c4..ed1a4207 100644 --- a/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp +++ b/libraries/plugins/generate_genesis/include/graphene/generate_genesis/generate_genesis_plugin.hpp @@ -30,56 +30,56 @@ namespace graphene { namespace generate_genesis_plugin { - class generate_genesis_plugin : public graphene::app::plugin { - public: - ~generate_genesis_plugin() { - } +class generate_genesis_plugin : public graphene::app::plugin { +public: + ~generate_genesis_plugin() { + } - std::string plugin_name()const override; + std::string plugin_name()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description &command_line_options, - boost::program_options::options_description &config_file_options - ) override; + virtual void plugin_set_program_options( + boost::program_options::options_description &command_line_options, + boost::program_options::options_description &config_file_options + ) override; - virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; - virtual void plugin_startup() override; - virtual void plugin_shutdown() override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; + virtual void plugin_startup() override; + virtual void plugin_shutdown() override; - private: - void block_applied(const graphene::chain::signed_block& b); - void generate_snapshot(); +private: + void block_applied(const graphene::chain::signed_block& b); + void generate_snapshot(); - boost::program_options::variables_map _options; + boost::program_options::variables_map _options; - fc::optional _block_to_snapshot; - std::string _genesis_filename; - std::string _csvlog_filename; - }; + fc::optional _block_to_snapshot; + std::string _genesis_filename; + std::string _csvlog_filename; +}; - class my_account_balance_object - { - public: - // constructor copying from base class - //my_account_balance_object(const graphene::chain::account_balance_object& abo) : graphene::chain::account_balance_object(abo) {} - graphene::chain::account_id_type account_id; +class my_account_balance_object +{ +public: + // constructor copying from base class + //my_account_balance_object(const graphene::chain::account_balance_object& abo) : graphene::chain::account_balance_object(abo) {} + graphene::chain::account_id_type account_id; - graphene::chain::share_type balance; - graphene::chain::share_type orders; - graphene::chain::share_type collateral; - graphene::chain::share_type vesting; - graphene::chain::share_type sharedrop; - graphene::chain::share_type get_effective_balance() const { return balance + orders + collateral + vesting; } - }; - using namespace boost::multi_index; - struct by_account{}; - struct by_effective_balance{}; - typedef multi_index_container, - member >, - ordered_non_unique, - const_mem_fun, - std::greater > > > my_account_balance_object_index_type; + graphene::chain::share_type balance; + graphene::chain::share_type orders; + graphene::chain::share_type collateral; + graphene::chain::share_type vesting; + graphene::chain::share_type sharedrop; + graphene::chain::share_type get_effective_balance() const { return balance + orders + collateral + vesting; } +}; +using namespace boost::multi_index; +struct by_account{}; +struct by_effective_balance{}; +typedef multi_index_container, + member >, + ordered_non_unique, + const_mem_fun, + std::greater > > > my_account_balance_object_index_type; - } } //graphene::generate_genesis_plugin \ No newline at end of file +} } //graphene::generate_genesis_plugin diff --git a/libraries/plugins/generate_uia_sharedrop_genesis/CMakeLists.txt b/libraries/plugins/generate_uia_sharedrop_genesis/CMakeLists.txt index 82376d12..d2a50ab6 100644 --- a/libraries/plugins/generate_uia_sharedrop_genesis/CMakeLists.txt +++ b/libraries/plugins/generate_uia_sharedrop_genesis/CMakeLists.txt @@ -1,17 +1,17 @@ file(GLOB HEADERS "include/graphene/generate_uia_sharedrop_genesis/*.hpp") add_library( graphene_generate_uia_sharedrop_genesis - generate_uia_sharedrop_genesis.cpp - ) + generate_uia_sharedrop_genesis.cpp + ) target_link_libraries( graphene_generate_uia_sharedrop_genesis graphene_chain graphene_app graphene_time ) target_include_directories( graphene_generate_uia_sharedrop_genesis - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) install( TARGETS - graphene_generate_uia_sharedrop_genesis + graphene_generate_uia_sharedrop_genesis - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - ) \ No newline at end of file + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) 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 660f8400..5d4b8594 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 @@ -47,11 +47,11 @@ void generate_uia_sharedrop_genesis_plugin::plugin_set_program_options(boost::pr boost::program_options::options_description& config_file_options) { command_line_options.add_options() - ("input-uia-sharedrop-genesis-file", bpo::value()->default_value("genesis.json"), "Genesis file to read") - ("output-uia-sharedrop-genesis-file", bpo::value()->default_value("genesis.json"), "Genesis file to create") - ("output-uia-sharedrop-csvlog-file", bpo::value()->default_value("log.csv"), "CSV log file to create") - ("uia-sharedrop-snapshot-block-number", bpo::value(), "Block number at which to snapshot balances") - ; + ("input-uia-sharedrop-genesis-file", bpo::value()->default_value("genesis.json"), "Genesis file to read") + ("output-uia-sharedrop-genesis-file", bpo::value()->default_value("genesis.json"), "Genesis file to create") + ("output-uia-sharedrop-csvlog-file", bpo::value()->default_value("log.csv"), "CSV log file to create") + ("uia-sharedrop-snapshot-block-number", bpo::value(), "Block number at which to snapshot balances") + ; config_file_options.add(command_line_options); } @@ -105,7 +105,7 @@ void generate_uia_sharedrop_genesis_plugin::block_applied(const graphene::chain: } } -namespace +namespace { // anonymous namespace for file-scoped helper functions @@ -128,53 +128,53 @@ namespace bool is_scam(const std::string& account_name) { static std::set scam_accounts{ - "polonie-wallet", - "polonie-xwallet", - "poloniewallet", - "poloniex-deposit", - "poloniex-wallet", - "poloniexwall-et", - "poloniexwallett", - "poloniexwall-t", - "poloniexwalle", - "poloniex", - "poloneix", - "poloniex1", - "bittrex-deopsit", - "bittrex-deposi", - "bittrex-depositt", - "bittrex-dposit", - "bittrex", - "bittrex-deposits", - "coinbase", - "blocktrade", - "locktrades", - "yun.bts", - "transwiser-walle", - "transwiser-wallets", - "ranswiser-wallet", - "yun.btc", - "pay.coinbase.com", - "pay.bts.com", - "btc38.com", - "yunbi.com", - "coinbase.com", - "ripple.com" + "polonie-wallet", + "polonie-xwallet", + "poloniewallet", + "poloniex-deposit", + "poloniex-wallet", + "poloniexwall-et", + "poloniexwallett", + "poloniexwall-t", + "poloniexwalle", + "poloniex", + "poloneix", + "poloniex1", + "bittrex-deopsit", + "bittrex-deposi", + "bittrex-depositt", + "bittrex-dposit", + "bittrex", + "bittrex-deposits", + "coinbase", + "blocktrade", + "locktrades", + "yun.bts", + "transwiser-walle", + "transwiser-wallets", + "ranswiser-wallet", + "yun.btc", + "pay.coinbase.com", + "pay.bts.com", + "btc38.com", + "yunbi.com", + "coinbase.com", + "ripple.com" }; return scam_accounts.find(account_name) != scam_accounts.end(); - } - + } + bool is_exchange(const std::string& account_name) { static std::set exchange_accounts{ - "poloniexcoldstorage", - "btc38-public-for-bts-cold", - "poloniexwallet", - "btercom", - "yunbi-cold-wallet", - "btc38-btsx-octo-72722", - "bittrex-deposit", - "btc38btsxwithdrawal" + "poloniexcoldstorage", + "btc38-public-for-bts-cold", + "poloniexwallet", + "btercom", + "yunbi-cold-wallet", + "btc38-btsx-octo-72722", + "bittrex-deposit", + "btc38btsxwithdrawal" }; return exchange_accounts.find(account_name) != exchange_accounts.end(); } @@ -283,7 +283,7 @@ void generate_uia_sharedrop_genesis_plugin::generate_snapshot() } - // Generate CSV file of all sharedrops and the balances we used to calculate them + // Generate CSV file of all sharedrops and the balances we used to calculate them std::ofstream csv_log_file; csv_log_file.open(_csvlog_filename); assert(csv_log_file.is_open()); @@ -342,9 +342,9 @@ void generate_uia_sharedrop_genesis_plugin::generate_snapshot() active.address_auths = account_obj.active.address_auths; new_genesis_state.initial_bts_accounts.emplace_back( - graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name), - owner, active, - sharedrop_amount)); + graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name), + owner, active, + sharedrop_amount)); already_generated.insert(account_id); ++accounts_generated_this_round; } @@ -359,3 +359,4 @@ void generate_uia_sharedrop_genesis_plugin::generate_snapshot() void generate_uia_sharedrop_genesis_plugin::plugin_shutdown() { } + diff --git a/libraries/plugins/generate_uia_sharedrop_genesis/include/graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp b/libraries/plugins/generate_uia_sharedrop_genesis/include/graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp index c3bb1e2b..d83595b8 100644 --- a/libraries/plugins/generate_uia_sharedrop_genesis/include/graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp +++ b/libraries/plugins/generate_uia_sharedrop_genesis/include/graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp @@ -30,50 +30,50 @@ namespace graphene { namespace generate_uia_sharedrop_genesis { - class generate_uia_sharedrop_genesis_plugin : public graphene::app::plugin { - public: - ~generate_uia_sharedrop_genesis_plugin() { - } +class generate_uia_sharedrop_genesis_plugin : public graphene::app::plugin { +public: + ~generate_uia_sharedrop_genesis_plugin() { + } - std::string plugin_name()const override; + std::string plugin_name()const override; - virtual void plugin_set_program_options( - boost::program_options::options_description &command_line_options, - boost::program_options::options_description &config_file_options - ) override; + virtual void plugin_set_program_options( + boost::program_options::options_description &command_line_options, + boost::program_options::options_description &config_file_options + ) override; - virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; - virtual void plugin_startup() override; - virtual void plugin_shutdown() override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; + virtual void plugin_startup() override; + virtual void plugin_shutdown() override; - private: - void block_applied(const graphene::chain::signed_block& b); - void generate_snapshot(); +private: + void block_applied(const graphene::chain::signed_block& b); + void generate_snapshot(); - boost::program_options::variables_map _options; + boost::program_options::variables_map _options; - fc::optional _block_to_snapshot; - std::string _input_genesis_filename; - std::string _output_genesis_filename; - std::string _csvlog_filename; - }; + fc::optional _block_to_snapshot; + std::string _input_genesis_filename; + std::string _output_genesis_filename; + std::string _csvlog_filename; +}; - class uia_sharedrop_balance_object - { - public: - graphene::chain::account_id_type account_id; +class uia_sharedrop_balance_object +{ +public: + graphene::chain::account_id_type account_id; - graphene::chain::share_type genesis; - graphene::chain::share_type balance; - graphene::chain::share_type orders; + graphene::chain::share_type genesis; + graphene::chain::share_type balance; + graphene::chain::share_type orders; - graphene::chain::share_type sharedrop; - }; + graphene::chain::share_type sharedrop; +}; - using namespace boost::multi_index; - struct by_account{}; - typedef multi_index_container, - member > > > uia_sharedrop_balance_object_index_type; +using namespace boost::multi_index; +struct by_account{}; +typedef multi_index_container, + member > > > uia_sharedrop_balance_object_index_type; - } } //graphene::generate_uia_sharedrop_genesis_plugin \ No newline at end of file +} } //graphene::generate_uia_sharedrop_genesis_plugin diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 828d3a89..0ac0b88a 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -583,7 +583,7 @@ class wallet_api /** * @param role - active | owner | memo */ - pair get_private_key_from_password( string password, string account, string role )const; + pair get_private_key_from_password( string account, string role, string password )const; /** Converts a signed_transaction in JSON form to its binary representation. * @@ -1348,38 +1348,39 @@ class wallet_api string witness, bool approve, bool broadcast = false); - /** Change your witness votes. + + /** Change your witness votes. * - * An account can publish a list of all witnesses 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 witnesses from this list - * in one call. When you are changing your vote on several witnesses, this - * may be easier than multiple `vote_for_witness` and - * `set_desired_witness_and_committee_member_count` calls. - * - * @note you cannot vote against a witness, you can only vote for the witness - * or not vote for the witness. - * - * @param voting_account the name or id of the account who is voting with their shares - * @param witnesses_to_approve the names or ids of the witnesses owner accounts you wish - * to approve (these will be added to the list of witnesses - * you currently approve). This list can be empty. - * @param witnesses_to_reject the names or ids of the witnesses owner accounts you wish - * to reject (these will be removed fromthe list of witnesses - * you currently approve). This list can be empty. - * @param desired_number_of_witnesses the number of witnesses you believe the network - * should have. You must vote for at least this many - * witnesses. You can set this to 0 to abstain from - * voting on the number of witnesses. - * @param broadcast true if you wish to broadcast the transaction - * @return the signed transaction changing your vote for the given witnesses - */ - signed_transaction update_witness_votes(string voting_account, - std::vector witnesses_to_approve, - std::vector witnesses_to_reject, - uint16_t desired_number_of_witnesses, - bool broadcast = false); + * An account can publish a list of all witnesses 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 witnesses from this list + * in one call. When you are changing your vote on several witnesses, this + * may be easier than multiple `vote_for_witness` and + * `set_desired_witness_and_committee_member_count` calls. + * + * @note you cannot vote against a witness, you can only vote for the witness + * or not vote for the witness. + * + * @param voting_account the name or id of the account who is voting with their shares + * @param witnesses_to_approve the names or ids of the witnesses owner accounts you wish + * to approve (these will be added to the list of witnesses + * you currently approve). This list can be empty. + * @param witnesses_to_reject the names or ids of the witnesses owner accounts you wish + * to reject (these will be removed fromthe list of witnesses + * you currently approve). This list can be empty. + * @param desired_number_of_witnesses the number of witnesses you believe the network + * should have. You must vote for at least this many + * witnesses. You can set this to 0 to abstain from + * voting on the number of witnesses. + * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction changing your vote for the given witnesses + */ + signed_transaction update_witness_votes(string voting_account, + std::vector witnesses_to_approve, + std::vector witnesses_to_reject, + uint16_t desired_number_of_witnesses, + bool broadcast = false); /** Set the voting proxy for an account. * * If a user does not wish to take an active part in voting, they can choose diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 555b0307..8f24192a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -91,4627 +91,4627 @@ namespace graphene { namespace wallet { - namespace detail { +namespace detail { - struct operation_result_printer - { - public: - operation_result_printer( const wallet_api_impl& w ) - : _wallet(w) {} - const wallet_api_impl& _wallet; - typedef std::string result_type; +struct operation_result_printer +{ +public: + operation_result_printer( const wallet_api_impl& w ) + : _wallet(w) {} + const wallet_api_impl& _wallet; + typedef std::string result_type; - std::string operator()(const void_result& x) const; - std::string operator()(const object_id_type& oid); - std::string operator()(const asset& a); - }; + std::string operator()(const void_result& x) const; + std::string operator()(const object_id_type& oid); + std::string operator()(const asset& a); +}; // BLOCK TRX OP VOP - struct operation_printer +struct operation_printer +{ +private: + ostream& out; + const wallet_api_impl& wallet; + operation_result result; + + std::string fee(const asset& a) const; + +public: + operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) + : out(out), + wallet(wallet), + result(r) + {} + typedef std::string result_type; + + template + std::string operator()(const T& op)const; + + std::string operator()(const transfer_operation& op)const; + std::string operator()(const transfer_from_blind_operation& op)const; + std::string operator()(const transfer_to_blind_operation& op)const; + std::string operator()(const account_create_operation& op)const; + std::string operator()(const account_update_operation& op)const; + std::string operator()(const asset_create_operation& op)const; + std::string operator()(const asset_dividend_distribution_operation& op)const; + std::string operator()(const tournament_payout_operation& op)const; +}; + +template +optional maybe_id( const string& name_or_id ) +{ + if( std::isdigit( name_or_id.front() ) ) + { + try + { + return fc::variant(name_or_id).as(); + } + catch (const fc::exception&) + { + } + } + return optional(); +} + +string address_to_shorthash( const address& addr ) +{ + uint32_t x = addr.addr._hash[0]; + static const char hd[] = "0123456789abcdef"; + string result; + + result += hd[(x >> 0x1c) & 0x0f]; + result += hd[(x >> 0x18) & 0x0f]; + result += hd[(x >> 0x14) & 0x0f]; + result += hd[(x >> 0x10) & 0x0f]; + result += hd[(x >> 0x0c) & 0x0f]; + result += hd[(x >> 0x08) & 0x0f]; + result += hd[(x >> 0x04) & 0x0f]; + result += hd[(x ) & 0x0f]; + + return result; +} + +fc::ecc::private_key derive_private_key( const std::string& prefix_string, + int sequence_number ) +{ + std::string sequence_string = std::to_string(sequence_number); + fc::sha512 h = fc::sha512::hash(prefix_string + " " + sequence_string); + fc::ecc::private_key derived_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h)); + return derived_key; +} + +string normalize_brain_key( string s ) +{ + size_t i = 0, n = s.length(); + std::string result; + char c; + result.reserve( n ); + + bool preceded_by_whitespace = false; + bool non_empty = false; + while( i < n ) + { + c = s[i++]; + switch( c ) + { + case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': + preceded_by_whitespace = true; + continue; + + case 'a': c = 'A'; break; + case 'b': c = 'B'; break; + case 'c': c = 'C'; break; + case 'd': c = 'D'; break; + case 'e': c = 'E'; break; + case 'f': c = 'F'; break; + case 'g': c = 'G'; break; + case 'h': c = 'H'; break; + case 'i': c = 'I'; break; + case 'j': c = 'J'; break; + case 'k': c = 'K'; break; + case 'l': c = 'L'; break; + case 'm': c = 'M'; break; + case 'n': c = 'N'; break; + case 'o': c = 'O'; break; + case 'p': c = 'P'; break; + case 'q': c = 'Q'; break; + case 'r': c = 'R'; break; + case 's': c = 'S'; break; + case 't': c = 'T'; break; + case 'u': c = 'U'; break; + case 'v': c = 'V'; break; + case 'w': c = 'W'; break; + case 'x': c = 'X'; break; + case 'y': c = 'Y'; break; + case 'z': c = 'Z'; break; + + default: + break; + } + if( preceded_by_whitespace && non_empty ) + result.push_back(' '); + result.push_back(c); + preceded_by_whitespace = false; + non_empty = true; + } + return result; +} + +struct op_prototype_visitor +{ + typedef void result_type; + + int t = 0; + flat_map< std::string, operation >& name2op; + + op_prototype_visitor( + int _t, + flat_map< std::string, operation >& _prototype_ops + ):t(_t), name2op(_prototype_ops) {} + + template + result_type operator()( const Type& op )const + { + string name = fc::get_typename::name(); + size_t p = name.rfind(':'); + if( p != string::npos ) + name = name.substr( p+1 ); + name2op[ name ] = Type(); + } +}; + +class wallet_api_impl +{ +public: + api_documentation method_documentation; +private: + void claim_registered_account(const account_object& account) + { + auto it = _wallet.pending_account_registrations.find( account.name ); + FC_ASSERT( it != _wallet.pending_account_registrations.end() ); + for (const std::string& wif_key : it->second) + if( !import_key( account.name, wif_key ) ) + { + // somebody else beat our pending registration, there is + // nothing we can do except log it and move on + elog( "account ${name} registered by someone else first!", + ("name", account.name) ); + // might as well remove it from pending regs, + // because there is now no way this registration + // can become valid (even in the extremely rare + // possibility of migrating to a fork where the + // name is available, the user can always + // manually re-register) + } + _wallet.pending_account_registrations.erase( it ); + } + + // after a witness registration succeeds, this saves the private key in the wallet permanently + // + void claim_registered_witness(const std::string& witness_name) + { + auto iter = _wallet.pending_witness_registrations.find(witness_name); + FC_ASSERT(iter != _wallet.pending_witness_registrations.end()); + std::string wif_key = iter->second; + + // get the list key id this key is registered with in the chain + fc::optional witness_private_key = wif_to_key(wif_key); + FC_ASSERT(witness_private_key); + + auto pub_key = witness_private_key->get_public_key(); + _keys[pub_key] = wif_key; + _wallet.pending_witness_registrations.erase(iter); + } + + fc::mutex _resync_mutex; + void resync() + { + fc::scoped_lock lock(_resync_mutex); + // this method is used to update wallet_data annotations + // e.g. wallet has been restarted and was not notified + // of events while it was down + // + // everything that is done "incremental style" when a push + // notification is received, should also be done here + // "batch style" by querying the blockchain + + if( !_wallet.pending_account_registrations.empty() ) + { + // make a vector of the account names pending registration + std::vector pending_account_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_account_registrations)); + + // look those up on the blockchain + std::vector> + pending_account_objects = _remote_db->lookup_account_names( pending_account_names ); + + // if any of them exist, claim them + for( const fc::optional& optional_account : pending_account_objects ) + if( optional_account ) + claim_registered_account(*optional_account); + } + + if (!_wallet.pending_witness_registrations.empty()) + { + // make a vector of the owner accounts for witnesses pending registration + std::vector pending_witness_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_witness_registrations)); + + // look up the owners on the blockchain + std::vector> owner_account_objects = _remote_db->lookup_account_names(pending_witness_names); + + // if any of them have registered witnesses, claim them + for( const fc::optional& optional_account : owner_account_objects ) + if (optional_account) { - private: - ostream& out; - const wallet_api_impl& wallet; - operation_result result; + fc::optional witness_obj = _remote_db->get_witness_by_account(optional_account->id); + if (witness_obj) + claim_registered_witness(optional_account->name); + } + } + } - std::string fee(const asset& a) const; + // return true if any of my_accounts are players in this tournament + bool tournament_is_relevant_to_my_accounts(const tournament_object& tournament_obj) + { + tournament_details_object tournament_details = get_object(tournament_obj.tournament_details_id); + for (const account_object& account_obj : _wallet.my_accounts) + if (tournament_details.registered_players.find(account_obj.id) != tournament_details.registered_players.end()) + return true; + return false; + } - public: - operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) - : out(out), - wallet(wallet), - result(r) - {} - typedef std::string result_type; - - template - std::string operator()(const T& op)const; - - std::string operator()(const transfer_operation& op)const; - std::string operator()(const transfer_from_blind_operation& op)const; - std::string operator()(const transfer_to_blind_operation& op)const; - std::string operator()(const account_create_operation& op)const; - std::string operator()(const account_update_operation& op)const; - std::string operator()(const asset_create_operation& op)const; - std::string operator()(const asset_dividend_distribution_operation& op)const; - std::string operator()(const tournament_payout_operation& op)const; - }; - - template - optional maybe_id( const string& name_or_id ) + fc::mutex _subscribed_object_changed_mutex; + void subscribed_object_changed(const variant& changed_objects_variant) + { + fc::scoped_lock lock(_resync_mutex); + fc::variants changed_objects = changed_objects_variant.get_array(); + for (const variant& changed_object_variant : changed_objects) + { + // changed_object_variant is either the object, or just the id if the object was removed + if (changed_object_variant.is_object()) + { + try { - if( std::isdigit( name_or_id.front() ) ) + object_id_type id = changed_object_variant["id"].as(); + tournament_object current_tournament_obj = changed_object_variant.as(); + auto tournament_cache_iter = tournament_cache.find(id); + if (tournament_cache_iter != tournament_cache.end()) { - try - { - return fc::variant(name_or_id).as(); - } - catch (const fc::exception&) + const tournament_object& cached_tournament_obj = *tournament_cache_iter; + if (cached_tournament_obj.get_state() != current_tournament_obj.get_state()) { + ilog("Tournament ${id} changed state from ${old} to ${new}", + ("id", id) + ("old", cached_tournament_obj.get_state()) + ("new", current_tournament_obj.get_state())); + if (current_tournament_obj.get_state() == tournament_state::in_progress) + monitor_matches_in_tournament(current_tournament_obj); } + tournament_cache.modify(tournament_cache_iter, [&](tournament_object& obj) { obj = current_tournament_obj; }); } - return optional(); - } - - string address_to_shorthash( const address& addr ) - { - uint32_t x = addr.addr._hash[0]; - static const char hd[] = "0123456789abcdef"; - string result; - - result += hd[(x >> 0x1c) & 0x0f]; - result += hd[(x >> 0x18) & 0x0f]; - result += hd[(x >> 0x14) & 0x0f]; - result += hd[(x >> 0x10) & 0x0f]; - result += hd[(x >> 0x0c) & 0x0f]; - result += hd[(x >> 0x08) & 0x0f]; - result += hd[(x >> 0x04) & 0x0f]; - result += hd[(x ) & 0x0f]; - - return result; - } - - fc::ecc::private_key derive_private_key( const std::string& prefix_string, - int sequence_number ) - { - std::string sequence_string = std::to_string(sequence_number); - fc::sha512 h = fc::sha512::hash(prefix_string + " " + sequence_string); - fc::ecc::private_key derived_key = fc::ecc::private_key::regenerate(fc::sha256::hash(h)); - return derived_key; - } - - string normalize_brain_key( string s ) - { - size_t i = 0, n = s.length(); - std::string result; - char c; - result.reserve( n ); - - bool preceded_by_whitespace = false; - bool non_empty = false; - while( i < n ) + else if (tournament_is_relevant_to_my_accounts(current_tournament_obj)) { - c = s[i++]; - switch( c ) - { - case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': - preceded_by_whitespace = true; - continue; - - case 'a': c = 'A'; break; - case 'b': c = 'B'; break; - case 'c': c = 'C'; break; - case 'd': c = 'D'; break; - case 'e': c = 'E'; break; - case 'f': c = 'F'; break; - case 'g': c = 'G'; break; - case 'h': c = 'H'; break; - case 'i': c = 'I'; break; - case 'j': c = 'J'; break; - case 'k': c = 'K'; break; - case 'l': c = 'L'; break; - case 'm': c = 'M'; break; - case 'n': c = 'N'; break; - case 'o': c = 'O'; break; - case 'p': c = 'P'; break; - case 'q': c = 'Q'; break; - case 'r': c = 'R'; break; - case 's': c = 'S'; break; - case 't': c = 'T'; break; - case 'u': c = 'U'; break; - case 'v': c = 'V'; break; - case 'w': c = 'W'; break; - case 'x': c = 'X'; break; - case 'y': c = 'Y'; break; - case 'z': c = 'Z'; break; - - default: - break; - } - if( preceded_by_whitespace && non_empty ) - result.push_back(' '); - result.push_back(c); - preceded_by_whitespace = false; - non_empty = true; + ilog ("We were just notified about an in-progress tournament ${id} relevant to our accounts", + ("id", current_tournament_obj.id)); + tournament_cache.insert(current_tournament_obj); + if (current_tournament_obj.get_state() == tournament_state::in_progress) + monitor_matches_in_tournament(current_tournament_obj); } - return result; + continue; } - - struct op_prototype_visitor + catch (const fc::exception& e) { - typedef void result_type; - - int t = 0; - flat_map< std::string, operation >& name2op; - - op_prototype_visitor( - int _t, - flat_map< std::string, operation >& _prototype_ops - ):t(_t), name2op(_prototype_ops) {} - - template - result_type operator()( const Type& op )const - { - string name = fc::get_typename::name(); - size_t p = name.rfind(':'); - if( p != string::npos ) - name = name.substr( p+1 ); - name2op[ name ] = Type(); - } - }; - - class wallet_api_impl + // idump((e)); + } + try { - public: - api_documentation method_documentation; - private: - void claim_registered_account(const account_object& account) - { - auto it = _wallet.pending_account_registrations.find( account.name ); - FC_ASSERT( it != _wallet.pending_account_registrations.end() ); - for (const std::string& wif_key : it->second) - if( !import_key( account.name, wif_key ) ) - { - // somebody else beat our pending registration, there is - // nothing we can do except log it and move on - elog( "account ${name} registered by someone else first!", - ("name", account.name) ); - // might as well remove it from pending regs, - // because there is now no way this registration - // can become valid (even in the extremely rare - // possibility of migrating to a fork where the - // name is available, the user can always - // manually re-register) - } - _wallet.pending_account_registrations.erase( it ); - } + object_id_type id = changed_object_variant["id"].as(); + match_object current_match_obj = changed_object_variant.as(); + auto match_cache_iter = match_cache.find(id); + if (match_cache_iter != match_cache.end()) + { + const match_object& cached_match_obj = *match_cache_iter; + if (cached_match_obj.get_state() != current_match_obj.get_state() || + cached_match_obj.games.size() != current_match_obj.games.size()) + { + ilog("match ${id} changed state from ${old} to ${new}", + ("id", id) + ("old", cached_match_obj.get_state()) + ("new", current_match_obj.get_state())); + match_in_new_state(current_match_obj); + } + match_cache.modify(match_cache_iter, [&](match_object& obj) { obj = current_match_obj; }); + } + continue; + } + catch (const fc::exception& e) + { + // idump((e)); + } + try + { + object_id_type id = changed_object_variant["id"].as(); + game_object current_game_obj = changed_object_variant.as(); + auto game_cache_iter = game_cache.find(id); + if (game_cache_iter != game_cache.end()) + { + const game_object& cached_game_obj = *game_cache_iter; + if (cached_game_obj.get_state() != current_game_obj.get_state()) + { + ilog("game ${id} changed state from ${old} to ${new}", + ("id", id) + ("old", cached_game_obj.get_state()) + ("new", current_game_obj.get_state())); + game_in_new_state(current_game_obj); + } + game_cache.modify(game_cache_iter, [&](game_object& obj) { obj = current_game_obj; }); + } + continue; + } + catch (const fc::exception& e) + { + // idump((e)); + } + try + { + object_id_type id = changed_object_variant["id"].as(); + if (_wallet.my_accounts.find(id) != _wallet.my_accounts.end()) + { + account_object account = changed_object_variant.as(); + _wallet.update_account(account); + } + continue; + } + catch (const fc::exception& e) + { + // idump((e)); + } + } + } + } - // after a witness registration succeeds, this saves the private key in the wallet permanently - // - void claim_registered_witness(const std::string& witness_name) - { - auto iter = _wallet.pending_witness_registrations.find(witness_name); - FC_ASSERT(iter != _wallet.pending_witness_registrations.end()); - std::string wif_key = iter->second; - - // get the list key id this key is registered with in the chain - fc::optional witness_private_key = wif_to_key(wif_key); - FC_ASSERT(witness_private_key); - - auto pub_key = witness_private_key->get_public_key(); - _keys[pub_key] = wif_key; - _wallet.pending_witness_registrations.erase(iter); - } - - fc::mutex _resync_mutex; - void resync() - { - fc::scoped_lock lock(_resync_mutex); - // this method is used to update wallet_data annotations - // e.g. wallet has been restarted and was not notified - // of events while it was down - // - // everything that is done "incremental style" when a push - // notification is received, should also be done here - // "batch style" by querying the blockchain - - if( !_wallet.pending_account_registrations.empty() ) - { - // make a vector of the account names pending registration - std::vector pending_account_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_account_registrations)); - - // look those up on the blockchain - std::vector> - pending_account_objects = _remote_db->lookup_account_names( pending_account_names ); - - // if any of them exist, claim them - for( const fc::optional& optional_account : pending_account_objects ) - if( optional_account ) - claim_registered_account(*optional_account); - } - - if (!_wallet.pending_witness_registrations.empty()) - { - // make a vector of the owner accounts for witnesses pending registration - std::vector pending_witness_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_witness_registrations)); - - // look up the owners on the blockchain - std::vector> owner_account_objects = _remote_db->lookup_account_names(pending_witness_names); - - // if any of them have registered witnesses, claim them - for( const fc::optional& optional_account : owner_account_objects ) - if (optional_account) - { - fc::optional witness_obj = _remote_db->get_witness_by_account(optional_account->id); - if (witness_obj) - claim_registered_witness(optional_account->name); - } - } - } - - // return true if any of my_accounts are players in this tournament - bool tournament_is_relevant_to_my_accounts(const tournament_object& tournament_obj) - { - tournament_details_object tournament_details = get_object(tournament_obj.tournament_details_id); - for (const account_object& account_obj : _wallet.my_accounts) - if (tournament_details.registered_players.find(account_obj.id) != tournament_details.registered_players.end()) - return true; - return false; - } - - fc::mutex _subscribed_object_changed_mutex; - void subscribed_object_changed(const variant& changed_objects_variant) - { - fc::scoped_lock lock(_resync_mutex); - fc::variants changed_objects = changed_objects_variant.get_array(); - for (const variant& changed_object_variant : changed_objects) - { - // changed_object_variant is either the object, or just the id if the object was removed - if (changed_object_variant.is_object()) - { - try - { - object_id_type id = changed_object_variant["id"].as(); - tournament_object current_tournament_obj = changed_object_variant.as(); - auto tournament_cache_iter = tournament_cache.find(id); - if (tournament_cache_iter != tournament_cache.end()) - { - const tournament_object& cached_tournament_obj = *tournament_cache_iter; - if (cached_tournament_obj.get_state() != current_tournament_obj.get_state()) - { - ilog("Tournament ${id} changed state from ${old} to ${new}", - ("id", id) - ("old", cached_tournament_obj.get_state()) - ("new", current_tournament_obj.get_state())); - if (current_tournament_obj.get_state() == tournament_state::in_progress) - monitor_matches_in_tournament(current_tournament_obj); - } - tournament_cache.modify(tournament_cache_iter, [&](tournament_object& obj) { obj = current_tournament_obj; }); - } - else if (tournament_is_relevant_to_my_accounts(current_tournament_obj)) - { - ilog ("We were just notified about an in-progress tournament ${id} relevant to our accounts", - ("id", current_tournament_obj.id)); - tournament_cache.insert(current_tournament_obj); - if (current_tournament_obj.get_state() == tournament_state::in_progress) - monitor_matches_in_tournament(current_tournament_obj); - } - continue; - } - catch (const fc::exception& e) - { - // idump((e)); - } - try - { - object_id_type id = changed_object_variant["id"].as(); - match_object current_match_obj = changed_object_variant.as(); - auto match_cache_iter = match_cache.find(id); - if (match_cache_iter != match_cache.end()) - { - const match_object& cached_match_obj = *match_cache_iter; - if (cached_match_obj.get_state() != current_match_obj.get_state() || - cached_match_obj.games.size() != current_match_obj.games.size()) - { - ilog("match ${id} changed state from ${old} to ${new}", - ("id", id) - ("old", cached_match_obj.get_state()) - ("new", current_match_obj.get_state())); - match_in_new_state(current_match_obj); - } - match_cache.modify(match_cache_iter, [&](match_object& obj) { obj = current_match_obj; }); - } - continue; - } - catch (const fc::exception& e) - { - // idump((e)); - } - try - { - object_id_type id = changed_object_variant["id"].as(); - game_object current_game_obj = changed_object_variant.as(); - auto game_cache_iter = game_cache.find(id); - if (game_cache_iter != game_cache.end()) - { - const game_object& cached_game_obj = *game_cache_iter; - if (cached_game_obj.get_state() != current_game_obj.get_state()) - { - ilog("game ${id} changed state from ${old} to ${new}", - ("id", id) - ("old", cached_game_obj.get_state()) - ("new", current_game_obj.get_state())); - game_in_new_state(current_game_obj); - } - game_cache.modify(game_cache_iter, [&](game_object& obj) { obj = current_game_obj; }); - } - continue; - } - catch (const fc::exception& e) - { - // idump((e)); - } - try - { - object_id_type id = changed_object_variant["id"].as(); - if (_wallet.my_accounts.find(id) != _wallet.my_accounts.end()) - { - account_object account = changed_object_variant.as(); - _wallet.update_account(account); - } - continue; - } - catch (const fc::exception& e) - { - // idump((e)); - } - } - } - } - - void enable_umask_protection() - { + void enable_umask_protection() + { #ifdef __unix__ - _old_umask = umask( S_IRWXG | S_IRWXO ); + _old_umask = umask( S_IRWXG | S_IRWXO ); #endif - } + } - void disable_umask_protection() - { + void disable_umask_protection() + { #ifdef __unix__ - umask( _old_umask ); + umask( _old_umask ); #endif - } + } - void init_prototype_ops() - { - operation op; - for( int t=0; t _builder_transactions; + map _builder_transactions; - // if the user executes the same command twice in quick succession, - // we might generate the same transaction id, and cause the second - // transaction to be rejected. This can be avoided by altering the - // second transaction slightly (bumping up the expiration time by - // a second). Keep track of recent transaction ids we've generated - // so we can know if we need to do this - struct recently_generated_transaction_record - { - fc::time_point_sec generation_time; - graphene::chain::transaction_id_type transaction_id; - }; - struct timestamp_index{}; - typedef boost::multi_index_container, - std::hash >, - boost::multi_index::ordered_non_unique, - boost::multi_index::member > > > recently_generated_transaction_set_type; - recently_generated_transaction_set_type _recently_generated_transactions; + // if the user executes the same command twice in quick succession, + // we might generate the same transaction id, and cause the second + // transaction to be rejected. This can be avoided by altering the + // second transaction slightly (bumping up the expiration time by + // a second). Keep track of recent transaction ids we've generated + // so we can know if we need to do this + struct recently_generated_transaction_record + { + fc::time_point_sec generation_time; + graphene::chain::transaction_id_type transaction_id; + }; + struct timestamp_index{}; + typedef boost::multi_index_container, + std::hash >, + boost::multi_index::ordered_non_unique, + boost::multi_index::member > > > recently_generated_transaction_set_type; + recently_generated_transaction_set_type _recently_generated_transactions; - public: - wallet_api& self; - wallet_api_impl( wallet_api& s, const wallet_data& initial_data, fc::api rapi ) - : self(s), - _chain_id(initial_data.chain_id), - _remote_api(rapi), - _remote_db(rapi->database()), - _remote_net_broadcast(rapi->network_broadcast()), - _remote_hist(rapi->history()) - { - chain_id_type remote_chain_id = _remote_db->get_chain_id(); - if( remote_chain_id != _chain_id ) - { - FC_THROW( "Remote server gave us an unexpected chain_id", - ("remote_chain_id", remote_chain_id) - ("chain_id", _chain_id) ); - } - init_prototype_ops(); +public: + wallet_api& self; + wallet_api_impl( wallet_api& s, const wallet_data& initial_data, fc::api rapi ) + : self(s), + _chain_id(initial_data.chain_id), + _remote_api(rapi), + _remote_db(rapi->database()), + _remote_net_broadcast(rapi->network_broadcast()), + _remote_hist(rapi->history()) + { + chain_id_type remote_chain_id = _remote_db->get_chain_id(); + if( remote_chain_id != _chain_id ) + { + FC_THROW( "Remote server gave us an unexpected chain_id", + ("remote_chain_id", remote_chain_id) + ("chain_id", _chain_id) ); + } + init_prototype_ops(); - _remote_db->set_block_applied_callback( [this](const variant& block_id ) - { - on_block_applied( block_id ); - } ); + _remote_db->set_block_applied_callback( [this](const variant& block_id ) + { + on_block_applied( block_id ); + } ); - _remote_db->set_subscribe_callback( [this](const variant& object ) - { - on_subscribe_callback( object ); - }, false ); + _remote_db->set_subscribe_callback( [this](const variant& object ) + { + on_subscribe_callback( object ); + }, false ); - _wallet.chain_id = _chain_id; - _wallet.ws_server = initial_data.ws_server; - _wallet.ws_user = initial_data.ws_user; - _wallet.ws_password = initial_data.ws_password; + _wallet.chain_id = _chain_id; + _wallet.ws_server = initial_data.ws_server; + _wallet.ws_user = initial_data.ws_user; + _wallet.ws_password = initial_data.ws_password; - } - virtual ~wallet_api_impl() - { - try - { - _remote_db->cancel_all_subscriptions(); - } - catch (const fc::exception& e) - { - // Right now the wallet_api has no way of knowing if the connection to the - // witness has already disconnected (via the witness node exiting first). - // If it has exited, cancel_all_subscriptsions() will throw and there's - // nothing we can do about it. - // dlog("Caught exception ${e} while canceling database subscriptions", ("e", e)); - } - } + } + virtual ~wallet_api_impl() + { + try + { + _remote_db->cancel_all_subscriptions(); + } + catch (const fc::exception& e) + { + // Right now the wallet_api has no way of knowing if the connection to the + // witness has already disconnected (via the witness node exiting first). + // If it has exited, cancel_all_subscriptsions() will throw and there's + // nothing we can do about it. + // dlog("Caught exception ${e} while canceling database subscriptions", ("e", e)); + } + } - void encrypt_keys() - { - if( !is_locked() ) - { - plain_keys data; - data.keys = _keys; - data.checksum = _checksum; - auto plain_txt = fc::raw::pack(data); - _wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt ); - } - } + void encrypt_keys() + { + if( !is_locked() ) + { + plain_keys data; + data.keys = _keys; + data.checksum = _checksum; + auto plain_txt = fc::raw::pack(data); + _wallet.cipher_keys = fc::aes_encrypt( data.checksum, plain_txt ); + } + } - void on_block_applied( const variant& block_id ) - { - fc::async([this]{resync();}, "Resync after block"); - } + void on_block_applied( const variant& block_id ) + { + fc::async([this]{resync();}, "Resync after block"); + } - void on_subscribe_callback( const variant& object ) - { - //idump((object)); - fc::async([this, object]{subscribed_object_changed(object);}, "Object changed"); - } + void on_subscribe_callback( const variant& object ) + { + //idump((object)); + fc::async([this, object]{subscribed_object_changed(object);}, "Object changed"); + } - bool copy_wallet_file( string destination_filename ) - { - fc::path src_path = get_wallet_filename(); - if( !fc::exists( src_path ) ) - return false; - fc::path dest_path = destination_filename + _wallet_filename_extension; - int suffix = 0; - while( fc::exists(dest_path) ) - { - ++suffix; - dest_path = destination_filename + "-" + to_string( suffix ) + _wallet_filename_extension; - } - wlog( "backing up wallet ${src} to ${dest}", - ("src", src_path) - ("dest", dest_path) ); + bool copy_wallet_file( string destination_filename ) + { + fc::path src_path = get_wallet_filename(); + if( !fc::exists( src_path ) ) + return false; + fc::path dest_path = destination_filename + _wallet_filename_extension; + int suffix = 0; + while( fc::exists(dest_path) ) + { + ++suffix; + dest_path = destination_filename + "-" + to_string( suffix ) + _wallet_filename_extension; + } + wlog( "backing up wallet ${src} to ${dest}", + ("src", src_path) + ("dest", dest_path) ); - fc::path dest_parent = fc::absolute(dest_path).parent_path(); - try - { - enable_umask_protection(); - if( !fc::exists( dest_parent ) ) - fc::create_directories( dest_parent ); - fc::copy( src_path, dest_path ); - disable_umask_protection(); - } - catch(...) - { - disable_umask_protection(); - throw; - } - return true; - } + fc::path dest_parent = fc::absolute(dest_path).parent_path(); + try + { + enable_umask_protection(); + if( !fc::exists( dest_parent ) ) + fc::create_directories( dest_parent ); + fc::copy( src_path, dest_path ); + disable_umask_protection(); + } + catch(...) + { + disable_umask_protection(); + throw; + } + return true; + } - bool is_locked()const - { - return _checksum == fc::sha512(); - } + bool is_locked()const + { + return _checksum == fc::sha512(); + } - template - T get_object(object_id id)const - { - auto ob = _remote_db->get_objects({id}).front(); - return ob.template as(); - } + template + T get_object(object_id id)const + { + auto ob = _remote_db->get_objects({id}).front(); + return ob.template as(); + } - void set_operation_fees( signed_transaction& tx, const fee_schedule& s ) - { - for( auto& op : tx.operations ) - s.set_fee(op); - } + void set_operation_fees( signed_transaction& tx, const fee_schedule& s ) + { + for( auto& op : tx.operations ) + s.set_fee(op); + } - variant info() const - { - auto chain_props = get_chain_properties(); - auto global_props = get_global_properties(); - auto dynamic_props = get_dynamic_global_properties(); - fc::mutable_variant_object result; - result["head_block_num"] = dynamic_props.head_block_number; - result["head_block_id"] = dynamic_props.head_block_id; - result["head_block_age"] = fc::get_approximate_relative_time_string(dynamic_props.time, - time_point_sec(time_point::now()), - " old"); - result["next_maintenance_time"] = fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time); - result["chain_id"] = chain_props.chain_id; - result["participation"] = (100*dynamic_props.recent_slots_filled.popcount()) / 128.0; - result["active_witnesses"] = global_props.active_witnesses; - result["active_committee_members"] = global_props.active_committee_members; - result["entropy"] = dynamic_props.random; - return result; - } + variant info() const + { + auto chain_props = get_chain_properties(); + auto global_props = get_global_properties(); + auto dynamic_props = get_dynamic_global_properties(); + fc::mutable_variant_object result; + result["head_block_num"] = dynamic_props.head_block_number; + result["head_block_id"] = dynamic_props.head_block_id; + result["head_block_age"] = fc::get_approximate_relative_time_string(dynamic_props.time, + time_point_sec(time_point::now()), + " old"); + result["next_maintenance_time"] = fc::get_approximate_relative_time_string(dynamic_props.next_maintenance_time); + result["chain_id"] = chain_props.chain_id; + result["participation"] = (100*dynamic_props.recent_slots_filled.popcount()) / 128.0; + result["active_witnesses"] = global_props.active_witnesses; + result["active_committee_members"] = global_props.active_committee_members; + result["entropy"] = dynamic_props.random; + return result; + } - variant_object about() const - { - string client_version( graphene::utilities::git_revision_description ); - const size_t pos = client_version.find( '/' ); - if( pos != string::npos && client_version.size() > pos ) - client_version = client_version.substr( pos + 1 ); + variant_object about() const + { + string client_version( graphene::utilities::git_revision_description ); + const size_t pos = client_version.find( '/' ); + if( pos != string::npos && client_version.size() > pos ) + client_version = client_version.substr( pos + 1 ); - fc::mutable_variant_object result; - //result["blockchain_name"] = BLOCKCHAIN_NAME; - //result["blockchain_description"] = BTS_BLOCKCHAIN_DESCRIPTION; - result["client_version"] = client_version; - result["graphene_revision"] = graphene::utilities::git_revision_sha; - result["graphene_revision_age"] = fc::get_approximate_relative_time_string( fc::time_point_sec( graphene::utilities::git_revision_unix_timestamp ) ); - result["fc_revision"] = fc::git_revision_sha; - result["fc_revision_age"] = fc::get_approximate_relative_time_string( fc::time_point_sec( fc::git_revision_unix_timestamp ) ); - result["compile_date"] = "compiled on " __DATE__ " at " __TIME__; - result["boost_version"] = boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", "."); - result["openssl_version"] = OPENSSL_VERSION_TEXT; + fc::mutable_variant_object result; + //result["blockchain_name"] = BLOCKCHAIN_NAME; + //result["blockchain_description"] = BTS_BLOCKCHAIN_DESCRIPTION; + result["client_version"] = client_version; + result["graphene_revision"] = graphene::utilities::git_revision_sha; + result["graphene_revision_age"] = fc::get_approximate_relative_time_string( fc::time_point_sec( graphene::utilities::git_revision_unix_timestamp ) ); + result["fc_revision"] = fc::git_revision_sha; + result["fc_revision_age"] = fc::get_approximate_relative_time_string( fc::time_point_sec( fc::git_revision_unix_timestamp ) ); + result["compile_date"] = "compiled on " __DATE__ " at " __TIME__; + result["boost_version"] = boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", "."); + result["openssl_version"] = OPENSSL_VERSION_TEXT; - std::string bitness = boost::lexical_cast(8 * sizeof(int*)) + "-bit"; + std::string bitness = boost::lexical_cast(8 * sizeof(int*)) + "-bit"; #if defined(__APPLE__) - std::string os = "osx"; + std::string os = "osx"; #elif defined(__linux__) - std::string os = "linux"; + std::string os = "linux"; #elif defined(_MSC_VER) - std::string os = "win32"; + std::string os = "win32"; #else - std::string os = "other"; + std::string os = "other"; #endif - result["build"] = os + " " + bitness; - - return result; - } - - chain_property_object get_chain_properties() const - { - return _remote_db->get_chain_properties(); - } - global_property_object get_global_properties() const - { - return _remote_db->get_global_properties(); - } - dynamic_global_property_object get_dynamic_global_properties() const - { - return _remote_db->get_dynamic_global_properties(); - } - account_object get_account(account_id_type id) const - { - if( _wallet.my_accounts.get().count(id) ) - return *_wallet.my_accounts.get().find(id); - auto rec = _remote_db->get_accounts({id}).front(); - FC_ASSERT(rec); - return *rec; - } - account_object get_account(string account_name_or_id) const - { - FC_ASSERT( account_name_or_id.size() > 0 ); - - if( auto id = maybe_id(account_name_or_id) ) - { - // It's an ID - return get_account(*id); - } else { - // It's a name - if( _wallet.my_accounts.get().count(account_name_or_id) ) - { - auto local_account = *_wallet.my_accounts.get().find(account_name_or_id); - auto blockchain_account = _remote_db->lookup_account_names({account_name_or_id}).front(); - FC_ASSERT( blockchain_account ); - if (local_account.id != blockchain_account->id) - elog("my account id ${id} different from blockchain id ${id2}", ("id", local_account.id)("id2", blockchain_account->id)); - if (local_account.name != blockchain_account->name) - elog("my account name ${id} different from blockchain name ${id2}", ("id", local_account.name)("id2", blockchain_account->name)); - - return *_wallet.my_accounts.get().find(account_name_or_id); - } - auto rec = _remote_db->lookup_account_names({account_name_or_id}).front(); - FC_ASSERT( rec && rec->name == account_name_or_id ); - return *rec; - } - } - account_id_type get_account_id(string account_name_or_id) const - { - return get_account(account_name_or_id).get_id(); - } - optional find_asset(asset_id_type id)const - { - auto rec = _remote_db->get_assets({id}).front(); - if( rec ) - _asset_cache[id] = *rec; - return rec; - } - optional find_asset(string asset_symbol_or_id)const - { - FC_ASSERT( asset_symbol_or_id.size() > 0 ); - - if( auto id = maybe_id(asset_symbol_or_id) ) - { - // It's an ID - return find_asset(*id); - } else { - // It's a symbol - auto rec = _remote_db->lookup_asset_symbols({asset_symbol_or_id}).front(); - if( rec ) - { - if( rec->symbol != asset_symbol_or_id ) - return optional(); - - _asset_cache[rec->get_id()] = *rec; - } - return rec; - } - } - asset_object get_asset(asset_id_type id)const - { - auto opt = find_asset(id); - FC_ASSERT(opt); - return *opt; - } - asset_object get_asset(string asset_symbol_or_id)const - { - auto opt = find_asset(asset_symbol_or_id); - FC_ASSERT(opt); - return *opt; - } - - asset_id_type get_asset_id(string asset_symbol_or_id) const - { - FC_ASSERT( asset_symbol_or_id.size() > 0 ); - vector> opt_asset; - if( std::isdigit( asset_symbol_or_id.front() ) ) - return fc::variant(asset_symbol_or_id).as(); - opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} ); - FC_ASSERT( (opt_asset.size() > 0) && (opt_asset[0].valid()) ); - return opt_asset[0]->id; - } - - string get_wallet_filename() const - { - return _wallet_filename; - } - - fc::ecc::private_key get_private_key(const public_key_type& id)const - { - auto it = _keys.find(id); - FC_ASSERT( it != _keys.end() ); - - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - FC_ASSERT( privkey ); - return *privkey; - } - - fc::ecc::private_key get_private_key_for_account(const account_object& account)const - { - vector active_keys = account.active.get_keys(); - if (active_keys.size() != 1) - FC_THROW("Expecting a simple authority with one active key"); - return get_private_key(active_keys.front()); - } - - // imports the private key into the wallet, and associate it in some way (?) with the - // given account name. - // @returns true if the key matches a current active/owner/memo key for the named - // account, false otherwise (but it is stored either way) - bool import_key(string account_name_or_id, string wif_key) - { - fc::optional optional_private_key = wif_to_key(wif_key); - if (!optional_private_key) - FC_THROW("Invalid private key"); - graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key(); - - account_object account = get_account( account_name_or_id ); - - // make a list of all current public keys for the named account - flat_set all_keys_for_account; - std::vector active_keys = account.active.get_keys(); - std::vector owner_keys = account.owner.get_keys(); - std::copy(active_keys.begin(), active_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); - std::copy(owner_keys.begin(), owner_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); - all_keys_for_account.insert(account.options.memo_key); - - _keys[wif_pub_key] = wif_key; - - _wallet.update_account(account); - - _wallet.extra_keys[account.id].insert(wif_pub_key); - - return all_keys_for_account.find(wif_pub_key) != all_keys_for_account.end(); - } - - vector< signed_transaction > import_balance( string name_or_id, const vector& wif_keys, bool broadcast ); - - void game_in_new_state(const game_object& game_obj) - { try { - if (game_obj.get_state() == game_state::expecting_commit_moves) - { - if (game_obj.players.size() != 2) // we only support RPS, a 2 player game - return; - const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get(); - for (unsigned i = 0; i < 2; ++i) - { - if (!rps_details.commit_moves.at(i)) // if this player hasn't committed their move - { - const account_id_type& account_id = game_obj.players[i]; - if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us - { - ilog("Game ${game_id}: it is ${account_name}'s turn to commit their move", - ("game_id", game_obj.id) - ("account_name", get_account(account_id).name)); - } - } - } - } - else if (game_obj.get_state() == game_state::expecting_reveal_moves) - { - if (game_obj.players.size() != 2) // we only support RPS, a 2 player game - return; - const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get(); - for (unsigned i = 0; i < 2; ++i) - { - if (rps_details.commit_moves.at(i) && - !rps_details.reveal_moves.at(i)) // if this player has committed but not revealed - { - const account_id_type& account_id = game_obj.players[i]; - if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us - { - if (self.is_locked()) - ilog("Game ${game_id}: unable to broadcast ${account_name}'s reveal because the wallet is locked", - ("game_id", game_obj.id) - ("account_name", get_account(account_id).name)); - else - { - ilog("Game ${game_id}: it is ${account_name}'s turn to reveal their move", - ("game_id", game_obj.id) - ("account_name", get_account(account_id).name)); - - auto iter = _wallet.committed_game_moves.find(*rps_details.commit_moves.at(i)); - if (iter != _wallet.committed_game_moves.end()) - { - const rock_paper_scissors_throw_reveal& reveal = iter->second; - - game_move_operation move_operation; - move_operation.game_id = game_obj.id; - move_operation.player_account_id = account_id; - move_operation.move = reveal; - - signed_transaction trx; - trx.operations = {move_operation}; - set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); - trx.validate(); - ilog("Broadcasting reveal..."); - trx = sign_transaction(trx, true); - ilog("Reveal broadcast, transaction id is ${id}", ("id", trx.id())); - } - } - } - } - } - } - } FC_RETHROW_EXCEPTIONS(warn, "") } - - void match_in_new_state(const match_object& match_obj) - { try { - if (match_obj.get_state() == match_state::match_in_progress) - { - for (const account_id_type& account_id : match_obj.players) - { - if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) - { - ilog("Match ${match} is now in progress for player ${account}", - ("match", match_obj.id)("account", get_account(account_id).name)); - for (const game_id_type& game_id : match_obj.games) - { - game_object game_obj = get_object(game_id); - auto insert_result = game_cache.insert(game_obj); - if (insert_result.second) - game_in_new_state(game_obj); - } - } - } - } - } FC_RETHROW_EXCEPTIONS(warn, "") } - - // Cache all matches in the tournament, which will also register us for - // updates on those matches - void monitor_matches_in_tournament(const tournament_object& tournament_obj) - { try { - tournament_details_object tournament_details = get_object(tournament_obj.tournament_details_id); - for (const match_id_type& match_id : tournament_details.matches) - { - match_object match_obj = get_object(match_id); - auto insert_result = match_cache.insert(match_obj); - if (insert_result.second) - match_in_new_state(match_obj); - } - } FC_RETHROW_EXCEPTIONS(warn, "") } - - void resync_active_tournaments() - { - // check to see if any of our accounts are registered for tournaments - // the real purpose of this is to ensure that we are subscribed for callbacks on these tournaments - ilog("Checking my accounts for active tournaments",); - tournament_cache.clear(); - match_cache.clear(); - game_cache.clear(); - for (const account_object& my_account : _wallet.my_accounts) - { - std::vector tournament_ids = _remote_db->get_registered_tournaments(my_account.id, 100); - for (const tournament_id_type& tournament_id : tournament_ids) - { - try - { - tournament_object tournament = get_object(tournament_id); - auto insert_result = tournament_cache.insert(tournament); - if (insert_result.second) - { - // then this is the first time we've seen this tournament - monitor_matches_in_tournament(tournament); - } - tournament_ids.push_back(tournament.id); - } - catch (const fc::exception& e) - { - edump((e)(tournament_id)); - } - } - if (!tournament_ids.empty()) - ilog("Account ${my_account} is registered for tournaments: ${tournaments}", ("my_account", my_account.name)("tournaments", tournament_ids)); - else - ilog("Account ${my_account} is not registered for any tournaments", ("my_account", my_account.name)); - } - } - - bool load_wallet_file(string wallet_filename = "") - { - // TODO: Merge imported wallet with existing wallet, - // instead of replacing it - if( wallet_filename == "" ) - wallet_filename = _wallet_filename; - - if( ! fc::exists( wallet_filename ) ) - return false; - - _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >(); - if( _wallet.chain_id != _chain_id ) - FC_THROW( "Wallet chain ID does not match", - ("wallet.chain_id", _wallet.chain_id) - ("chain_id", _chain_id) ); - - size_t account_pagination = 100; - vector< account_id_type > account_ids_to_send; - size_t n = _wallet.my_accounts.size(); - account_ids_to_send.reserve( std::min( account_pagination, n ) ); - auto it = _wallet.my_accounts.begin(); - - for( size_t start=0; start start ); - account_ids_to_send.clear(); - std::vector< account_object > old_accounts; - for( size_t i=start; i > accounts = _remote_db->get_accounts(account_ids_to_send); - // server response should be same length as request - FC_ASSERT( accounts.size() == account_ids_to_send.size() ); - size_t i = 0; - for( const optional< account_object >& acct : accounts ) - { - account_object& old_acct = old_accounts[i]; - if( !acct.valid() ) - { - elog( "Could not find account ${id} : \"${name}\" does not exist on the chain!", ("id", old_acct.id)("name", old_acct.name) ); - i++; - continue; - } - // this check makes sure the server didn't send results - // in a different order, or accounts we didn't request - FC_ASSERT( acct->id == old_acct.id ); - if( fc::json::to_string(*acct) != fc::json::to_string(old_acct) ) - { - wlog( "Account ${id} : \"${name}\" updated on chain", ("id", acct->id)("name", acct->name) ); - } - _wallet.update_account( *acct ); - i++; - } - } - - resync_active_tournaments(); - - return true; - } - void save_wallet_file(string wallet_filename = "") - { - // - // Serialize in memory, then save to disk - // - // This approach lessens the risk of a partially written wallet - // if exceptions are thrown in serialization - // - - encrypt_keys(); - - if( wallet_filename == "" ) - wallet_filename = _wallet_filename; - - wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); - - string data = fc::json::to_pretty_string( _wallet ); - try - { - enable_umask_protection(); - // - // Parentheses on the following declaration fails to compile, - // due to the Most Vexing Parse. Thanks, C++ - // - // http://en.wikipedia.org/wiki/Most_vexing_parse - // - fc::ofstream outfile{ fc::path( wallet_filename ) }; - outfile.write( data.c_str(), data.length() ); - outfile.flush(); - outfile.close(); - disable_umask_protection(); - } - catch(...) - { - disable_umask_protection(); - throw; - } - } - - transaction_handle_type begin_builder_transaction() - { - int trx_handle = _builder_transactions.empty()? 0 - : (--_builder_transactions.end())->first + 1; - _builder_transactions[trx_handle]; - return trx_handle; - } - void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) - { - FC_ASSERT(_builder_transactions.count(transaction_handle)); - _builder_transactions[transaction_handle].operations.emplace_back(op); - } - void replace_operation_in_builder_transaction(transaction_handle_type handle, - uint32_t operation_index, - const operation& new_op) - { - FC_ASSERT(_builder_transactions.count(handle)); - signed_transaction& trx = _builder_transactions[handle]; - FC_ASSERT( operation_index < trx.operations.size()); - trx.operations[operation_index] = new_op; - } - asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL) - { - FC_ASSERT(_builder_transactions.count(handle)); - - auto fee_asset_obj = get_asset(fee_asset); - asset total_fee = fee_asset_obj.amount(0); - - auto gprops = _remote_db->get_global_properties().parameters; - if( fee_asset_obj.get_id() != asset_id_type() ) - { - for( auto& op : _builder_transactions[handle].operations ) - total_fee += gprops.current_fees->set_fee( op, fee_asset_obj.options.core_exchange_rate ); - - FC_ASSERT((total_fee * fee_asset_obj.options.core_exchange_rate).amount <= - get_object(fee_asset_obj.dynamic_asset_data_id).fee_pool, - "Cannot pay fees in ${asset}, as this asset's fee pool is insufficiently funded.", - ("asset", fee_asset_obj.symbol)); - } else { - for( auto& op : _builder_transactions[handle].operations ) - total_fee += gprops.current_fees->set_fee( op ); - } - - return total_fee; - } - transaction preview_builder_transaction(transaction_handle_type handle) - { - FC_ASSERT(_builder_transactions.count(handle)); - return _builder_transactions[handle]; - } - signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true) - { - FC_ASSERT(_builder_transactions.count(transaction_handle)); - - return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast); - } - signed_transaction propose_builder_transaction( - transaction_handle_type handle, - time_point_sec expiration = time_point::now() + fc::minutes(1), - uint32_t review_period_seconds = 0, bool broadcast = true) - { - FC_ASSERT(_builder_transactions.count(handle)); - proposal_create_operation op; - op.expiration_time = expiration; - signed_transaction& trx = _builder_transactions[handle]; - std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops), - [](const operation& op) -> op_wrapper { return op; }); - if( review_period_seconds ) - op.review_period_seconds = review_period_seconds; - trx.operations = {op}; - _remote_db->get_global_properties().parameters.current_fees->set_fee( trx.operations.front() ); - - return trx = sign_transaction(trx, broadcast); - } - - signed_transaction propose_builder_transaction2( - transaction_handle_type handle, - string account_name_or_id, - time_point_sec expiration = time_point::now() + fc::minutes(1), - uint32_t review_period_seconds = 0, bool broadcast = true) - { - FC_ASSERT(_builder_transactions.count(handle)); - proposal_create_operation op; - op.fee_paying_account = get_account(account_name_or_id).get_id(); - op.expiration_time = expiration; - signed_transaction& trx = _builder_transactions[handle]; - std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops), - [](const operation& op) -> op_wrapper { return op; }); - if( review_period_seconds ) - op.review_period_seconds = review_period_seconds; - trx.operations = {op}; - _remote_db->get_global_properties().parameters.current_fees->set_fee( trx.operations.front() ); - - return trx = sign_transaction(trx, broadcast); - } - - void remove_builder_transaction(transaction_handle_type handle) - { - _builder_transactions.erase(handle); - } - - - signed_transaction register_account(string name, - public_key_type owner, - public_key_type active, - string registrar_account, - string referrer_account, - uint32_t referrer_percent, - bool broadcast = false) - { try { - FC_ASSERT( !self.is_locked() ); - FC_ASSERT( is_valid_name(name) ); - account_create_operation account_create_op; - - // #449 referrer_percent is on 0-100 scale, if user has larger - // number it means their script is using GRAPHENE_100_PERCENT scale - // instead of 0-100 scale. - FC_ASSERT( referrer_percent <= 100 ); - // TODO: process when pay_from_account is ID - - account_object registrar_account_object = - this->get_account( registrar_account ); - FC_ASSERT( registrar_account_object.is_lifetime_member() ); - - account_id_type registrar_account_id = registrar_account_object.id; - - account_object referrer_account_object = - this->get_account( referrer_account ); - account_create_op.referrer = referrer_account_object.id; - account_create_op.referrer_percent = uint16_t( referrer_percent * GRAPHENE_1_PERCENT ); - - account_create_op.registrar = registrar_account_id; - account_create_op.name = name; - account_create_op.owner = authority(1, owner, 1); - account_create_op.active = authority(1, active, 1); - account_create_op.options.memo_key = active; - - signed_transaction tx; - - tx.operations.push_back( account_create_op ); - - auto current_fees = _remote_db->get_global_properties().parameters.current_fees; - set_operation_fees( tx, current_fees ); - - vector paying_keys = registrar_account_object.active.get_keys(); - - auto dyn_props = get_dynamic_global_properties(); - tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( dyn_props.time + fc::seconds(30) ); - tx.validate(); - - for( public_key_type& key : paying_keys ) - { - auto it = _keys.find(key); - if( it != _keys.end() ) - { - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - if( !privkey.valid() ) - { - FC_ASSERT( false, "Malformed private key in _keys" ); - } - tx.sign( *privkey, _chain_id ); - } - } - - if( broadcast ) - _remote_net_broadcast->broadcast_transaction( tx ); - return tx; - } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)(referrer_account)(referrer_percent)(broadcast) ) } - - - signed_transaction upgrade_account(string name, bool broadcast) - { try { - FC_ASSERT( !self.is_locked() ); - account_object account_obj = get_account(name); - FC_ASSERT( !account_obj.is_lifetime_member() ); - - signed_transaction tx; - account_upgrade_operation op; - op.account_to_upgrade = account_obj.get_id(); - op.upgrade_to_lifetime_member = true; - tx.operations = {op}; - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (name) ) } - - - // This function generates derived keys starting with index 0 and keeps incrementing - // the index until it finds a key that isn't registered in the block chain. To be - // safer, it continues checking for a few more keys to make sure there wasn't a short gap - // caused by a failed registration or the like. - int find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key) - { - int first_unused_index = 0; - int number_of_consecutive_unused_keys = 0; - for (int key_index = 0; ; ++key_index) - { - fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index); - graphene::chain::public_key_type derived_public_key = derived_private_key.get_public_key(); - if( _keys.find(derived_public_key) == _keys.end() ) - { - if (number_of_consecutive_unused_keys) - { - ++number_of_consecutive_unused_keys; - if (number_of_consecutive_unused_keys > 5) - return first_unused_index; - } - else - { - first_unused_index = key_index; - number_of_consecutive_unused_keys = 1; - } - } - else - { - // key_index is used - first_unused_index = 0; - number_of_consecutive_unused_keys = 0; - } - } - } - - signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey, - string account_name, - string registrar_account, - string referrer_account, - bool broadcast = false, - bool save_wallet = true) - { try { - 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); - - int memo_key_index = find_first_unused_derived_key_index(active_privkey); - fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index); - - graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key(); - graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key(); - graphene::chain::public_key_type memo_pubkey = memo_privkey.get_public_key(); - - account_create_operation account_create_op; - - // TODO: process when pay_from_account is ID - - account_object registrar_account_object = get_account( registrar_account ); - - account_id_type registrar_account_id = registrar_account_object.id; - - account_object referrer_account_object = get_account( referrer_account ); - account_create_op.referrer = referrer_account_object.id; - account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage; - - account_create_op.registrar = registrar_account_id; - account_create_op.name = account_name; - account_create_op.owner = authority(1, owner_pubkey, 1); - account_create_op.active = authority(1, active_pubkey, 1); - account_create_op.options.memo_key = memo_pubkey; - - // current_fee_schedule() - // find_account(pay_from_account) - - // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule()); - - signed_transaction tx; - - tx.operations.push_back( account_create_op ); - - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - - vector paying_keys = registrar_account_object.active.get_keys(); - - auto dyn_props = get_dynamic_global_properties(); - tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( dyn_props.time + fc::seconds(30) ); - tx.validate(); - - for( public_key_type& key : paying_keys ) - { - auto it = _keys.find(key); - if( it != _keys.end() ) - { - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); - tx.sign( *privkey, _chain_id ); - } - } - - // we do not insert owner_privkey here because - // it is intended to only be used for key recovery - _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey )); - _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey )); - if( save_wallet ) - save_wallet_file(); - if( broadcast ) - _remote_net_broadcast->broadcast_transaction( tx ); - return tx; - } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) } - - signed_transaction create_account_with_brain_key(string brain_key, - string account_name, - string registrar_account, - string referrer_account, - bool broadcast = false, - bool save_wallet = true) - { try { - FC_ASSERT( !self.is_locked() ); - string normalized_brain_key = normalize_brain_key( brain_key ); - // TODO: scan blockchain for accounts that exist with same brain key - fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 ); - return create_account_with_private_key(owner_privkey, account_name, registrar_account, referrer_account, broadcast, save_wallet); - } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) } - - - signed_transaction create_asset(string issuer, - string symbol, - uint8_t precision, - asset_options common, - fc::optional bitasset_opts, - bool broadcast = false) - { try { - account_object issuer_account = get_account( issuer ); - FC_ASSERT(!find_asset(symbol).valid(), "Asset with that symbol already exists!"); - - asset_create_operation create_op; - create_op.issuer = issuer_account.id; - create_op.symbol = symbol; - create_op.precision = precision; - create_op.common_options = common; - create_op.bitasset_opts = bitasset_opts; - - signed_transaction tx; - tx.operations.push_back( 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( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) } - - signed_transaction update_asset(string symbol, - optional new_issuer, - asset_options new_options, - bool broadcast /* = false */) - { try { - optional asset_to_update = find_asset(symbol); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - optional new_issuer_account_id; - if (new_issuer) - { - account_object new_issuer_account = get_account(*new_issuer); - new_issuer_account_id = new_issuer_account.id; - } - - asset_update_operation update_op; - update_op.issuer = asset_to_update->issuer; - update_op.asset_to_update = asset_to_update->id; - update_op.new_issuer = new_issuer_account_id; - update_op.new_options = new_options; - - signed_transaction tx; - tx.operations.push_back( 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( (symbol)(new_issuer)(new_options)(broadcast) ) } - - signed_transaction update_bitasset(string symbol, - bitasset_options new_options, - bool broadcast /* = false */) - { try { - optional asset_to_update = find_asset(symbol); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - - asset_update_bitasset_operation update_op; - update_op.issuer = asset_to_update->issuer; - update_op.asset_to_update = asset_to_update->id; - update_op.new_options = new_options; - - signed_transaction tx; - tx.operations.push_back( 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( (symbol)(new_options)(broadcast) ) } - - signed_transaction update_dividend_asset(string symbol, - dividend_asset_options new_options, - bool broadcast /* = false */) - { try { - optional asset_to_update = find_asset(symbol); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - - asset_update_dividend_operation update_op; - update_op.issuer = asset_to_update->issuer; - update_op.asset_to_update = asset_to_update->id; - update_op.new_options = new_options; - - signed_transaction tx; - tx.operations.push_back( 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( (symbol)(new_options)(broadcast) ) } - - signed_transaction update_asset_feed_producers(string symbol, - flat_set new_feed_producers, - bool broadcast /* = false */) - { try { - optional asset_to_update = find_asset(symbol); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - - asset_update_feed_producers_operation update_op; - update_op.issuer = asset_to_update->issuer; - update_op.asset_to_update = asset_to_update->id; - update_op.new_feed_producers.reserve(new_feed_producers.size()); - std::transform(new_feed_producers.begin(), new_feed_producers.end(), - std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()), - [this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); }); - - signed_transaction tx; - tx.operations.push_back( 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( (symbol)(new_feed_producers)(broadcast) ) } - - signed_transaction publish_asset_feed(string publishing_account, - string symbol, - price_feed feed, - bool broadcast /* = false */) - { try { - optional asset_to_update = find_asset(symbol); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - - asset_publish_feed_operation publish_op; - publish_op.publisher = get_account_id(publishing_account); - publish_op.asset_id = asset_to_update->id; - publish_op.feed = feed; - - signed_transaction tx; - tx.operations.push_back( publish_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) } - - signed_transaction fund_asset_fee_pool(string from, - string symbol, - string amount, - bool broadcast /* = false */) - { try { - account_object from_account = get_account(from); - optional asset_to_fund = find_asset(symbol); - if (!asset_to_fund) - FC_THROW("No asset with that symbol exists!"); - asset_object core_asset = get_asset(asset_id_type()); - - asset_fund_fee_pool_operation fund_op; - fund_op.from_account = from_account.id; - fund_op.asset_id = asset_to_fund->id; - fund_op.amount = core_asset.amount_from_string(amount).amount; - - signed_transaction tx; - tx.operations.push_back( fund_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) } - - signed_transaction reserve_asset(string from, - string amount, - string symbol, - bool broadcast /* = false */) - { try { - account_object from_account = get_account(from); - optional asset_to_reserve = find_asset(symbol); - if (!asset_to_reserve) - FC_THROW("No asset with that symbol exists!"); - - asset_reserve_operation reserve_op; - reserve_op.payer = from_account.id; - reserve_op.amount_to_reserve = asset_to_reserve->amount_from_string(amount); - - signed_transaction tx; - tx.operations.push_back( reserve_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) } - - signed_transaction global_settle_asset(string symbol, - price settle_price, - bool broadcast /* = false */) - { try { - optional asset_to_settle = find_asset(symbol); - if (!asset_to_settle) - FC_THROW("No asset with that symbol exists!"); - - asset_global_settle_operation settle_op; - settle_op.issuer = asset_to_settle->issuer; - settle_op.asset_to_settle = asset_to_settle->id; - settle_op.settle_price = settle_price; - - signed_transaction tx; - tx.operations.push_back( settle_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) } - - signed_transaction settle_asset(string account_to_settle, - string amount_to_settle, - string symbol, - bool broadcast /* = false */) - { try { - optional asset_to_settle = find_asset(symbol); - if (!asset_to_settle) - FC_THROW("No asset with that symbol exists!"); - - asset_settle_operation settle_op; - settle_op.account = get_account_id(account_to_settle); - settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle); - - signed_transaction tx; - tx.operations.push_back( settle_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) } - - signed_transaction whitelist_account(string authorizing_account, - string account_to_list, - account_whitelist_operation::account_listing new_listing_status, - bool broadcast /* = false */) - { try { - account_whitelist_operation whitelist_op; - whitelist_op.authorizing_account = get_account_id(authorizing_account); - whitelist_op.account_to_list = get_account_id(account_to_list); - whitelist_op.new_listing = new_listing_status; - - signed_transaction tx; - tx.operations.push_back( whitelist_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } - - signed_transaction create_committee_member(string owner_account, string url, - bool broadcast /* = false */) - { try { - - committee_member_create_operation committee_member_create_op; - committee_member_create_op.committee_member_account = get_account_id(owner_account); - committee_member_create_op.url = url; - if (_remote_db->get_committee_member_by_account(committee_member_create_op.committee_member_account)) - FC_THROW("Account ${owner_account} is already a committee_member", ("owner_account", owner_account)); - - signed_transaction tx; - tx.operations.push_back( committee_member_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) ) } - - witness_object get_witness(string owner_account) - { - try - { - fc::optional witness_id = maybe_id(owner_account); - if (witness_id) - { - std::vector ids_to_get; - ids_to_get.push_back(*witness_id); - std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get); - if (witness_objects.front()) - return *witness_objects.front(); - FC_THROW("No witness 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 witness = _remote_db->get_witness_by_account(owner_account_id); - if (witness) - return *witness; - else - FC_THROW("No witness is registered for account ${account}", ("account", owner_account)); - } - catch (const fc::exception&) - { - FC_THROW("No account or witness named ${account}", ("account", owner_account)); - } - } - } - FC_CAPTURE_AND_RETHROW( (owner_account) ) - } - - committee_member_object get_committee_member(string owner_account) - { - try - { - fc::optional committee_member_id = maybe_id(owner_account); - if (committee_member_id) - { - std::vector ids_to_get; - ids_to_get.push_back(*committee_member_id); - std::vector> committee_member_objects = _remote_db->get_committee_members(ids_to_get); - if (committee_member_objects.front()) - return *committee_member_objects.front(); - FC_THROW("No committee_member 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 committee_member = _remote_db->get_committee_member_by_account(owner_account_id); - if (committee_member) - return *committee_member; - else - FC_THROW("No committee_member is registered for account ${account}", ("account", owner_account)); - } - catch (const fc::exception&) - { - FC_THROW("No account or committee_member named ${account}", ("account", owner_account)); - } - } - } - FC_CAPTURE_AND_RETHROW( (owner_account) ) - } - - signed_transaction create_witness(string owner_account, - string url, - bool broadcast /* = false */) - { try { - 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); - fc::ecc::private_key witness_private_key = derive_private_key(key_to_wif(active_private_key), witness_key_index); - graphene::chain::public_key_type witness_public_key = witness_private_key.get_public_key(); - - witness_create_operation witness_create_op; - witness_create_op.witness_account = witness_account.id; - witness_create_op.block_signing_key = witness_public_key; - witness_create_op.url = url; - secret_hash_type::encoder enc; - fc::raw::pack(enc, witness_private_key); - fc::raw::pack(enc, secret_hash_type()); - witness_create_op.initial_secret = secret_hash_type::hash(enc.result()); - - - if (_remote_db->get_witness_by_account(witness_create_op.witness_account)) - FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account)); - - signed_transaction tx; - tx.operations.push_back( witness_create_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - _wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } - - signed_transaction update_witness(string witness_name, - string url, - string block_signing_key, - bool broadcast /* = false */) - { try { - witness_object witness = get_witness(witness_name); - account_object witness_account = get_account( witness.witness_account ); - fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account); - - witness_update_operation witness_update_op; - witness_update_op.witness = witness.id; - witness_update_op.witness_account = witness_account.id; - if( url != "" ) - witness_update_op.new_url = url; - if( block_signing_key != "" ) { - witness_update_op.new_signing_key = public_key_type( block_signing_key ); - witness_update_op.new_initial_secret = secret_hash_type::hash(secret_hash_type()); - } - - signed_transaction tx; - tx.operations.push_back( witness_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( (witness_name)(url)(block_signing_key)(broadcast) ) } - - template - static WorkerInit _create_worker_initializer( const variant& worker_settings ) - { - WorkerInit result; - from_variant( worker_settings, result ); - return result; - } - - signed_transaction create_worker( - string owner_account, - time_point_sec work_begin_date, - time_point_sec work_end_date, - share_type daily_pay, - string name, - string url, - variant worker_settings, - bool broadcast - ) - { - worker_initializer init; - std::string wtype = worker_settings["type"].get_string(); - - // TODO: Use introspection to do this dispatch - if( wtype == "burn" ) - init = _create_worker_initializer< burn_worker_initializer >( worker_settings ); - else if( wtype == "refund" ) - init = _create_worker_initializer< refund_worker_initializer >( worker_settings ); - else if( wtype == "vesting" ) - init = _create_worker_initializer< vesting_balance_worker_initializer >( worker_settings ); - else - { - FC_ASSERT( false, "unknown worker[\"type\"] value" ); - } - - worker_create_operation op; - op.owner = get_account( owner_account ).id; - op.work_begin_date = work_begin_date; - op.work_end_date = work_end_date; - op.daily_pay = daily_pay; - op.name = name; - op.url = url; - op.initializer = init; - - 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 ); - } - - signed_transaction update_worker_votes( - string account, - worker_vote_delta delta, - bool broadcast - ) - { - account_object acct = get_account( account ); - account_update_operation op; - - // you could probably use a faster algorithm for this, but flat_set is fast enough :) - flat_set< worker_id_type > merged; - merged.reserve( delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); - for( const worker_id_type& wid : delta.vote_for ) - { - bool inserted = merged.insert( wid ).second; - FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); - } - for( const worker_id_type& wid : delta.vote_against ) - { - bool inserted = merged.insert( wid ).second; - FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); - } - for( const worker_id_type& wid : delta.vote_abstain ) - { - bool inserted = merged.insert( wid ).second; - FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); - } - - // should be enforced by FC_ASSERT's above - assert( merged.size() == delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); - - vector< object_id_type > query_ids; - for( const worker_id_type& wid : merged ) - query_ids.push_back( wid ); - - flat_set new_votes( acct.options.votes ); - - fc::variants objects = _remote_db->get_objects( query_ids ); - for( const variant& obj : objects ) - { - worker_object wo; - from_variant( obj, wo ); - new_votes.erase( wo.vote_for ); - new_votes.erase( wo.vote_against ); - if( delta.vote_for.find( wo.id ) != delta.vote_for.end() ) - new_votes.insert( wo.vote_for ); - else if( delta.vote_against.find( wo.id ) != delta.vote_against.end() ) - new_votes.insert( wo.vote_against ); - else - assert( delta.vote_abstain.find( wo.id ) != delta.vote_abstain.end() ); - } - - account_update_operation update_op; - update_op.account = acct.id; - update_op.new_options = acct.options; - update_op.new_options->votes = new_votes; - - signed_transaction tx; - tx.operations.push_back( update_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } - - vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) - { try { - fc::optional vbid = maybe_id( account_name ); - std::vector result; - fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time; - - if( vbid ) - { - result.emplace_back( get_object(*vbid), now ); - return result; - } - - // try casting to avoid a round-trip if we were given an account ID - fc::optional acct_id = maybe_id( account_name ); - if( !acct_id ) - acct_id = get_account( account_name ).id; - - vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( *acct_id ); - if( vbos.size() == 0 ) - return result; - - for( const vesting_balance_object& vbo : vbos ) - result.emplace_back( vbo, now ); - - return result; - } FC_CAPTURE_AND_RETHROW( (account_name) ) - } - - signed_transaction withdraw_vesting( - string witness_name, - string amount, - string asset_symbol, - bool broadcast = false ) - { try { - asset_object asset_obj = get_asset( asset_symbol ); - fc::optional vbid = maybe_id(witness_name); - if( !vbid ) - { - witness_object wit = get_witness( witness_name ); - FC_ASSERT( wit.pay_vb ); - vbid = wit.pay_vb; - } - - vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid ); - vesting_balance_withdraw_operation vesting_balance_withdraw_op; - - vesting_balance_withdraw_op.vesting_balance = *vbid; - vesting_balance_withdraw_op.owner = vbo.owner; - vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount); - - signed_transaction tx; - tx.operations.push_back( vesting_balance_withdraw_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - - return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) ) - } - - signed_transaction vote_for_committee_member(string voting_account, - string committee_member, - bool approve, - bool broadcast /* = false */) - { try { - account_object voting_account_object = get_account(voting_account); - account_id_type committee_member_owner_account_id = get_account_id(committee_member); - fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id); - if (!committee_member_obj) - FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member)); - if (approve) - { - auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id); - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); - } - else - { - unsigned votes_removed = voting_account_object.options.votes.erase(committee_member_obj->vote_id); - if (!votes_removed) - FC_THROW("Account ${account} is already not voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); - } - 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)(committee_member)(approve)(broadcast) ) } - - signed_transaction vote_for_witness(string voting_account, - string witness, - bool approve, - bool broadcast /* = false */) - { try { - account_object voting_account_object = get_account(voting_account); - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); - if (!witness_obj) - FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); - if (approve) - { - auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); - } - else - { - unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); - if (!votes_removed) - FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); - } - 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)(witness)(approve)(broadcast) ) } - - signed_transaction update_witness_votes(string voting_account, - std::vector witnesses_to_approve, - std::vector witnesses_to_reject, - uint16_t desired_number_of_witnesses, - bool broadcast /* = false */) - { try { - account_object voting_account_object = get_account(voting_account); - for (const std::string& witness : witnesses_to_approve) - { - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); - if (!witness_obj) - FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); - auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); - if (!insert_result.second) - FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); - } - for (const std::string& witness : witnesses_to_reject) - { - account_id_type witness_owner_account_id = get_account_id(witness); - fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); - if (!witness_obj) - FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); - unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); - if (!votes_removed) - FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); - } - voting_account_object.options.num_witness = desired_number_of_witnesses; - - 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)(witnesses_to_approve)(witnesses_to_reject)(desired_number_of_witnesses)(broadcast) ) } - - signed_transaction set_voting_proxy(string account_to_modify, - optional voting_account, - bool broadcast /* = false */) - { try { - account_object account_object_to_modify = get_account(account_to_modify); - if (voting_account) - { - account_id_type new_voting_account_id = get_account_id(*voting_account); - if (account_object_to_modify.options.voting_account == new_voting_account_id) - FC_THROW("Voting proxy for ${account} is already set to ${voter}", ("account", account_to_modify)("voter", *voting_account)); - account_object_to_modify.options.voting_account = new_voting_account_id; - } - else - { - if (account_object_to_modify.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT) - FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify)); - account_object_to_modify.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; - } - - account_update_operation account_update_op; - account_update_op.account = account_object_to_modify.id; - account_update_op.new_options = account_object_to_modify.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( (account_to_modify)(voting_account)(broadcast) ) } - - signed_transaction set_desired_witness_and_committee_member_count(string account_to_modify, - uint16_t desired_number_of_witnesses, - uint16_t desired_number_of_committee_members, - bool broadcast /* = false */) - { try { - account_object account_object_to_modify = get_account(account_to_modify); - - if (account_object_to_modify.options.num_witness == desired_number_of_witnesses && - account_object_to_modify.options.num_committee == desired_number_of_committee_members) - FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members", - ("account", account_to_modify)("witnesses", desired_number_of_witnesses)("committee_members",desired_number_of_witnesses)); - account_object_to_modify.options.num_witness = desired_number_of_witnesses; - account_object_to_modify.options.num_committee = desired_number_of_committee_members; - - account_update_operation account_update_op; - account_update_op.account = account_object_to_modify.id; - account_update_op.new_options = account_object_to_modify.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( (account_to_modify)(desired_number_of_witnesses)(desired_number_of_committee_members)(broadcast) ) } - - signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) - { - flat_set req_active_approvals; - flat_set req_owner_approvals; - vector other_auths; - - tx.get_required_authorities( req_active_approvals, req_owner_approvals, other_auths ); - - for( const auto& auth : other_auths ) - for( const auto& a : auth.account_auths ) - req_active_approvals.insert(a.first); - - // std::merge lets us de-duplicate account_id's that occur in both - // sets, and dump them into a vector (as required by remote_db api) - // at the same time - vector v_approving_account_ids; - std::merge(req_active_approvals.begin(), req_active_approvals.end(), - req_owner_approvals.begin() , req_owner_approvals.end(), - std::back_inserter(v_approving_account_ids)); - - /// TODO: fetch the accounts specified via other_auths as well. - - vector< optional > approving_account_objects = - _remote_db->get_accounts( v_approving_account_ids ); - - /// TODO: recursively check one layer deeper in the authority tree for keys - - FC_ASSERT( approving_account_objects.size() == v_approving_account_ids.size() ); - - flat_map approving_account_lut; - size_t i = 0; - for( optional& approving_acct : approving_account_objects ) - { - if( !approving_acct.valid() ) - { - wlog( "operation_get_required_auths said approval of non-existing account ${id} was needed", - ("id", v_approving_account_ids[i]) ); - i++; - continue; - } - approving_account_lut[ approving_acct->id ] = &(*approving_acct); - i++; - } - - flat_set approving_key_set; - for( account_id_type& acct_id : req_active_approvals ) - { - const auto it = approving_account_lut.find( acct_id ); - if( it == approving_account_lut.end() ) - continue; - const account_object* acct = it->second; - vector v_approving_keys = acct->active.get_keys(); - for( const public_key_type& approving_key : v_approving_keys ) - approving_key_set.insert( approving_key ); - } - for( account_id_type& acct_id : req_owner_approvals ) - { - const auto it = approving_account_lut.find( acct_id ); - if( it == approving_account_lut.end() ) - continue; - const account_object* acct = it->second; - vector v_approving_keys = acct->owner.get_keys(); - for( const public_key_type& approving_key : v_approving_keys ) - approving_key_set.insert( approving_key ); - } - for( const authority& a : other_auths ) - { - for( const auto& k : a.key_auths ) - approving_key_set.insert( k.first ); - } - - auto dyn_props = get_dynamic_global_properties(); - tx.set_reference_block( dyn_props.head_block_id ); - - // first, some bookkeeping, expire old items from _recently_generated_transactions - // since transactions include the head block id, we just need the index for keeping transactions unique - // when there are multiple transactions in the same block. choose a time period that should be at - // least one block long, even in the worst case. 2 minutes ought to be plenty. - fc::time_point_sec oldest_transaction_ids_to_track(dyn_props.time - fc::minutes(2)); - auto oldest_transaction_record_iter = _recently_generated_transactions.get().lower_bound(oldest_transaction_ids_to_track); - auto begin_iter = _recently_generated_transactions.get().begin(); - _recently_generated_transactions.get().erase(begin_iter, oldest_transaction_record_iter); - - uint32_t expiration_time_offset = 0; - for (;;) - { - tx.set_expiration( dyn_props.time + fc::seconds(30 + expiration_time_offset) ); - tx.signatures.clear(); - - for( public_key_type& key : approving_key_set ) - { - auto it = _keys.find(key); - if( it != _keys.end() ) - { - fc::optional privkey = wif_to_key( it->second ); - FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); - tx.sign( *privkey, _chain_id ); - } - /// TODO: if transaction has enough signatures to be "valid" don't add any more, - /// there are cases where the wallet may have more keys than strictly necessary and - /// the transaction will be rejected if the transaction validates without requiring - /// all signatures provided - } - - graphene::chain::transaction_id_type this_transaction_id = tx.id(); - auto iter = _recently_generated_transactions.find(this_transaction_id); - if (iter == _recently_generated_transactions.end()) - { - // we haven't generated this transaction before, the usual case - recently_generated_transaction_record this_transaction_record; - this_transaction_record.generation_time = dyn_props.time; - this_transaction_record.transaction_id = this_transaction_id; - _recently_generated_transactions.insert(this_transaction_record); - break; - } - - // else we've generated a dupe, increment expiration time and re-sign it - ++expiration_time_offset; - } - - if( broadcast ) - { - try - { - _remote_net_broadcast->broadcast_transaction( tx ); - } - catch (const fc::exception& e) - { - elog("Caught exception while broadcasting tx ${id}: ${e}", ("id", tx.id().str())("e", e.to_detail_string()) ); - throw; - } - } - - return tx; - } - - signed_transaction sell_asset(string seller_account, - string amount_to_sell, - string symbol_to_sell, - string min_to_receive, - string symbol_to_receive, - uint32_t timeout_sec = 0, - bool fill_or_kill = false, - bool broadcast = false) - { - account_object seller = get_account( seller_account ); - - limit_order_create_operation op; - op.seller = seller.id; - op.amount_to_sell = get_asset(symbol_to_sell).amount_from_string(amount_to_sell); - op.min_to_receive = get_asset(symbol_to_receive).amount_from_string(min_to_receive); - if( timeout_sec ) - op.expiration = fc::time_point::now() + fc::seconds(timeout_sec); - op.fill_or_kill = fill_or_kill; - - 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 ); - } - - signed_transaction borrow_asset(string seller_name, string amount_to_borrow, string asset_symbol, - string amount_of_collateral, bool broadcast = false) - { - account_object seller = get_account(seller_name); - asset_object mia = get_asset(asset_symbol); - FC_ASSERT(mia.is_market_issued()); - asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset); - - call_order_update_operation op; - op.funding_account = seller.id; - op.delta_debt = mia.amount_from_string(amount_to_borrow); - op.delta_collateral = collateral.amount_from_string(amount_of_collateral); - - signed_transaction trx; - trx.operations = {op}; - set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); - trx.validate(); - idump((broadcast)); - - return sign_transaction(trx, broadcast); - } - - signed_transaction cancel_order(object_id_type order_id, bool broadcast = false) - { try { - FC_ASSERT(!is_locked()); - FC_ASSERT(order_id.space() == protocol_ids, "Invalid order ID ${id}", ("id", order_id)); - signed_transaction trx; - - limit_order_cancel_operation op; - op.fee_paying_account = get_object(order_id).seller; - op.order = order_id; - trx.operations = {op}; - set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); - - trx.validate(); - return sign_transaction(trx, broadcast); - } FC_CAPTURE_AND_RETHROW((order_id)) } - - signed_transaction transfer(string from, string to, string amount, - string asset_symbol, string memo, bool broadcast = false) - { try { - FC_ASSERT( !self.is_locked() ); - fc::optional asset_obj = get_asset(asset_symbol); - FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); - - account_object from_account = get_account(from); - account_object to_account = get_account(to); - account_id_type from_id = from_account.id; - account_id_type to_id = get_account_id(to); - - transfer_operation xfer_op; - - xfer_op.from = from_id; - xfer_op.to = to_id; - xfer_op.amount = asset_obj->amount_from_string(amount); - - if( memo.size() ) - { - xfer_op.memo = memo_data(); - xfer_op.memo->from = from_account.options.memo_key; - xfer_op.memo->to = to_account.options.memo_key; - xfer_op.memo->set_message(get_private_key(from_account.options.memo_key), - to_account.options.memo_key, memo); - } - - signed_transaction tx; - tx.operations.push_back(xfer_op); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction(tx, broadcast); - } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } - - signed_transaction issue_asset(string to_account, string amount, string symbol, - string memo, bool broadcast = false) - { - auto asset_obj = get_asset(symbol); - - account_object to = get_account(to_account); - account_object issuer = get_account(asset_obj.issuer); - - asset_issue_operation issue_op; - issue_op.issuer = asset_obj.issuer; - issue_op.asset_to_issue = asset_obj.amount_from_string(amount); - issue_op.issue_to_account = to.id; - - if( memo.size() ) - { - issue_op.memo = memo_data(); - issue_op.memo->from = issuer.options.memo_key; - issue_op.memo->to = to.options.memo_key; - issue_op.memo->set_message(get_private_key(issuer.options.memo_key), - to.options.memo_key, memo); - } - - signed_transaction tx; - tx.operations.push_back(issue_op); - set_operation_fees(tx,_remote_db->get_global_properties().parameters.current_fees); - tx.validate(); - - return sign_transaction(tx, broadcast); - } - - std::map> get_result_formatters() const - { - std::map > m; - m["help"] = [](variant result, const fc::variants& a) - { - return result.get_string(); - }; - - m["gethelp"] = [](variant result, const fc::variants& a) - { - return result.get_string(); - }; - - m["get_account_history"] = [this](variant result, const fc::variants& a) - { - auto r = result.as>(); - std::stringstream ss; - - for( operation_detail& d : r ) - { - operation_history_object& i = d.op; - auto b = _remote_db->get_block_header(i.block_num); - FC_ASSERT(b); - ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); - ss << " \n"; - } - - return ss.str(); - }; - - m["list_account_balances"] = [this](variant result, const fc::variants& a) - { - auto r = result.as>(); - vector asset_recs; - std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { - return get_asset(a.asset_id); - }); - - std::stringstream ss; - for( unsigned i = 0; i < asset_recs.size(); ++i ) - ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; - - return ss.str(); - }; - - m["get_blind_balances"] = [this](variant result, const fc::variants& a) - { - auto r = result.as>(); - vector asset_recs; - std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { - return get_asset(a.asset_id); - }); - - std::stringstream ss; - for( unsigned i = 0; i < asset_recs.size(); ++i ) - ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; - - return ss.str(); - }; - m["transfer_to_blind"] = [this](variant result, const fc::variants& a) - { - auto r = result.as(); - std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); - ss << "\n"; - for( const auto& out : r.outputs ) - { - asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); - ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; - } - return ss.str(); - }; - m["blind_transfer"] = [this](variant result, const fc::variants& a) - { - auto r = result.as(); - std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); - ss << "\n"; - for( const auto& out : r.outputs ) - { - asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); - ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; - } - return ss.str(); - }; - m["receive_blind_transfer"] = [this](variant result, const fc::variants& a) - { - auto r = result.as(); - std::stringstream ss; - asset_object as = get_asset( r.amount.asset_id ); - ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; - return ss.str(); - }; - m["blind_history"] = [this](variant result, const fc::variants& a) - { - auto records = result.as>(); - std::stringstream ss; - ss << "WHEN " - << " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n"; - ss << "====================================================================================\n"; - for( auto& r : records ) - { - asset_object as = get_asset( r.amount.asset_id ); - ss << fc::get_approximate_relative_time_string( r.date ) - << " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; - } - return ss.str(); - }; - m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a) - { - const vector tournaments = result.as >(); - std::stringstream ss; - ss << "ID GAME BUY IN PLAYERS\n"; - ss << "====================================================================================\n"; - for( const tournament_object& tournament_obj : tournaments ) - { - asset_object buy_in_asset = get_asset(tournament_obj.options.buy_in.asset_id); - ss << fc::variant(tournament_obj.id).as() << " " - << buy_in_asset.amount_to_pretty_string(tournament_obj.options.buy_in.amount) << " " - << tournament_obj.options.number_of_players << " players\n"; - switch (tournament_obj.get_state()) - { - case tournament_state::accepting_registrations: - { - ss << " Waiting for players, " << tournament_obj.registered_players << " of " << tournament_obj.options.number_of_players << " have registered\n"; - ss << " If enough players register, the game will start "; - if (tournament_obj.options.start_time) - ss << "at " << tournament_obj.options.start_time->to_iso_string() << "\n"; - else - ss << *tournament_obj.options.start_delay << " seconds after the last player registers\n"; - break; - } - case tournament_state::awaiting_start: - { - ss << " All players have registered, tournament will start at " << tournament_obj.start_time->to_iso_string() << "\n"; - break; - } - case tournament_state::in_progress: - { - ss << " Tournament started at " << tournament_obj.start_time->to_iso_string() << "\n"; - break; - } - case tournament_state::registration_period_expired: - { - ss << " Tournament was canceled at " << tournament_obj.options.registration_deadline.to_iso_string() << ", not enough players registered\n"; - break; - } - case tournament_state::concluded: - { - ss << " Tournament finished at " << tournament_obj.end_time->to_iso_string() << "\n"; - break; - } - } - } - return ss.str(); - }; - m["get_tournament"] = [this](variant result, const fc::variants& a) - { - std::stringstream ss; - - tournament_object tournament = result.as(); - tournament_details_object tournament_details = _remote_db->get_objects({result["tournament_details_id"].as()})[0].as(); - tournament_state state = tournament.get_state(); - if (state == tournament_state::accepting_registrations) - { - ss << "Tournament is accepting registrations\n"; - ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; - for (const account_id_type& player : tournament_details.registered_players) - ss << "\t" << get_account(player).name << "\n"; - } - else if (state == tournament_state::registration_period_expired) - { - ss << "Tournament registration period expired\n"; - ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; - for (const account_id_type& player : tournament_details.registered_players) - ss << "\t" << get_account(player).name << "\n"; - } - else if (state == tournament_state::awaiting_start) - { - ss << "Tournament starts at " << tournament.start_time->to_iso_string() << "\n"; - ss << "Players:\n"; - for (const account_id_type& player : tournament_details.registered_players) - ss << "\t" << get_account(player).name << "\n"; - } - else if (state == tournament_state::in_progress || - state == tournament_state::concluded) - { - unsigned num_matches = tournament_details.matches.size(); - uint32_t num_rounds = boost::multiprecision::detail::find_msb(tournament_details.matches.size() + 1); - unsigned num_rows = (num_matches + 1) * 2 - 1; - for (unsigned row = 0; row < num_rows; ++row) - { - for (unsigned round = 0; round <= num_rounds; ++round) - { - unsigned row_offset = (1 << round) - 1; - unsigned row_vertical_spacing = 1 << (round + 1); - if (row >= row_offset && - (row - row_offset) % row_vertical_spacing == 0) - { - unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing; - unsigned first_player_in_round = (num_matches - (num_matches >> round)) * 2; - unsigned player_number = first_player_in_round + player_number_in_round; - - unsigned match_number = player_number / 2; - unsigned player_in_match = player_number % 2; - - std::string player_name; - if (round == num_rounds) - { - match_object match = get_object(tournament_details.matches[num_matches - 1]); - if (match.get_state() == match_state::match_complete && - !match.match_winners.empty()) - { - assert(match.match_winners.size() == 1); - player_name = get_account(*match.match_winners.begin()).name; - } - } - else - { - match_object match = get_object(tournament_details.matches[match_number]); - if (!match.players.empty()) - { - if (player_in_match < match.players.size()) - player_name = get_account(match.players[player_in_match]).name; - else - player_name = "[bye]"; - } - } - - ss << "__"; - ss << std::setfill('_') << std::setw(10) << player_name.substr(0,10); - ss << "__"; - } - else - ss << " "; - - if (round != num_rounds) - { - unsigned round_horizontal_spacing = 1 << round; - unsigned next_row_vertical_spacing = 1 << (round + 2); - for (unsigned i = 0; i < round_horizontal_spacing; ++i) - { - if ((row - 1 - i - row_offset) % next_row_vertical_spacing == 0) - ss << "\\"; - else if ((row - row_vertical_spacing + i - row_offset) % next_row_vertical_spacing == 0) - ss << "/"; - else - ss << " "; - } - } - } - ss << "\n"; - } - } - - return ss.str(); - }; - m["get_order_book"] = [this](variant result, const fc::variants& a) - { - auto orders = result.as(); - auto bids = orders.bids; - auto asks = orders.asks; - std::stringstream ss; - std::stringstream sum_stream; - sum_stream << "Sum(" << orders.base << ')'; - double bid_sum = 0; - double ask_sum = 0; - const int spacing = 20; - - auto prettify_num = [&]( double n ) - { - //ss << n; - if (abs( round( n ) - n ) < 0.00000000001 ) - { - //ss << setiosflags( !ios::fixed ) << (int) n; // doesn't compile on Linux with gcc - ss << (int) n; - } - else if (n - floor(n) < 0.000001) - { - ss << setiosflags( ios::fixed ) << setprecision(10) << n; - } - else - { - ss << setiosflags( ios::fixed ) << setprecision(6) << n; - } - }; - - ss << setprecision( 8 ) << setiosflags( ios::fixed ) << setiosflags( ios::left ); - - ss << ' ' << setw( (spacing * 4) + 6 ) << "BUY ORDERS" << "SELL ORDERS\n" - << ' ' << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) - << orders.base << ' ' << setw( spacing ) << sum_stream.str() - << " " << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) - << orders.base << ' ' << setw( spacing ) << sum_stream.str() - << "\n=====================================================================================" - << "|=====================================================================================\n"; - - for (unsigned i = 0; i < bids.size() || i < asks.size() ; i++) - { - if ( i < bids.size() ) - { - bid_sum += bids[i].base; - ss << ' ' << setw( spacing ); - prettify_num( bids[i].price ); - ss << ' ' << setw( spacing ); - prettify_num( bids[i].quote ); - ss << ' ' << setw( spacing ); - prettify_num( bids[i].base ); - ss << ' ' << setw( spacing ); - prettify_num( bid_sum ); - ss << ' '; - } - else - { - ss << setw( (spacing * 4) + 5 ) << ' '; - } - - ss << '|'; - - if ( i < asks.size() ) - { - ask_sum += asks[i].base; - ss << ' ' << setw( spacing ); - prettify_num( asks[i].price ); - ss << ' ' << setw( spacing ); - prettify_num( asks[i].quote ); - ss << ' ' << setw( spacing ); - prettify_num( asks[i].base ); - ss << ' ' << setw( spacing ); - prettify_num( ask_sum ); - } - - ss << '\n'; - } - - ss << endl - << "Buy Total: " << bid_sum << ' ' << orders.base << endl - << "Sell Total: " << ask_sum << ' ' << orders.base << endl; - - return ss.str(); - }; - - return m; - } - - signed_transaction propose_parameter_change( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_values, - bool broadcast = false) - { - FC_ASSERT( !changed_values.contains("current_fees") ); - - const chain_parameters& current_params = get_global_properties().parameters; - chain_parameters new_params = current_params; - fc::reflector::visit( - fc::from_variant_visitor( changed_values, new_params ) - ); - - committee_member_update_global_parameters_operation update_op; - update_op.new_parameters = new_params; - - proposal_create_operation prop_op; - - prop_op.expiration_time = expiration_time; - prop_op.review_period_seconds = current_params.committee_proposal_review_period; - prop_op.fee_paying_account = get_account(proposing_account).id; - - prop_op.proposed_ops.emplace_back( update_op ); - current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); - - signed_transaction tx; - tx.operations.push_back(prop_op); - set_operation_fees(tx, current_params.current_fees); - tx.validate(); - - return sign_transaction(tx, broadcast); - } - - signed_transaction propose_fee_change( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_fees, - bool broadcast = false) - { - const chain_parameters& current_params = get_global_properties().parameters; - const fee_schedule_type& current_fees = *(current_params.current_fees); - - flat_map< int, fee_parameters > fee_map; - fee_map.reserve( current_fees.parameters.size() ); - for( const fee_parameters& op_fee : current_fees.parameters ) - fee_map[ op_fee.which() ] = op_fee; - uint32_t scale = current_fees.scale; - - for( const auto& item : changed_fees ) - { - const string& key = item.key(); - if( key == "scale" ) - { - int64_t _scale = item.value().as_int64(); - FC_ASSERT( _scale >= 0 ); - FC_ASSERT( _scale <= std::numeric_limits::max() ); - scale = uint32_t( _scale ); - continue; - } - // is key a number? - auto is_numeric = [&]() -> bool - { - size_t n = key.size(); - for( size_t i=0; isecond; - } - - fee_parameters fp = from_which_variant< fee_parameters >( which, item.value() ); - fee_map[ which ] = fp; - } - - fee_schedule_type new_fees; - - for( const std::pair< int, fee_parameters >& item : fee_map ) - new_fees.parameters.insert( item.second ); - new_fees.scale = scale; - - chain_parameters new_params = current_params; - new_params.current_fees = new_fees; - - committee_member_update_global_parameters_operation update_op; - update_op.new_parameters = new_params; - - proposal_create_operation prop_op; - - prop_op.expiration_time = expiration_time; - prop_op.review_period_seconds = current_params.committee_proposal_review_period; - prop_op.fee_paying_account = get_account(proposing_account).id; - - prop_op.proposed_ops.emplace_back( update_op ); - current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); - - signed_transaction tx; - tx.operations.push_back(prop_op); - set_operation_fees(tx, current_params.current_fees); - tx.validate(); - - return sign_transaction(tx, broadcast); - } - - signed_transaction propose_dividend_asset_update( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_values, - bool broadcast = false) - { - FC_ASSERT( changed_values.contains("asset_to_update") ); - - const chain_parameters& current_params = get_global_properties().parameters; - asset_update_dividend_operation changed_op; - fc::reflector::visit( - fc::from_variant_visitor( changed_values, changed_op ) - ); - - optional asset_to_update = find_asset(changed_op.asset_to_update); - if (!asset_to_update) - FC_THROW("No asset with that symbol exists!"); - - asset_update_dividend_operation update_op; - update_op.issuer = asset_to_update->issuer; - update_op.asset_to_update = asset_to_update->id; - update_op.new_options = changed_op.new_options; - - proposal_create_operation prop_op; - - prop_op.expiration_time = expiration_time; - prop_op.review_period_seconds = current_params.committee_proposal_review_period; - prop_op.fee_paying_account = get_account(proposing_account).id; - - prop_op.proposed_ops.emplace_back( update_op ); - current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); - - signed_transaction tx; - tx.operations.push_back(prop_op); - set_operation_fees(tx, current_params.current_fees); - tx.validate(); - - return sign_transaction(tx, broadcast); - } - - signed_transaction approve_proposal( - const string& fee_paying_account, - const string& proposal_id, - const approval_delta& delta, - bool broadcast = false) - { - proposal_update_operation update_op; - - update_op.fee_paying_account = get_account(fee_paying_account).id; - update_op.proposal = fc::variant(proposal_id).as(); - // make sure the proposal exists - get_object( update_op.proposal ); - - for( const std::string& name : delta.active_approvals_to_add ) - update_op.active_approvals_to_add.insert( get_account( name ).id ); - for( const std::string& name : delta.active_approvals_to_remove ) - update_op.active_approvals_to_remove.insert( get_account( name ).id ); - for( const std::string& name : delta.owner_approvals_to_add ) - update_op.owner_approvals_to_add.insert( get_account( name ).id ); - for( const std::string& name : delta.owner_approvals_to_remove ) - update_op.owner_approvals_to_remove.insert( get_account( name ).id ); - for( const std::string& k : delta.key_approvals_to_add ) - update_op.key_approvals_to_add.insert( public_key_type( k ) ); - for( const std::string& k : delta.key_approvals_to_remove ) - update_op.key_approvals_to_remove.insert( public_key_type( k ) ); - - signed_transaction tx; - tx.operations.push_back(update_op); - set_operation_fees(tx, get_global_properties().parameters.current_fees); - tx.validate(); - return sign_transaction(tx, broadcast); - } - - void dbg_make_uia(string creator, string symbol) - { - asset_options opts; - opts.flags &= ~(white_list | disable_force_settle | global_settle); - opts.issuer_permissions = opts.flags; - opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); - create_asset(get_account(creator).name, symbol, 2, opts, {}, true); - } - - void dbg_make_mia(string creator, string symbol) - { - asset_options opts; - opts.flags &= ~white_list; - opts.issuer_permissions = opts.flags; - opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); - bitasset_options bopts; - create_asset(get_account(creator).name, symbol, 2, opts, bopts, true); - } - - void dbg_push_blocks( const std::string& src_filename, uint32_t count ) - { - use_debug_api(); - (*_remote_debug)->debug_push_blocks( src_filename, count ); - (*_remote_debug)->debug_stream_json_objects_flush(); - } - - void dbg_generate_blocks( const std::string& debug_wif_key, uint32_t count ) - { - use_debug_api(); - (*_remote_debug)->debug_generate_blocks( debug_wif_key, count ); - (*_remote_debug)->debug_stream_json_objects_flush(); - } - - void dbg_stream_json_objects( const std::string& filename ) - { - use_debug_api(); - (*_remote_debug)->debug_stream_json_objects( filename ); - (*_remote_debug)->debug_stream_json_objects_flush(); - } - - void dbg_update_object( const fc::variant_object& update ) - { - use_debug_api(); - (*_remote_debug)->debug_update_object( update ); - (*_remote_debug)->debug_stream_json_objects_flush(); - } - - void use_network_node_api() - { - if( _remote_net_node ) - return; - try - { - _remote_net_node = _remote_api->network_node(); - } - catch( const fc::exception& e ) - { - std::cerr << "\nCouldn't get network node API. You probably are not configured\n" - "to access the network API on the witness_node you are\n" - "connecting to. Please follow the instructions in README.md to set up an apiaccess file.\n" - "\n"; - throw(e); - } - } - - void use_debug_api() - { - if( _remote_debug ) - return; - try - { - _remote_debug = _remote_api->debug(); - } - catch( const fc::exception& e ) - { - std::cerr << "\nCouldn't get debug node API. You probably are not configured\n" - "to access the debug API on the node you are connecting to.\n" - "\n" - "To fix this problem:\n" - "- Please ensure you are running debug_node, not witness_node.\n" - "- Please follow the instructions in README.md to set up an apiaccess file.\n" - "\n"; - } - } - - void network_add_nodes( const vector& nodes ) - { - use_network_node_api(); - for( const string& node_address : nodes ) - { - (*_remote_net_node)->add_node( fc::ip::endpoint::from_string( node_address ) ); - } - } - - vector< variant > network_get_connected_peers() - { - use_network_node_api(); - const auto peers = (*_remote_net_node)->get_connected_peers(); - vector< variant > result; - result.reserve( peers.size() ); - for( const auto& peer : peers ) - { - variant v; - fc::to_variant( peer, v ); - result.push_back( v ); - } - return result; - } - - void flood_network(string prefix, uint32_t number_of_transactions) - { - try - { - const account_object& master = *_wallet.my_accounts.get().lower_bound("import"); - int number_of_accounts = number_of_transactions / 3; - number_of_transactions -= number_of_accounts; - //auto key = derive_private_key("floodshill", 0); - try { - dbg_make_uia(master.name, "SHILL"); - } catch(...) {/* Ignore; the asset probably already exists.*/} - - fc::time_point start = fc::time_point::now(); - for( int i = 0; i < number_of_accounts; ++i ) - { - std::ostringstream brain_key; - brain_key << "brain key for account " << prefix << i; - signed_transaction trx = create_account_with_brain_key(brain_key.str(), prefix + fc::to_string(i), master.name, master.name, /* broadcast = */ true, /* save wallet = */ false); - } - fc::time_point end = fc::time_point::now(); - ilog("Created ${n} accounts in ${time} milliseconds", - ("n", number_of_accounts)("time", (end - start).count() / 1000)); - - start = fc::time_point::now(); - for( int i = 0; i < number_of_accounts; ++i ) - { - signed_transaction trx = transfer(master.name, prefix + fc::to_string(i), "10", "CORE", "", true); - trx = transfer(master.name, prefix + fc::to_string(i), "1", "CORE", "", true); - } - end = fc::time_point::now(); - ilog("Transferred to ${n} accounts in ${time} milliseconds", - ("n", number_of_accounts*2)("time", (end - start).count() / 1000)); - - start = fc::time_point::now(); - for( int i = 0; i < number_of_accounts; ++i ) - { - signed_transaction trx = issue_asset(prefix + fc::to_string(i), "1000", "SHILL", "", true); - } - end = fc::time_point::now(); - ilog("Issued to ${n} accounts in ${time} milliseconds", - ("n", number_of_accounts)("time", (end - start).count() / 1000)); - } - catch (...) - { - throw; - } - - } - - operation get_prototype_operation( string operation_name ) - { - auto it = _prototype_ops.find( operation_name ); - if( it == _prototype_ops.end() ) - FC_THROW("Unsupported operation: \"${operation_name}\"", ("operation_name", operation_name)); - return it->second; - } - - string _wallet_filename; - wallet_data _wallet; - - map _keys; - fc::sha512 _checksum; - - chain_id_type _chain_id; - fc::api _remote_api; - fc::api _remote_db; - fc::api _remote_net_broadcast; - fc::api _remote_hist; - optional< fc::api > _remote_net_node; - optional< fc::api > _remote_debug; - - flat_map _prototype_ops; - - static_variant_map _operation_which_map = create_static_variant_map< operation >(); - - typedef multi_index_container< - tournament_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > > > tournament_index_type; - tournament_index_type tournament_cache; - - typedef multi_index_container< - match_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > > > match_index_type; - match_index_type match_cache; - - typedef multi_index_container< - game_object, - indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > > > > game_index_type; - game_index_type game_cache; - -#ifdef __unix__ - mode_t _old_umask; -#endif - const string _wallet_filename_extension = ".wallet"; - - mutable map _asset_cache; - }; - - std::string operation_printer::fee(const asset& a)const { - out << " (Fee: " << wallet.get_asset(a.asset_id).amount_to_pretty_string(a) << ")"; - return ""; - } - - template - std::string operation_printer::operator()(const T& op)const + result["build"] = os + " " + bitness; + + return result; + } + + chain_property_object get_chain_properties() const + { + return _remote_db->get_chain_properties(); + } + global_property_object get_global_properties() const + { + return _remote_db->get_global_properties(); + } + dynamic_global_property_object get_dynamic_global_properties() const + { + return _remote_db->get_dynamic_global_properties(); + } + account_object get_account(account_id_type id) const + { + if( _wallet.my_accounts.get().count(id) ) + return *_wallet.my_accounts.get().find(id); + auto rec = _remote_db->get_accounts({id}).front(); + FC_ASSERT(rec); + return *rec; + } + account_object get_account(string account_name_or_id) const + { + FC_ASSERT( account_name_or_id.size() > 0 ); + + if( auto id = maybe_id(account_name_or_id) ) + { + // It's an ID + return get_account(*id); + } else { + // It's a name + if( _wallet.my_accounts.get().count(account_name_or_id) ) + { + auto local_account = *_wallet.my_accounts.get().find(account_name_or_id); + auto blockchain_account = _remote_db->lookup_account_names({account_name_or_id}).front(); + FC_ASSERT( blockchain_account ); + if (local_account.id != blockchain_account->id) + elog("my account id ${id} different from blockchain id ${id2}", ("id", local_account.id)("id2", blockchain_account->id)); + if (local_account.name != blockchain_account->name) + elog("my account name ${id} different from blockchain name ${id2}", ("id", local_account.name)("id2", blockchain_account->name)); + + return *_wallet.my_accounts.get().find(account_name_or_id); + } + auto rec = _remote_db->lookup_account_names({account_name_or_id}).front(); + FC_ASSERT( rec && rec->name == account_name_or_id ); + return *rec; + } + } + account_id_type get_account_id(string account_name_or_id) const + { + return get_account(account_name_or_id).get_id(); + } + optional find_asset(asset_id_type id)const + { + auto rec = _remote_db->get_assets({id}).front(); + if( rec ) + _asset_cache[id] = *rec; + return rec; + } + optional find_asset(string asset_symbol_or_id)const + { + FC_ASSERT( asset_symbol_or_id.size() > 0 ); + + if( auto id = maybe_id(asset_symbol_or_id) ) + { + // It's an ID + return find_asset(*id); + } else { + // It's a symbol + auto rec = _remote_db->lookup_asset_symbols({asset_symbol_or_id}).front(); + if( rec ) + { + if( rec->symbol != asset_symbol_or_id ) + return optional(); + + _asset_cache[rec->get_id()] = *rec; + } + return rec; + } + } + asset_object get_asset(asset_id_type id)const + { + auto opt = find_asset(id); + FC_ASSERT(opt); + return *opt; + } + asset_object get_asset(string asset_symbol_or_id)const + { + auto opt = find_asset(asset_symbol_or_id); + FC_ASSERT(opt); + return *opt; + } + + asset_id_type get_asset_id(string asset_symbol_or_id) const + { + FC_ASSERT( asset_symbol_or_id.size() > 0 ); + vector> opt_asset; + if( std::isdigit( asset_symbol_or_id.front() ) ) + return fc::variant(asset_symbol_or_id).as(); + opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} ); + FC_ASSERT( (opt_asset.size() > 0) && (opt_asset[0].valid()) ); + return opt_asset[0]->id; + } + + string get_wallet_filename() const + { + return _wallet_filename; + } + + fc::ecc::private_key get_private_key(const public_key_type& id)const + { + auto it = _keys.find(id); + FC_ASSERT( it != _keys.end() ); + + fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); + FC_ASSERT( privkey ); + return *privkey; + } + + fc::ecc::private_key get_private_key_for_account(const account_object& account)const + { + vector active_keys = account.active.get_keys(); + if (active_keys.size() != 1) + FC_THROW("Expecting a simple authority with one active key"); + return get_private_key(active_keys.front()); + } + + // imports the private key into the wallet, and associate it in some way (?) with the + // given account name. + // @returns true if the key matches a current active/owner/memo key for the named + // account, false otherwise (but it is stored either way) + bool import_key(string account_name_or_id, string wif_key) + { + fc::optional optional_private_key = wif_to_key(wif_key); + if (!optional_private_key) + FC_THROW("Invalid private key"); + graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key(); + + account_object account = get_account( account_name_or_id ); + + // make a list of all current public keys for the named account + flat_set all_keys_for_account; + std::vector active_keys = account.active.get_keys(); + std::vector owner_keys = account.owner.get_keys(); + std::copy(active_keys.begin(), active_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); + std::copy(owner_keys.begin(), owner_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end())); + all_keys_for_account.insert(account.options.memo_key); + + _keys[wif_pub_key] = wif_key; + + _wallet.update_account(account); + + _wallet.extra_keys[account.id].insert(wif_pub_key); + + return all_keys_for_account.find(wif_pub_key) != all_keys_for_account.end(); + } + + vector< signed_transaction > import_balance( string name_or_id, const vector& wif_keys, bool broadcast ); + + void game_in_new_state(const game_object& game_obj) + { try { + if (game_obj.get_state() == game_state::expecting_commit_moves) + { + if (game_obj.players.size() != 2) // we only support RPS, a 2 player game + return; + const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get(); + for (unsigned i = 0; i < 2; ++i) + { + if (!rps_details.commit_moves.at(i)) // if this player hasn't committed their move { - //balance_accumulator acc; - //op.get_balance_delta( acc, result ); - auto a = wallet.get_asset( op.fee.asset_id ); - auto payer = wallet.get_account( op.fee_payer() ); - - string op_name = fc::get_typename::name(); - if( op_name.find_last_of(':') != string::npos ) - op_name.erase(0, op_name.find_last_of(':')+1); - out << op_name <<" "; - // out << "balance delta: " << fc::json::to_string(acc.balance) <<" "; - out << payer.name << " fee: " << a.amount_to_pretty_string( op.fee ); - operation_result_printer rprinter(wallet); - std::string str_result = result.visit(rprinter); - if( str_result != "" ) - out << " result: " << str_result; - return ""; - } - std::string operation_printer::operator()(const transfer_from_blind_operation& op)const - { - auto a = wallet.get_asset( op.fee.asset_id ); - auto receiver = wallet.get_account( op.to ); - - out << receiver.name - << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; - return ""; - } - std::string operation_printer::operator()(const transfer_to_blind_operation& op)const - { - auto fa = wallet.get_asset( op.fee.asset_id ); - auto a = wallet.get_asset( op.amount.asset_id ); - auto sender = wallet.get_account( op.from ); - - out << sender.name - << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"") - << " fee: " << fa.amount_to_pretty_string( op.fee ); - return ""; - } - string operation_printer::operator()(const transfer_operation& op) const - { - out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) - << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; - std::string memo; - if( op.memo ) + const account_id_type& account_id = game_obj.players[i]; + if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us { - if( wallet.is_locked() ) + ilog("Game ${game_id}: it is ${account_name}'s turn to commit their move", + ("game_id", game_obj.id) + ("account_name", get_account(account_id).name)); + } + } + } + } + else if (game_obj.get_state() == game_state::expecting_reveal_moves) + { + if (game_obj.players.size() != 2) // we only support RPS, a 2 player game + return; + const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get(); + for (unsigned i = 0; i < 2; ++i) + { + if (rps_details.commit_moves.at(i) && + !rps_details.reveal_moves.at(i)) // if this player has committed but not revealed + { + const account_id_type& account_id = game_obj.players[i]; + if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us + { + if (self.is_locked()) + ilog("Game ${game_id}: unable to broadcast ${account_name}'s reveal because the wallet is locked", + ("game_id", game_obj.id) + ("account_name", get_account(account_id).name)); + else { - out << " -- Unlock wallet to see memo."; - } else { - try { - FC_ASSERT(wallet._keys.count(op.memo->to) || wallet._keys.count(op.memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", ("to", op.memo->to)("from",op.memo->from)); - if( wallet._keys.count(op.memo->to) ) { - auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); - FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->from); - out << " -- Memo: " << memo; - } else { - auto my_key = wif_to_key(wallet._keys.at(op.memo->from)); - FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->to); - out << " -- Memo: " << memo; - } - } catch (const fc::exception& e) { - out << " -- could not decrypt memo"; - elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); + ilog("Game ${game_id}: it is ${account_name}'s turn to reveal their move", + ("game_id", game_obj.id) + ("account_name", get_account(account_id).name)); + + auto iter = _wallet.committed_game_moves.find(*rps_details.commit_moves.at(i)); + if (iter != _wallet.committed_game_moves.end()) + { + const rock_paper_scissors_throw_reveal& reveal = iter->second; + + game_move_operation move_operation; + move_operation.game_id = game_obj.id; + move_operation.player_account_id = account_id; + move_operation.move = reveal; + + signed_transaction trx; + trx.operations = {move_operation}; + set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); + trx.validate(); + ilog("Broadcasting reveal..."); + trx = sign_transaction(trx, true); + ilog("Reveal broadcast, transaction id is ${id}", ("id", trx.id())); } } } - fee(op.fee); - return memo; } + } + } + } FC_RETHROW_EXCEPTIONS(warn, "") } - std::string operation_printer::operator()(const account_create_operation& op) const + void match_in_new_state(const match_object& match_obj) + { try { + if (match_obj.get_state() == match_state::match_in_progress) + { + for (const account_id_type& account_id : match_obj.players) + { + if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) { - out << "Create Account '" << op.name << "'"; - return fee(op.fee); - } - - std::string operation_printer::operator()(const account_update_operation& op) const - { - out << "Update Account '" << wallet.get_account(op.account).name << "'"; - return fee(op.fee); - } - - std::string operation_printer::operator()(const asset_create_operation& op) const - { - out << "Create "; - if( op.bitasset_opts.valid() ) - out << "BitAsset "; - else - out << "User-Issue Asset "; - out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name; - return fee(op.fee); - } - - std::string operation_printer::operator()(const asset_dividend_distribution_operation& op)const - { - asset_object dividend_paying_asset = wallet.get_asset(op.dividend_asset_id); - account_object receiver = wallet.get_account(op.account_id); - - out << receiver.name << " received dividend payments for " << dividend_paying_asset.symbol << ": "; - std::vector pretty_payout_amounts; - for (const asset& payment : op.amounts) + ilog("Match ${match} is now in progress for player ${account}", + ("match", match_obj.id)("account", get_account(account_id).name)); + for (const game_id_type& game_id : match_obj.games) { - asset_object payout_asset = wallet.get_asset(payment.asset_id); - pretty_payout_amounts.push_back(payout_asset.amount_to_pretty_string(payment)); + game_object game_obj = get_object(game_id); + auto insert_result = game_cache.insert(game_obj); + if (insert_result.second) + game_in_new_state(game_obj); } - out << boost::algorithm::join(pretty_payout_amounts, ", "); - return ""; } + } + } + } FC_RETHROW_EXCEPTIONS(warn, "") } - std::string operation_printer::operator()(const tournament_payout_operation& op)const + // Cache all matches in the tournament, which will also register us for + // updates on those matches + void monitor_matches_in_tournament(const tournament_object& tournament_obj) + { try { + tournament_details_object tournament_details = get_object(tournament_obj.tournament_details_id); + for (const match_id_type& match_id : tournament_details.matches) + { + match_object match_obj = get_object(match_id); + auto insert_result = match_cache.insert(match_obj); + if (insert_result.second) + match_in_new_state(match_obj); + } + } FC_RETHROW_EXCEPTIONS(warn, "") } + + void resync_active_tournaments() + { + // check to see if any of our accounts are registered for tournaments + // the real purpose of this is to ensure that we are subscribed for callbacks on these tournaments + ilog("Checking my accounts for active tournaments",); + tournament_cache.clear(); + match_cache.clear(); + game_cache.clear(); + for (const account_object& my_account : _wallet.my_accounts) + { + std::vector tournament_ids = _remote_db->get_registered_tournaments(my_account.id, 100); + for (const tournament_id_type& tournament_id : tournament_ids) + { + try { - asset_object payout_asset = wallet.get_asset(op.payout_amount.asset_id); - - out << "Tournament #" << std::string(object_id_type(op.tournament_id)) << " Payout : " - << "Account '" << wallet.get_account(op.payout_account_id).name - << "', Amount " << payout_asset.amount_to_pretty_string(op.payout_amount) << ", Type " - << (op.type == payout_type::buyin_refund ? "buyin refund" : (op.type == payout_type::rake_fee ? "rake fee" : "prize award")) - << "."; - return ""; + tournament_object tournament = get_object(tournament_id); + auto insert_result = tournament_cache.insert(tournament); + if (insert_result.second) + { + // then this is the first time we've seen this tournament + monitor_matches_in_tournament(tournament); + } + tournament_ids.push_back(tournament.id); } - - std::string operation_result_printer::operator()(const void_result& x) const + catch (const fc::exception& e) { - return ""; + edump((e)(tournament_id)); } + } + if (!tournament_ids.empty()) + ilog("Account ${my_account} is registered for tournaments: ${tournaments}", ("my_account", my_account.name)("tournaments", tournament_ids)); + else + ilog("Account ${my_account} is not registered for any tournaments", ("my_account", my_account.name)); + } + } - std::string operation_result_printer::operator()(const object_id_type& oid) + bool load_wallet_file(string wallet_filename = "") + { + // TODO: Merge imported wallet with existing wallet, + // instead of replacing it + if( wallet_filename == "" ) + wallet_filename = _wallet_filename; + + if( ! fc::exists( wallet_filename ) ) + return false; + + _wallet = fc::json::from_file( wallet_filename ).as< wallet_data >(); + if( _wallet.chain_id != _chain_id ) + FC_THROW( "Wallet chain ID does not match", + ("wallet.chain_id", _wallet.chain_id) + ("chain_id", _chain_id) ); + + size_t account_pagination = 100; + vector< account_id_type > account_ids_to_send; + size_t n = _wallet.my_accounts.size(); + account_ids_to_send.reserve( std::min( account_pagination, n ) ); + auto it = _wallet.my_accounts.begin(); + + for( size_t start=0; start start ); + account_ids_to_send.clear(); + std::vector< account_object > old_accounts; + for( size_t i=start; i > accounts = _remote_db->get_accounts(account_ids_to_send); + // server response should be same length as request + FC_ASSERT( accounts.size() == account_ids_to_send.size() ); + size_t i = 0; + for( const optional< account_object >& acct : accounts ) + { + account_object& old_acct = old_accounts[i]; + if( !acct.valid() ) { - return std::string(oid); + elog( "Could not find account ${id} : \"${name}\" does not exist on the chain!", ("id", old_acct.id)("name", old_acct.name) ); + i++; + continue; } - - std::string operation_result_printer::operator()(const asset& a) + // this check makes sure the server didn't send results + // in a different order, or accounts we didn't request + FC_ASSERT( acct->id == old_acct.id ); + if( fc::json::to_string(*acct) != fc::json::to_string(old_acct) ) { - return _wallet.get_asset(a.asset_id).amount_to_pretty_string(a); + wlog( "Account ${id} : \"${name}\" updated on chain", ("id", acct->id)("name", acct->name) ); + } + _wallet.update_account( *acct ); + i++; + } + } + + resync_active_tournaments(); + + return true; + } + void save_wallet_file(string wallet_filename = "") + { + // + // Serialize in memory, then save to disk + // + // This approach lessens the risk of a partially written wallet + // if exceptions are thrown in serialization + // + + encrypt_keys(); + + if( wallet_filename == "" ) + wallet_filename = _wallet_filename; + + wlog( "saving wallet to file ${fn}", ("fn", wallet_filename) ); + + string data = fc::json::to_pretty_string( _wallet ); + try + { + enable_umask_protection(); + // + // Parentheses on the following declaration fails to compile, + // due to the Most Vexing Parse. Thanks, C++ + // + // http://en.wikipedia.org/wiki/Most_vexing_parse + // + fc::ofstream outfile{ fc::path( wallet_filename ) }; + outfile.write( data.c_str(), data.length() ); + outfile.flush(); + outfile.close(); + disable_umask_protection(); + } + catch(...) + { + disable_umask_protection(); + throw; + } + } + + transaction_handle_type begin_builder_transaction() + { + int trx_handle = _builder_transactions.empty()? 0 + : (--_builder_transactions.end())->first + 1; + _builder_transactions[trx_handle]; + return trx_handle; + } + void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) + { + FC_ASSERT(_builder_transactions.count(transaction_handle)); + _builder_transactions[transaction_handle].operations.emplace_back(op); + } + void replace_operation_in_builder_transaction(transaction_handle_type handle, + uint32_t operation_index, + const operation& new_op) + { + FC_ASSERT(_builder_transactions.count(handle)); + signed_transaction& trx = _builder_transactions[handle]; + FC_ASSERT( operation_index < trx.operations.size()); + trx.operations[operation_index] = new_op; + } + asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL) + { + FC_ASSERT(_builder_transactions.count(handle)); + + auto fee_asset_obj = get_asset(fee_asset); + asset total_fee = fee_asset_obj.amount(0); + + auto gprops = _remote_db->get_global_properties().parameters; + if( fee_asset_obj.get_id() != asset_id_type() ) + { + for( auto& op : _builder_transactions[handle].operations ) + total_fee += gprops.current_fees->set_fee( op, fee_asset_obj.options.core_exchange_rate ); + + FC_ASSERT((total_fee * fee_asset_obj.options.core_exchange_rate).amount <= + get_object(fee_asset_obj.dynamic_asset_data_id).fee_pool, + "Cannot pay fees in ${asset}, as this asset's fee pool is insufficiently funded.", + ("asset", fee_asset_obj.symbol)); + } else { + for( auto& op : _builder_transactions[handle].operations ) + total_fee += gprops.current_fees->set_fee( op ); + } + + return total_fee; + } + transaction preview_builder_transaction(transaction_handle_type handle) + { + FC_ASSERT(_builder_transactions.count(handle)); + return _builder_transactions[handle]; + } + signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true) + { + FC_ASSERT(_builder_transactions.count(transaction_handle)); + + return _builder_transactions[transaction_handle] = sign_transaction(_builder_transactions[transaction_handle], broadcast); + } + signed_transaction propose_builder_transaction( + transaction_handle_type handle, + time_point_sec expiration = time_point::now() + fc::minutes(1), + uint32_t review_period_seconds = 0, bool broadcast = true) + { + FC_ASSERT(_builder_transactions.count(handle)); + proposal_create_operation op; + op.expiration_time = expiration; + signed_transaction& trx = _builder_transactions[handle]; + std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops), + [](const operation& op) -> op_wrapper { return op; }); + if( review_period_seconds ) + op.review_period_seconds = review_period_seconds; + trx.operations = {op}; + _remote_db->get_global_properties().parameters.current_fees->set_fee( trx.operations.front() ); + + return trx = sign_transaction(trx, broadcast); + } + + signed_transaction propose_builder_transaction2( + transaction_handle_type handle, + string account_name_or_id, + time_point_sec expiration = time_point::now() + fc::minutes(1), + uint32_t review_period_seconds = 0, bool broadcast = true) + { + FC_ASSERT(_builder_transactions.count(handle)); + proposal_create_operation op; + op.fee_paying_account = get_account(account_name_or_id).get_id(); + op.expiration_time = expiration; + signed_transaction& trx = _builder_transactions[handle]; + std::transform(trx.operations.begin(), trx.operations.end(), std::back_inserter(op.proposed_ops), + [](const operation& op) -> op_wrapper { return op; }); + if( review_period_seconds ) + op.review_period_seconds = review_period_seconds; + trx.operations = {op}; + _remote_db->get_global_properties().parameters.current_fees->set_fee( trx.operations.front() ); + + return trx = sign_transaction(trx, broadcast); + } + + void remove_builder_transaction(transaction_handle_type handle) + { + _builder_transactions.erase(handle); + } + + + signed_transaction register_account(string name, + public_key_type owner, + public_key_type active, + string registrar_account, + string referrer_account, + uint32_t referrer_percent, + bool broadcast = false) + { try { + FC_ASSERT( !self.is_locked() ); + FC_ASSERT( is_valid_name(name) ); + account_create_operation account_create_op; + + // #449 referrer_percent is on 0-100 scale, if user has larger + // number it means their script is using GRAPHENE_100_PERCENT scale + // instead of 0-100 scale. + FC_ASSERT( referrer_percent <= 100 ); + // TODO: process when pay_from_account is ID + + account_object registrar_account_object = + this->get_account( registrar_account ); + FC_ASSERT( registrar_account_object.is_lifetime_member() ); + + account_id_type registrar_account_id = registrar_account_object.id; + + account_object referrer_account_object = + this->get_account( referrer_account ); + account_create_op.referrer = referrer_account_object.id; + account_create_op.referrer_percent = uint16_t( referrer_percent * GRAPHENE_1_PERCENT ); + + account_create_op.registrar = registrar_account_id; + account_create_op.name = name; + account_create_op.owner = authority(1, owner, 1); + account_create_op.active = authority(1, active, 1); + account_create_op.options.memo_key = active; + + signed_transaction tx; + + tx.operations.push_back( account_create_op ); + + auto current_fees = _remote_db->get_global_properties().parameters.current_fees; + set_operation_fees( tx, current_fees ); + + vector paying_keys = registrar_account_object.active.get_keys(); + + auto dyn_props = get_dynamic_global_properties(); + tx.set_reference_block( dyn_props.head_block_id ); + tx.set_expiration( dyn_props.time + fc::seconds(30) ); + tx.validate(); + + for( public_key_type& key : paying_keys ) + { + auto it = _keys.find(key); + if( it != _keys.end() ) + { + fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); + if( !privkey.valid() ) + { + FC_ASSERT( false, "Malformed private key in _keys" ); + } + tx.sign( *privkey, _chain_id ); + } + } + + if( broadcast ) + _remote_net_broadcast->broadcast_transaction( tx ); + return tx; + } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account)(referrer_account)(referrer_percent)(broadcast) ) } + + + signed_transaction upgrade_account(string name, bool broadcast) + { try { + FC_ASSERT( !self.is_locked() ); + account_object account_obj = get_account(name); + FC_ASSERT( !account_obj.is_lifetime_member() ); + + signed_transaction tx; + account_upgrade_operation op; + op.account_to_upgrade = account_obj.get_id(); + op.upgrade_to_lifetime_member = true; + tx.operations = {op}; + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (name) ) } + + + // This function generates derived keys starting with index 0 and keeps incrementing + // the index until it finds a key that isn't registered in the block chain. To be + // safer, it continues checking for a few more keys to make sure there wasn't a short gap + // caused by a failed registration or the like. + int find_first_unused_derived_key_index(const fc::ecc::private_key& parent_key) + { + int first_unused_index = 0; + int number_of_consecutive_unused_keys = 0; + for (int key_index = 0; ; ++key_index) + { + fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index); + graphene::chain::public_key_type derived_public_key = derived_private_key.get_public_key(); + if( _keys.find(derived_public_key) == _keys.end() ) + { + if (number_of_consecutive_unused_keys) + { + ++number_of_consecutive_unused_keys; + if (number_of_consecutive_unused_keys > 5) + return first_unused_index; + } + else + { + first_unused_index = key_index; + number_of_consecutive_unused_keys = 1; + } + } + else + { + // key_index is used + first_unused_index = 0; + number_of_consecutive_unused_keys = 0; + } + } + } + + signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey, + string account_name, + string registrar_account, + string referrer_account, + bool broadcast = false, + bool save_wallet = true) + { try { + 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); + + int memo_key_index = find_first_unused_derived_key_index(active_privkey); + fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index); + + graphene::chain::public_key_type owner_pubkey = owner_privkey.get_public_key(); + graphene::chain::public_key_type active_pubkey = active_privkey.get_public_key(); + graphene::chain::public_key_type memo_pubkey = memo_privkey.get_public_key(); + + account_create_operation account_create_op; + + // TODO: process when pay_from_account is ID + + account_object registrar_account_object = get_account( registrar_account ); + + account_id_type registrar_account_id = registrar_account_object.id; + + account_object referrer_account_object = get_account( referrer_account ); + account_create_op.referrer = referrer_account_object.id; + account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage; + + account_create_op.registrar = registrar_account_id; + account_create_op.name = account_name; + account_create_op.owner = authority(1, owner_pubkey, 1); + account_create_op.active = authority(1, active_pubkey, 1); + account_create_op.options.memo_key = memo_pubkey; + + // current_fee_schedule() + // find_account(pay_from_account) + + // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule()); + + signed_transaction tx; + + tx.operations.push_back( account_create_op ); + + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + + vector paying_keys = registrar_account_object.active.get_keys(); + + auto dyn_props = get_dynamic_global_properties(); + tx.set_reference_block( dyn_props.head_block_id ); + tx.set_expiration( dyn_props.time + fc::seconds(30) ); + tx.validate(); + + for( public_key_type& key : paying_keys ) + { + auto it = _keys.find(key); + if( it != _keys.end() ) + { + fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); + FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); + tx.sign( *privkey, _chain_id ); + } + } + + // we do not insert owner_privkey here because + // it is intended to only be used for key recovery + _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey )); + _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey )); + if( save_wallet ) + save_wallet_file(); + if( broadcast ) + _remote_net_broadcast->broadcast_transaction( tx ); + return tx; + } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) } + + signed_transaction create_account_with_brain_key(string brain_key, + string account_name, + string registrar_account, + string referrer_account, + bool broadcast = false, + bool save_wallet = true) + { try { + FC_ASSERT( !self.is_locked() ); + string normalized_brain_key = normalize_brain_key( brain_key ); + // TODO: scan blockchain for accounts that exist with same brain key + fc::ecc::private_key owner_privkey = derive_private_key( normalized_brain_key, 0 ); + return create_account_with_private_key(owner_privkey, account_name, registrar_account, referrer_account, broadcast, save_wallet); + } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account) ) } + + + signed_transaction create_asset(string issuer, + string symbol, + uint8_t precision, + asset_options common, + fc::optional bitasset_opts, + bool broadcast = false) + { try { + account_object issuer_account = get_account( issuer ); + FC_ASSERT(!find_asset(symbol).valid(), "Asset with that symbol already exists!"); + + asset_create_operation create_op; + create_op.issuer = issuer_account.id; + create_op.symbol = symbol; + create_op.precision = precision; + create_op.common_options = common; + create_op.bitasset_opts = bitasset_opts; + + signed_transaction tx; + tx.operations.push_back( 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( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) } + + signed_transaction update_asset(string symbol, + optional new_issuer, + asset_options new_options, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + optional new_issuer_account_id; + if (new_issuer) + { + account_object new_issuer_account = get_account(*new_issuer); + new_issuer_account_id = new_issuer_account.id; + } + + asset_update_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_issuer = new_issuer_account_id; + update_op.new_options = new_options; + + signed_transaction tx; + tx.operations.push_back( 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( (symbol)(new_issuer)(new_options)(broadcast) ) } + + signed_transaction update_bitasset(string symbol, + bitasset_options new_options, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_bitasset_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_options = new_options; + + signed_transaction tx; + tx.operations.push_back( 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( (symbol)(new_options)(broadcast) ) } + + signed_transaction update_dividend_asset(string symbol, + dividend_asset_options new_options, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_dividend_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_options = new_options; + + signed_transaction tx; + tx.operations.push_back( 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( (symbol)(new_options)(broadcast) ) } + + signed_transaction update_asset_feed_producers(string symbol, + flat_set new_feed_producers, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_feed_producers_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_feed_producers.reserve(new_feed_producers.size()); + std::transform(new_feed_producers.begin(), new_feed_producers.end(), + std::inserter(update_op.new_feed_producers, update_op.new_feed_producers.end()), + [this](const std::string& account_name_or_id){ return get_account_id(account_name_or_id); }); + + signed_transaction tx; + tx.operations.push_back( 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( (symbol)(new_feed_producers)(broadcast) ) } + + signed_transaction publish_asset_feed(string publishing_account, + string symbol, + price_feed feed, + bool broadcast /* = false */) + { try { + optional asset_to_update = find_asset(symbol); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_publish_feed_operation publish_op; + publish_op.publisher = get_account_id(publishing_account); + publish_op.asset_id = asset_to_update->id; + publish_op.feed = feed; + + signed_transaction tx; + tx.operations.push_back( publish_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) } + + signed_transaction fund_asset_fee_pool(string from, + string symbol, + string amount, + bool broadcast /* = false */) + { try { + account_object from_account = get_account(from); + optional asset_to_fund = find_asset(symbol); + if (!asset_to_fund) + FC_THROW("No asset with that symbol exists!"); + asset_object core_asset = get_asset(asset_id_type()); + + asset_fund_fee_pool_operation fund_op; + fund_op.from_account = from_account.id; + fund_op.asset_id = asset_to_fund->id; + fund_op.amount = core_asset.amount_from_string(amount).amount; + + signed_transaction tx; + tx.operations.push_back( fund_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) } + + signed_transaction reserve_asset(string from, + string amount, + string symbol, + bool broadcast /* = false */) + { try { + account_object from_account = get_account(from); + optional asset_to_reserve = find_asset(symbol); + if (!asset_to_reserve) + FC_THROW("No asset with that symbol exists!"); + + asset_reserve_operation reserve_op; + reserve_op.payer = from_account.id; + reserve_op.amount_to_reserve = asset_to_reserve->amount_from_string(amount); + + signed_transaction tx; + tx.operations.push_back( reserve_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) } + + signed_transaction global_settle_asset(string symbol, + price settle_price, + bool broadcast /* = false */) + { try { + optional asset_to_settle = find_asset(symbol); + if (!asset_to_settle) + FC_THROW("No asset with that symbol exists!"); + + asset_global_settle_operation settle_op; + settle_op.issuer = asset_to_settle->issuer; + settle_op.asset_to_settle = asset_to_settle->id; + settle_op.settle_price = settle_price; + + signed_transaction tx; + tx.operations.push_back( settle_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (symbol)(settle_price)(broadcast) ) } + + signed_transaction settle_asset(string account_to_settle, + string amount_to_settle, + string symbol, + bool broadcast /* = false */) + { try { + optional asset_to_settle = find_asset(symbol); + if (!asset_to_settle) + FC_THROW("No asset with that symbol exists!"); + + asset_settle_operation settle_op; + settle_op.account = get_account_id(account_to_settle); + settle_op.amount = asset_to_settle->amount_from_string(amount_to_settle); + + signed_transaction tx; + tx.operations.push_back( settle_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (account_to_settle)(amount_to_settle)(symbol)(broadcast) ) } + + signed_transaction whitelist_account(string authorizing_account, + string account_to_list, + account_whitelist_operation::account_listing new_listing_status, + bool broadcast /* = false */) + { try { + account_whitelist_operation whitelist_op; + whitelist_op.authorizing_account = get_account_id(authorizing_account); + whitelist_op.account_to_list = get_account_id(account_to_list); + whitelist_op.new_listing = new_listing_status; + + signed_transaction tx; + tx.operations.push_back( whitelist_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } + + signed_transaction create_committee_member(string owner_account, string url, + bool broadcast /* = false */) + { try { + + committee_member_create_operation committee_member_create_op; + committee_member_create_op.committee_member_account = get_account_id(owner_account); + committee_member_create_op.url = url; + if (_remote_db->get_committee_member_by_account(committee_member_create_op.committee_member_account)) + FC_THROW("Account ${owner_account} is already a committee_member", ("owner_account", owner_account)); + + signed_transaction tx; + tx.operations.push_back( committee_member_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) ) } + + witness_object get_witness(string owner_account) + { + try + { + fc::optional witness_id = maybe_id(owner_account); + if (witness_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*witness_id); + std::vector> witness_objects = _remote_db->get_witnesses(ids_to_get); + if (witness_objects.front()) + return *witness_objects.front(); + FC_THROW("No witness 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 witness = _remote_db->get_witness_by_account(owner_account_id); + if (witness) + return *witness; + else + FC_THROW("No witness is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or witness named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + + committee_member_object get_committee_member(string owner_account) + { + try + { + fc::optional committee_member_id = maybe_id(owner_account); + if (committee_member_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*committee_member_id); + std::vector> committee_member_objects = _remote_db->get_committee_members(ids_to_get); + if (committee_member_objects.front()) + return *committee_member_objects.front(); + FC_THROW("No committee_member 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 committee_member = _remote_db->get_committee_member_by_account(owner_account_id); + if (committee_member) + return *committee_member; + else + FC_THROW("No committee_member is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or committee_member named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + + signed_transaction create_witness(string owner_account, + string url, + bool broadcast /* = false */) + { try { + 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); + fc::ecc::private_key witness_private_key = derive_private_key(key_to_wif(active_private_key), witness_key_index); + graphene::chain::public_key_type witness_public_key = witness_private_key.get_public_key(); + + witness_create_operation witness_create_op; + witness_create_op.witness_account = witness_account.id; + witness_create_op.block_signing_key = witness_public_key; + witness_create_op.url = url; + secret_hash_type::encoder enc; + fc::raw::pack(enc, witness_private_key); + fc::raw::pack(enc, secret_hash_type()); + witness_create_op.initial_secret = secret_hash_type::hash(enc.result()); + + + if (_remote_db->get_witness_by_account(witness_create_op.witness_account)) + FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account)); + + signed_transaction tx; + tx.operations.push_back( witness_create_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + _wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(broadcast) ) } + + signed_transaction update_witness(string witness_name, + string url, + string block_signing_key, + bool broadcast /* = false */) + { try { + witness_object witness = get_witness(witness_name); + account_object witness_account = get_account( witness.witness_account ); + fc::ecc::private_key active_private_key = get_private_key_for_account(witness_account); + + witness_update_operation witness_update_op; + witness_update_op.witness = witness.id; + witness_update_op.witness_account = witness_account.id; + if( url != "" ) + witness_update_op.new_url = url; + if( block_signing_key != "" ) { + witness_update_op.new_signing_key = public_key_type( block_signing_key ); + witness_update_op.new_initial_secret = secret_hash_type::hash(secret_hash_type()); + } + + signed_transaction tx; + tx.operations.push_back( witness_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( (witness_name)(url)(block_signing_key)(broadcast) ) } + + template + static WorkerInit _create_worker_initializer( const variant& worker_settings ) + { + WorkerInit result; + from_variant( worker_settings, result ); + return result; + } + + signed_transaction create_worker( + string owner_account, + time_point_sec work_begin_date, + time_point_sec work_end_date, + share_type daily_pay, + string name, + string url, + variant worker_settings, + bool broadcast + ) + { + worker_initializer init; + std::string wtype = worker_settings["type"].get_string(); + + // TODO: Use introspection to do this dispatch + if( wtype == "burn" ) + init = _create_worker_initializer< burn_worker_initializer >( worker_settings ); + else if( wtype == "refund" ) + init = _create_worker_initializer< refund_worker_initializer >( worker_settings ); + else if( wtype == "vesting" ) + init = _create_worker_initializer< vesting_balance_worker_initializer >( worker_settings ); + else + { + FC_ASSERT( false, "unknown worker[\"type\"] value" ); + } + + worker_create_operation op; + op.owner = get_account( owner_account ).id; + op.work_begin_date = work_begin_date; + op.work_end_date = work_end_date; + op.daily_pay = daily_pay; + op.name = name; + op.url = url; + op.initializer = init; + + 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 ); + } + + signed_transaction update_worker_votes( + string account, + worker_vote_delta delta, + bool broadcast + ) + { + account_object acct = get_account( account ); + account_update_operation op; + + // you could probably use a faster algorithm for this, but flat_set is fast enough :) + flat_set< worker_id_type > merged; + merged.reserve( delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); + for( const worker_id_type& wid : delta.vote_for ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + for( const worker_id_type& wid : delta.vote_against ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + for( const worker_id_type& wid : delta.vote_abstain ) + { + bool inserted = merged.insert( wid ).second; + FC_ASSERT( inserted, "worker ${wid} specified multiple times", ("wid", wid) ); + } + + // should be enforced by FC_ASSERT's above + assert( merged.size() == delta.vote_for.size() + delta.vote_against.size() + delta.vote_abstain.size() ); + + vector< object_id_type > query_ids; + for( const worker_id_type& wid : merged ) + query_ids.push_back( wid ); + + flat_set new_votes( acct.options.votes ); + + fc::variants objects = _remote_db->get_objects( query_ids ); + for( const variant& obj : objects ) + { + worker_object wo; + from_variant( obj, wo ); + new_votes.erase( wo.vote_for ); + new_votes.erase( wo.vote_against ); + if( delta.vote_for.find( wo.id ) != delta.vote_for.end() ) + new_votes.insert( wo.vote_for ); + else if( delta.vote_against.find( wo.id ) != delta.vote_against.end() ) + new_votes.insert( wo.vote_against ); + else + assert( delta.vote_abstain.find( wo.id ) != delta.vote_abstain.end() ); + } + + account_update_operation update_op; + update_op.account = acct.id; + update_op.new_options = acct.options; + update_op.new_options->votes = new_votes; + + signed_transaction tx; + tx.operations.push_back( update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } + + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) + { try { + fc::optional vbid = maybe_id( account_name ); + std::vector result; + fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time; + + if( vbid ) + { + result.emplace_back( get_object(*vbid), now ); + return result; + } + + // try casting to avoid a round-trip if we were given an account ID + fc::optional acct_id = maybe_id( account_name ); + if( !acct_id ) + acct_id = get_account( account_name ).id; + + vector< vesting_balance_object > vbos = _remote_db->get_vesting_balances( *acct_id ); + if( vbos.size() == 0 ) + return result; + + for( const vesting_balance_object& vbo : vbos ) + result.emplace_back( vbo, now ); + + return result; + } FC_CAPTURE_AND_RETHROW( (account_name) ) + } + + signed_transaction withdraw_vesting( + string witness_name, + string amount, + string asset_symbol, + bool broadcast = false ) + { try { + asset_object asset_obj = get_asset( asset_symbol ); + fc::optional vbid = maybe_id(witness_name); + if( !vbid ) + { + witness_object wit = get_witness( witness_name ); + FC_ASSERT( wit.pay_vb ); + vbid = wit.pay_vb; + } + + vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid ); + vesting_balance_withdraw_operation vesting_balance_withdraw_op; + + vesting_balance_withdraw_op.vesting_balance = *vbid; + vesting_balance_withdraw_op.owner = vbo.owner; + vesting_balance_withdraw_op.amount = asset_obj.amount_from_string(amount); + + signed_transaction tx; + tx.operations.push_back( vesting_balance_withdraw_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (witness_name)(amount) ) + } + + signed_transaction vote_for_committee_member(string voting_account, + string committee_member, + bool approve, + bool broadcast /* = false */) + { try { + account_object voting_account_object = get_account(voting_account); + account_id_type committee_member_owner_account_id = get_account_id(committee_member); + fc::optional committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id); + if (!committee_member_obj) + FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member)); + if (approve) + { + auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); + } + else + { + unsigned votes_removed = voting_account_object.options.votes.erase(committee_member_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member)); + } + 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)(committee_member)(approve)(broadcast) ) } + + signed_transaction vote_for_witness(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) + { try { + account_object voting_account_object = get_account(voting_account); + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + if (!witness_obj) + FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); + if (approve) + { + auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); + } + else + { + unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); + } + 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)(witness)(approve)(broadcast) ) } + + signed_transaction update_witness_votes(string voting_account, + std::vector witnesses_to_approve, + std::vector witnesses_to_reject, + uint16_t desired_number_of_witnesses, + bool broadcast /* = false */) + { try { + account_object voting_account_object = get_account(voting_account); + for (const std::string& witness : witnesses_to_approve) + { + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + if (!witness_obj) + FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); + auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness)); + } + for (const std::string& witness : witnesses_to_reject) + { + account_id_type witness_owner_account_id = get_account_id(witness); + fc::optional witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id); + if (!witness_obj) + FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); + unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness)); + } + voting_account_object.options.num_witness = desired_number_of_witnesses; + + 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)(witnesses_to_approve)(witnesses_to_reject)(desired_number_of_witnesses)(broadcast) ) } + + signed_transaction set_voting_proxy(string account_to_modify, + optional voting_account, + bool broadcast /* = false */) + { try { + account_object account_object_to_modify = get_account(account_to_modify); + if (voting_account) + { + account_id_type new_voting_account_id = get_account_id(*voting_account); + if (account_object_to_modify.options.voting_account == new_voting_account_id) + FC_THROW("Voting proxy for ${account} is already set to ${voter}", ("account", account_to_modify)("voter", *voting_account)); + account_object_to_modify.options.voting_account = new_voting_account_id; + } + else + { + if (account_object_to_modify.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT) + FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify)); + account_object_to_modify.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + } + + account_update_operation account_update_op; + account_update_op.account = account_object_to_modify.id; + account_update_op.new_options = account_object_to_modify.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( (account_to_modify)(voting_account)(broadcast) ) } + + signed_transaction set_desired_witness_and_committee_member_count(string account_to_modify, + uint16_t desired_number_of_witnesses, + uint16_t desired_number_of_committee_members, + bool broadcast /* = false */) + { try { + account_object account_object_to_modify = get_account(account_to_modify); + + if (account_object_to_modify.options.num_witness == desired_number_of_witnesses && + account_object_to_modify.options.num_committee == desired_number_of_committee_members) + FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members", + ("account", account_to_modify)("witnesses", desired_number_of_witnesses)("committee_members",desired_number_of_witnesses)); + account_object_to_modify.options.num_witness = desired_number_of_witnesses; + account_object_to_modify.options.num_committee = desired_number_of_committee_members; + + account_update_operation account_update_op; + account_update_op.account = account_object_to_modify.id; + account_update_op.new_options = account_object_to_modify.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( (account_to_modify)(desired_number_of_witnesses)(desired_number_of_committee_members)(broadcast) ) } + + signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) + { + flat_set req_active_approvals; + flat_set req_owner_approvals; + vector other_auths; + + tx.get_required_authorities( req_active_approvals, req_owner_approvals, other_auths ); + + for( const auto& auth : other_auths ) + for( const auto& a : auth.account_auths ) + req_active_approvals.insert(a.first); + + // std::merge lets us de-duplicate account_id's that occur in both + // sets, and dump them into a vector (as required by remote_db api) + // at the same time + vector v_approving_account_ids; + std::merge(req_active_approvals.begin(), req_active_approvals.end(), + req_owner_approvals.begin() , req_owner_approvals.end(), + std::back_inserter(v_approving_account_ids)); + + /// TODO: fetch the accounts specified via other_auths as well. + + vector< optional > approving_account_objects = + _remote_db->get_accounts( v_approving_account_ids ); + + /// TODO: recursively check one layer deeper in the authority tree for keys + + FC_ASSERT( approving_account_objects.size() == v_approving_account_ids.size() ); + + flat_map approving_account_lut; + size_t i = 0; + for( optional& approving_acct : approving_account_objects ) + { + if( !approving_acct.valid() ) + { + wlog( "operation_get_required_auths said approval of non-existing account ${id} was needed", + ("id", v_approving_account_ids[i]) ); + i++; + continue; + } + approving_account_lut[ approving_acct->id ] = &(*approving_acct); + i++; + } + + flat_set approving_key_set; + for( account_id_type& acct_id : req_active_approvals ) + { + const auto it = approving_account_lut.find( acct_id ); + if( it == approving_account_lut.end() ) + continue; + const account_object* acct = it->second; + vector v_approving_keys = acct->active.get_keys(); + for( const public_key_type& approving_key : v_approving_keys ) + approving_key_set.insert( approving_key ); + } + for( account_id_type& acct_id : req_owner_approvals ) + { + const auto it = approving_account_lut.find( acct_id ); + if( it == approving_account_lut.end() ) + continue; + const account_object* acct = it->second; + vector v_approving_keys = acct->owner.get_keys(); + for( const public_key_type& approving_key : v_approving_keys ) + approving_key_set.insert( approving_key ); + } + for( const authority& a : other_auths ) + { + for( const auto& k : a.key_auths ) + approving_key_set.insert( k.first ); + } + + auto dyn_props = get_dynamic_global_properties(); + tx.set_reference_block( dyn_props.head_block_id ); + + // first, some bookkeeping, expire old items from _recently_generated_transactions + // since transactions include the head block id, we just need the index for keeping transactions unique + // when there are multiple transactions in the same block. choose a time period that should be at + // least one block long, even in the worst case. 2 minutes ought to be plenty. + fc::time_point_sec oldest_transaction_ids_to_track(dyn_props.time - fc::minutes(2)); + auto oldest_transaction_record_iter = _recently_generated_transactions.get().lower_bound(oldest_transaction_ids_to_track); + auto begin_iter = _recently_generated_transactions.get().begin(); + _recently_generated_transactions.get().erase(begin_iter, oldest_transaction_record_iter); + + uint32_t expiration_time_offset = 0; + for (;;) + { + tx.set_expiration( dyn_props.time + fc::seconds(30 + expiration_time_offset) ); + tx.signatures.clear(); + + for( public_key_type& key : approving_key_set ) + { + auto it = _keys.find(key); + if( it != _keys.end() ) + { + fc::optional privkey = wif_to_key( it->second ); + FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); + tx.sign( *privkey, _chain_id ); + } + /// TODO: if transaction has enough signatures to be "valid" don't add any more, + /// there are cases where the wallet may have more keys than strictly necessary and + /// the transaction will be rejected if the transaction validates without requiring + /// all signatures provided + } + + graphene::chain::transaction_id_type this_transaction_id = tx.id(); + auto iter = _recently_generated_transactions.find(this_transaction_id); + if (iter == _recently_generated_transactions.end()) + { + // we haven't generated this transaction before, the usual case + recently_generated_transaction_record this_transaction_record; + this_transaction_record.generation_time = dyn_props.time; + this_transaction_record.transaction_id = this_transaction_id; + _recently_generated_transactions.insert(this_transaction_record); + break; + } + + // else we've generated a dupe, increment expiration time and re-sign it + ++expiration_time_offset; + } + + if( broadcast ) + { + try + { + _remote_net_broadcast->broadcast_transaction( tx ); + } + catch (const fc::exception& e) + { + elog("Caught exception while broadcasting tx ${id}: ${e}", ("id", tx.id().str())("e", e.to_detail_string()) ); + throw; + } + } + + return tx; + } + + signed_transaction sell_asset(string seller_account, + string amount_to_sell, + string symbol_to_sell, + string min_to_receive, + string symbol_to_receive, + uint32_t timeout_sec = 0, + bool fill_or_kill = false, + bool broadcast = false) + { + account_object seller = get_account( seller_account ); + + limit_order_create_operation op; + op.seller = seller.id; + op.amount_to_sell = get_asset(symbol_to_sell).amount_from_string(amount_to_sell); + op.min_to_receive = get_asset(symbol_to_receive).amount_from_string(min_to_receive); + if( timeout_sec ) + op.expiration = fc::time_point::now() + fc::seconds(timeout_sec); + op.fill_or_kill = fill_or_kill; + + 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 ); + } + + signed_transaction borrow_asset(string seller_name, string amount_to_borrow, string asset_symbol, + string amount_of_collateral, bool broadcast = false) + { + account_object seller = get_account(seller_name); + asset_object mia = get_asset(asset_symbol); + FC_ASSERT(mia.is_market_issued()); + asset_object collateral = get_asset(get_object(*mia.bitasset_data_id).options.short_backing_asset); + + call_order_update_operation op; + op.funding_account = seller.id; + op.delta_debt = mia.amount_from_string(amount_to_borrow); + op.delta_collateral = collateral.amount_from_string(amount_of_collateral); + + signed_transaction trx; + trx.operations = {op}; + set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); + trx.validate(); + idump((broadcast)); + + return sign_transaction(trx, broadcast); + } + + signed_transaction cancel_order(object_id_type order_id, bool broadcast = false) + { try { + FC_ASSERT(!is_locked()); + FC_ASSERT(order_id.space() == protocol_ids, "Invalid order ID ${id}", ("id", order_id)); + signed_transaction trx; + + limit_order_cancel_operation op; + op.fee_paying_account = get_object(order_id).seller; + op.order = order_id; + trx.operations = {op}; + set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); + + trx.validate(); + return sign_transaction(trx, broadcast); + } FC_CAPTURE_AND_RETHROW((order_id)) } + + signed_transaction transfer(string from, string to, string amount, + string asset_symbol, string memo, bool broadcast = false) + { try { + FC_ASSERT( !self.is_locked() ); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + + account_object from_account = get_account(from); + account_object to_account = get_account(to); + account_id_type from_id = from_account.id; + account_id_type to_id = get_account_id(to); + + transfer_operation xfer_op; + + xfer_op.from = from_id; + xfer_op.to = to_id; + xfer_op.amount = asset_obj->amount_from_string(amount); + + if( memo.size() ) + { + xfer_op.memo = memo_data(); + xfer_op.memo->from = from_account.options.memo_key; + xfer_op.memo->to = to_account.options.memo_key; + xfer_op.memo->set_message(get_private_key(from_account.options.memo_key), + to_account.options.memo_key, memo); + } + + signed_transaction tx; + tx.operations.push_back(xfer_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } + + signed_transaction issue_asset(string to_account, string amount, string symbol, + string memo, bool broadcast = false) + { + auto asset_obj = get_asset(symbol); + + account_object to = get_account(to_account); + account_object issuer = get_account(asset_obj.issuer); + + asset_issue_operation issue_op; + issue_op.issuer = asset_obj.issuer; + issue_op.asset_to_issue = asset_obj.amount_from_string(amount); + issue_op.issue_to_account = to.id; + + if( memo.size() ) + { + issue_op.memo = memo_data(); + issue_op.memo->from = issuer.options.memo_key; + issue_op.memo->to = to.options.memo_key; + issue_op.memo->set_message(get_private_key(issuer.options.memo_key), + to.options.memo_key, memo); + } + + signed_transaction tx; + tx.operations.push_back(issue_op); + set_operation_fees(tx,_remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } + + std::map> get_result_formatters() const + { + std::map > m; + m["help"] = [](variant result, const fc::variants& a) + { + return result.get_string(); + }; + + m["gethelp"] = [](variant result, const fc::variants& a) + { + return result.get_string(); + }; + + m["get_account_history"] = [this](variant result, const fc::variants& a) + { + auto r = result.as>(); + std::stringstream ss; + + for( operation_detail& d : r ) + { + operation_history_object& i = d.op; + auto b = _remote_db->get_block_header(i.block_num); + FC_ASSERT(b); + ss << b->timestamp.to_iso_string() << " "; + i.op.visit(operation_printer(ss, *this, i.result)); + ss << " \n"; + } + + return ss.str(); + }; + + m["list_account_balances"] = [this](variant result, const fc::variants& a) + { + auto r = result.as>(); + vector asset_recs; + std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { + return get_asset(a.asset_id); + }); + + std::stringstream ss; + for( unsigned i = 0; i < asset_recs.size(); ++i ) + ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; + + return ss.str(); + }; + + m["get_blind_balances"] = [this](variant result, const fc::variants& a) + { + auto r = result.as>(); + vector asset_recs; + std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { + return get_asset(a.asset_id); + }); + + std::stringstream ss; + for( unsigned i = 0; i < asset_recs.size(); ++i ) + ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; + + return ss.str(); + }; + m["transfer_to_blind"] = [this](variant result, const fc::variants& a) + { + auto r = result.as(); + std::stringstream ss; + r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + ss << "\n"; + for( const auto& out : r.outputs ) + { + asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); + ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; + } + return ss.str(); + }; + m["blind_transfer"] = [this](variant result, const fc::variants& a) + { + auto r = result.as(); + std::stringstream ss; + r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + ss << "\n"; + for( const auto& out : r.outputs ) + { + asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); + ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; + } + return ss.str(); + }; + m["receive_blind_transfer"] = [this](variant result, const fc::variants& a) + { + auto r = result.as(); + std::stringstream ss; + asset_object as = get_asset( r.amount.asset_id ); + ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; + return ss.str(); + }; + m["blind_history"] = [this](variant result, const fc::variants& a) + { + auto records = result.as>(); + std::stringstream ss; + ss << "WHEN " + << " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n"; + ss << "====================================================================================\n"; + for( auto& r : records ) + { + asset_object as = get_asset( r.amount.asset_id ); + ss << fc::get_approximate_relative_time_string( r.date ) + << " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; + } + return ss.str(); + }; + m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a) + { + const vector tournaments = result.as >(); + std::stringstream ss; + ss << "ID GAME BUY IN PLAYERS\n"; + ss << "====================================================================================\n"; + for( const tournament_object& tournament_obj : tournaments ) + { + asset_object buy_in_asset = get_asset(tournament_obj.options.buy_in.asset_id); + ss << fc::variant(tournament_obj.id).as() << " " + << buy_in_asset.amount_to_pretty_string(tournament_obj.options.buy_in.amount) << " " + << tournament_obj.options.number_of_players << " players\n"; + switch (tournament_obj.get_state()) + { + case tournament_state::accepting_registrations: + { + ss << " Waiting for players, " << tournament_obj.registered_players << " of " << tournament_obj.options.number_of_players << " have registered\n"; + ss << " If enough players register, the game will start "; + if (tournament_obj.options.start_time) + ss << "at " << tournament_obj.options.start_time->to_iso_string() << "\n"; + else + ss << *tournament_obj.options.start_delay << " seconds after the last player registers\n"; + break; + } + case tournament_state::awaiting_start: + { + ss << " All players have registered, tournament will start at " << tournament_obj.start_time->to_iso_string() << "\n"; + break; + } + case tournament_state::in_progress: + { + ss << " Tournament started at " << tournament_obj.start_time->to_iso_string() << "\n"; + break; + } + case tournament_state::registration_period_expired: + { + ss << " Tournament was canceled at " << tournament_obj.options.registration_deadline.to_iso_string() << ", not enough players registered\n"; + break; + } + case tournament_state::concluded: + { + ss << " Tournament finished at " << tournament_obj.end_time->to_iso_string() << "\n"; + break; + } + } + } + return ss.str(); + }; + m["get_tournament"] = [this](variant result, const fc::variants& a) + { + std::stringstream ss; + + tournament_object tournament = result.as(); + tournament_details_object tournament_details = _remote_db->get_objects({result["tournament_details_id"].as()})[0].as(); + tournament_state state = tournament.get_state(); + if (state == tournament_state::accepting_registrations) + { + ss << "Tournament is accepting registrations\n"; + ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::registration_period_expired) + { + ss << "Tournament registration period expired\n"; + ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::awaiting_start) + { + ss << "Tournament starts at " << tournament.start_time->to_iso_string() << "\n"; + ss << "Players:\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::in_progress || + state == tournament_state::concluded) + { + unsigned num_matches = tournament_details.matches.size(); + uint32_t num_rounds = boost::multiprecision::detail::find_msb(tournament_details.matches.size() + 1); + unsigned num_rows = (num_matches + 1) * 2 - 1; + for (unsigned row = 0; row < num_rows; ++row) + { + for (unsigned round = 0; round <= num_rounds; ++round) + { + unsigned row_offset = (1 << round) - 1; + unsigned row_vertical_spacing = 1 << (round + 1); + if (row >= row_offset && + (row - row_offset) % row_vertical_spacing == 0) + { + unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing; + unsigned first_player_in_round = (num_matches - (num_matches >> round)) * 2; + unsigned player_number = first_player_in_round + player_number_in_round; + + unsigned match_number = player_number / 2; + unsigned player_in_match = player_number % 2; + + std::string player_name; + if (round == num_rounds) + { + match_object match = get_object(tournament_details.matches[num_matches - 1]); + if (match.get_state() == match_state::match_complete && + !match.match_winners.empty()) + { + assert(match.match_winners.size() == 1); + player_name = get_account(*match.match_winners.begin()).name; + } + } + else + { + match_object match = get_object(tournament_details.matches[match_number]); + if (!match.players.empty()) + { + if (player_in_match < match.players.size()) + player_name = get_account(match.players[player_in_match]).name; + else + player_name = "[bye]"; + } + } + + ss << "__"; + ss << std::setfill('_') << std::setw(10) << player_name.substr(0,10); + ss << "__"; + } + else + ss << " "; + + if (round != num_rounds) + { + unsigned round_horizontal_spacing = 1 << round; + unsigned next_row_vertical_spacing = 1 << (round + 2); + for (unsigned i = 0; i < round_horizontal_spacing; ++i) + { + if ((row - 1 - i - row_offset) % next_row_vertical_spacing == 0) + ss << "\\"; + else if ((row - row_vertical_spacing + i - row_offset) % next_row_vertical_spacing == 0) + ss << "/"; + else + ss << " "; + } + } + } + ss << "\n"; + } + } + + return ss.str(); + }; + m["get_order_book"] = [this](variant result, const fc::variants& a) + { + auto orders = result.as(); + auto bids = orders.bids; + auto asks = orders.asks; + std::stringstream ss; + std::stringstream sum_stream; + sum_stream << "Sum(" << orders.base << ')'; + double bid_sum = 0; + double ask_sum = 0; + const int spacing = 20; + + auto prettify_num = [&]( double n ) + { + //ss << n; + if (abs( round( n ) - n ) < 0.00000000001 ) + { + //ss << setiosflags( !ios::fixed ) << (int) n; // doesn't compile on Linux with gcc + ss << (int) n; + } + else if (n - floor(n) < 0.000001) + { + ss << setiosflags( ios::fixed ) << setprecision(10) << n; + } + else + { + ss << setiosflags( ios::fixed ) << setprecision(6) << n; + } + }; + + ss << setprecision( 8 ) << setiosflags( ios::fixed ) << setiosflags( ios::left ); + + ss << ' ' << setw( (spacing * 4) + 6 ) << "BUY ORDERS" << "SELL ORDERS\n" + << ' ' << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) + << orders.base << ' ' << setw( spacing ) << sum_stream.str() + << " " << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) + << orders.base << ' ' << setw( spacing ) << sum_stream.str() + << "\n=====================================================================================" + << "|=====================================================================================\n"; + + for (unsigned i = 0; i < bids.size() || i < asks.size() ; i++) + { + if ( i < bids.size() ) + { + bid_sum += bids[i].base; + ss << ' ' << setw( spacing ); + prettify_num( bids[i].price ); + ss << ' ' << setw( spacing ); + prettify_num( bids[i].quote ); + ss << ' ' << setw( spacing ); + prettify_num( bids[i].base ); + ss << ' ' << setw( spacing ); + prettify_num( bid_sum ); + ss << ' '; + } + else + { + ss << setw( (spacing * 4) + 5 ) << ' '; } - }}} + ss << '|'; + + if ( i < asks.size() ) + { + ask_sum += asks[i].base; + ss << ' ' << setw( spacing ); + prettify_num( asks[i].price ); + ss << ' ' << setw( spacing ); + prettify_num( asks[i].quote ); + ss << ' ' << setw( spacing ); + prettify_num( asks[i].base ); + ss << ' ' << setw( spacing ); + prettify_num( ask_sum ); + } + + ss << '\n'; + } + + ss << endl + << "Buy Total: " << bid_sum << ' ' << orders.base << endl + << "Sell Total: " << ask_sum << ' ' << orders.base << endl; + + return ss.str(); + }; + + return m; + } + + signed_transaction propose_parameter_change( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_values, + bool broadcast = false) + { + FC_ASSERT( !changed_values.contains("current_fees") ); + + const chain_parameters& current_params = get_global_properties().parameters; + chain_parameters new_params = current_params; + fc::reflector::visit( + fc::from_variant_visitor( changed_values, new_params ) + ); + + committee_member_update_global_parameters_operation update_op; + update_op.new_parameters = new_params; + + proposal_create_operation prop_op; + + prop_op.expiration_time = expiration_time; + prop_op.review_period_seconds = current_params.committee_proposal_review_period; + prop_op.fee_paying_account = get_account(proposing_account).id; + + prop_op.proposed_ops.emplace_back( update_op ); + current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); + + signed_transaction tx; + tx.operations.push_back(prop_op); + set_operation_fees(tx, current_params.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } + + signed_transaction propose_fee_change( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_fees, + bool broadcast = false) + { + const chain_parameters& current_params = get_global_properties().parameters; + const fee_schedule_type& current_fees = *(current_params.current_fees); + + flat_map< int, fee_parameters > fee_map; + fee_map.reserve( current_fees.parameters.size() ); + for( const fee_parameters& op_fee : current_fees.parameters ) + fee_map[ op_fee.which() ] = op_fee; + uint32_t scale = current_fees.scale; + + for( const auto& item : changed_fees ) + { + const string& key = item.key(); + if( key == "scale" ) + { + int64_t _scale = item.value().as_int64(); + FC_ASSERT( _scale >= 0 ); + FC_ASSERT( _scale <= std::numeric_limits::max() ); + scale = uint32_t( _scale ); + continue; + } + // is key a number? + auto is_numeric = [&]() -> bool + { + size_t n = key.size(); + for( size_t i=0; isecond; + } + + fee_parameters fp = from_which_variant< fee_parameters >( which, item.value() ); + fee_map[ which ] = fp; + } + + fee_schedule_type new_fees; + + for( const std::pair< int, fee_parameters >& item : fee_map ) + new_fees.parameters.insert( item.second ); + new_fees.scale = scale; + + chain_parameters new_params = current_params; + new_params.current_fees = new_fees; + + committee_member_update_global_parameters_operation update_op; + update_op.new_parameters = new_params; + + proposal_create_operation prop_op; + + prop_op.expiration_time = expiration_time; + prop_op.review_period_seconds = current_params.committee_proposal_review_period; + prop_op.fee_paying_account = get_account(proposing_account).id; + + prop_op.proposed_ops.emplace_back( update_op ); + current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); + + signed_transaction tx; + tx.operations.push_back(prop_op); + set_operation_fees(tx, current_params.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } + + signed_transaction propose_dividend_asset_update( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_values, + bool broadcast = false) + { + FC_ASSERT( changed_values.contains("asset_to_update") ); + + const chain_parameters& current_params = get_global_properties().parameters; + asset_update_dividend_operation changed_op; + fc::reflector::visit( + fc::from_variant_visitor( changed_values, changed_op ) + ); + + optional asset_to_update = find_asset(changed_op.asset_to_update); + if (!asset_to_update) + FC_THROW("No asset with that symbol exists!"); + + asset_update_dividend_operation update_op; + update_op.issuer = asset_to_update->issuer; + update_op.asset_to_update = asset_to_update->id; + update_op.new_options = changed_op.new_options; + + proposal_create_operation prop_op; + + prop_op.expiration_time = expiration_time; + prop_op.review_period_seconds = current_params.committee_proposal_review_period; + prop_op.fee_paying_account = get_account(proposing_account).id; + + prop_op.proposed_ops.emplace_back( update_op ); + current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); + + signed_transaction tx; + tx.operations.push_back(prop_op); + set_operation_fees(tx, current_params.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } + + signed_transaction approve_proposal( + const string& fee_paying_account, + const string& proposal_id, + const approval_delta& delta, + bool broadcast = false) + { + proposal_update_operation update_op; + + update_op.fee_paying_account = get_account(fee_paying_account).id; + update_op.proposal = fc::variant(proposal_id).as(); + // make sure the proposal exists + get_object( update_op.proposal ); + + for( const std::string& name : delta.active_approvals_to_add ) + update_op.active_approvals_to_add.insert( get_account( name ).id ); + for( const std::string& name : delta.active_approvals_to_remove ) + update_op.active_approvals_to_remove.insert( get_account( name ).id ); + for( const std::string& name : delta.owner_approvals_to_add ) + update_op.owner_approvals_to_add.insert( get_account( name ).id ); + for( const std::string& name : delta.owner_approvals_to_remove ) + update_op.owner_approvals_to_remove.insert( get_account( name ).id ); + for( const std::string& k : delta.key_approvals_to_add ) + update_op.key_approvals_to_add.insert( public_key_type( k ) ); + for( const std::string& k : delta.key_approvals_to_remove ) + update_op.key_approvals_to_remove.insert( public_key_type( k ) ); + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees(tx, get_global_properties().parameters.current_fees); + tx.validate(); + return sign_transaction(tx, broadcast); + } + + void dbg_make_uia(string creator, string symbol) + { + asset_options opts; + opts.flags &= ~(white_list | disable_force_settle | global_settle); + opts.issuer_permissions = opts.flags; + opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); + create_asset(get_account(creator).name, symbol, 2, opts, {}, true); + } + + void dbg_make_mia(string creator, string symbol) + { + asset_options opts; + opts.flags &= ~white_list; + opts.issuer_permissions = opts.flags; + opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); + bitasset_options bopts; + create_asset(get_account(creator).name, symbol, 2, opts, bopts, true); + } + + void dbg_push_blocks( const std::string& src_filename, uint32_t count ) + { + use_debug_api(); + (*_remote_debug)->debug_push_blocks( src_filename, count ); + (*_remote_debug)->debug_stream_json_objects_flush(); + } + + void dbg_generate_blocks( const std::string& debug_wif_key, uint32_t count ) + { + use_debug_api(); + (*_remote_debug)->debug_generate_blocks( debug_wif_key, count ); + (*_remote_debug)->debug_stream_json_objects_flush(); + } + + void dbg_stream_json_objects( const std::string& filename ) + { + use_debug_api(); + (*_remote_debug)->debug_stream_json_objects( filename ); + (*_remote_debug)->debug_stream_json_objects_flush(); + } + + void dbg_update_object( const fc::variant_object& update ) + { + use_debug_api(); + (*_remote_debug)->debug_update_object( update ); + (*_remote_debug)->debug_stream_json_objects_flush(); + } + + void use_network_node_api() + { + if( _remote_net_node ) + return; + try + { + _remote_net_node = _remote_api->network_node(); + } + catch( const fc::exception& e ) + { + std::cerr << "\nCouldn't get network node API. You probably are not configured\n" + "to access the network API on the witness_node you are\n" + "connecting to. Please follow the instructions in README.md to set up an apiaccess file.\n" + "\n"; + throw(e); + } + } + + void use_debug_api() + { + if( _remote_debug ) + return; + try + { + _remote_debug = _remote_api->debug(); + } + catch( const fc::exception& e ) + { + std::cerr << "\nCouldn't get debug node API. You probably are not configured\n" + "to access the debug API on the node you are connecting to.\n" + "\n" + "To fix this problem:\n" + "- Please ensure you are running debug_node, not witness_node.\n" + "- Please follow the instructions in README.md to set up an apiaccess file.\n" + "\n"; + } + } + + void network_add_nodes( const vector& nodes ) + { + use_network_node_api(); + for( const string& node_address : nodes ) + { + (*_remote_net_node)->add_node( fc::ip::endpoint::from_string( node_address ) ); + } + } + + vector< variant > network_get_connected_peers() + { + use_network_node_api(); + const auto peers = (*_remote_net_node)->get_connected_peers(); + vector< variant > result; + result.reserve( peers.size() ); + for( const auto& peer : peers ) + { + variant v; + fc::to_variant( peer, v ); + result.push_back( v ); + } + return result; + } + + void flood_network(string prefix, uint32_t number_of_transactions) + { + try + { + const account_object& master = *_wallet.my_accounts.get().lower_bound("import"); + int number_of_accounts = number_of_transactions / 3; + number_of_transactions -= number_of_accounts; + //auto key = derive_private_key("floodshill", 0); + try { + dbg_make_uia(master.name, "SHILL"); + } catch(...) {/* Ignore; the asset probably already exists.*/} + + fc::time_point start = fc::time_point::now(); + for( int i = 0; i < number_of_accounts; ++i ) + { + std::ostringstream brain_key; + brain_key << "brain key for account " << prefix << i; + signed_transaction trx = create_account_with_brain_key(brain_key.str(), prefix + fc::to_string(i), master.name, master.name, /* broadcast = */ true, /* save wallet = */ false); + } + fc::time_point end = fc::time_point::now(); + ilog("Created ${n} accounts in ${time} milliseconds", + ("n", number_of_accounts)("time", (end - start).count() / 1000)); + + start = fc::time_point::now(); + for( int i = 0; i < number_of_accounts; ++i ) + { + signed_transaction trx = transfer(master.name, prefix + fc::to_string(i), "10", "CORE", "", true); + trx = transfer(master.name, prefix + fc::to_string(i), "1", "CORE", "", true); + } + end = fc::time_point::now(); + ilog("Transferred to ${n} accounts in ${time} milliseconds", + ("n", number_of_accounts*2)("time", (end - start).count() / 1000)); + + start = fc::time_point::now(); + for( int i = 0; i < number_of_accounts; ++i ) + { + signed_transaction trx = issue_asset(prefix + fc::to_string(i), "1000", "SHILL", "", true); + } + end = fc::time_point::now(); + ilog("Issued to ${n} accounts in ${time} milliseconds", + ("n", number_of_accounts)("time", (end - start).count() / 1000)); + } + catch (...) + { + throw; + } + + } + + operation get_prototype_operation( string operation_name ) + { + auto it = _prototype_ops.find( operation_name ); + if( it == _prototype_ops.end() ) + FC_THROW("Unsupported operation: \"${operation_name}\"", ("operation_name", operation_name)); + return it->second; + } + + string _wallet_filename; + wallet_data _wallet; + + map _keys; + fc::sha512 _checksum; + + chain_id_type _chain_id; + fc::api _remote_api; + fc::api _remote_db; + fc::api _remote_net_broadcast; + fc::api _remote_hist; + optional< fc::api > _remote_net_node; + optional< fc::api > _remote_debug; + + flat_map _prototype_ops; + + static_variant_map _operation_which_map = create_static_variant_map< operation >(); + + typedef multi_index_container< + tournament_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > > tournament_index_type; + tournament_index_type tournament_cache; + + typedef multi_index_container< + match_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > > match_index_type; + match_index_type match_cache; + + typedef multi_index_container< + game_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > > game_index_type; + game_index_type game_cache; + +#ifdef __unix__ + mode_t _old_umask; +#endif + const string _wallet_filename_extension = ".wallet"; + + mutable map _asset_cache; +}; + +std::string operation_printer::fee(const asset& a)const { + out << " (Fee: " << wallet.get_asset(a.asset_id).amount_to_pretty_string(a) << ")"; + return ""; +} + +template +std::string operation_printer::operator()(const T& op)const +{ + //balance_accumulator acc; + //op.get_balance_delta( acc, result ); + auto a = wallet.get_asset( op.fee.asset_id ); + auto payer = wallet.get_account( op.fee_payer() ); + + string op_name = fc::get_typename::name(); + if( op_name.find_last_of(':') != string::npos ) + op_name.erase(0, op_name.find_last_of(':')+1); + out << op_name <<" "; + // out << "balance delta: " << fc::json::to_string(acc.balance) <<" "; + out << payer.name << " fee: " << a.amount_to_pretty_string( op.fee ); + operation_result_printer rprinter(wallet); + std::string str_result = result.visit(rprinter); + if( str_result != "" ) + out << " result: " << str_result; + return ""; +} +std::string operation_printer::operator()(const transfer_from_blind_operation& op)const +{ + auto a = wallet.get_asset( op.fee.asset_id ); + auto receiver = wallet.get_account( op.to ); + + out << receiver.name + << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; + return ""; +} +std::string operation_printer::operator()(const transfer_to_blind_operation& op)const +{ + auto fa = wallet.get_asset( op.fee.asset_id ); + auto a = wallet.get_asset( op.amount.asset_id ); + auto sender = wallet.get_account( op.from ); + + out << sender.name + << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"") + << " fee: " << fa.amount_to_pretty_string( op.fee ); + return ""; +} +string operation_printer::operator()(const transfer_operation& op) const +{ + out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) + << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; + std::string memo; + if( op.memo ) + { + if( wallet.is_locked() ) + { + out << " -- Unlock wallet to see memo."; + } else { + try { + FC_ASSERT(wallet._keys.count(op.memo->to) || wallet._keys.count(op.memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", ("to", op.memo->to)("from",op.memo->from)); + if( wallet._keys.count(op.memo->to) ) { + auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + memo = op.memo->get_message(*my_key, op.memo->from); + out << " -- Memo: " << memo; + } else { + auto my_key = wif_to_key(wallet._keys.at(op.memo->from)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + memo = op.memo->get_message(*my_key, op.memo->to); + out << " -- Memo: " << memo; + } + } catch (const fc::exception& e) { + out << " -- could not decrypt memo"; + elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); + } + } + } + fee(op.fee); + return memo; +} + +std::string operation_printer::operator()(const account_create_operation& op) const +{ + out << "Create Account '" << op.name << "'"; + return fee(op.fee); +} + +std::string operation_printer::operator()(const account_update_operation& op) const +{ + out << "Update Account '" << wallet.get_account(op.account).name << "'"; + return fee(op.fee); +} + +std::string operation_printer::operator()(const asset_create_operation& op) const +{ + out << "Create "; + if( op.bitasset_opts.valid() ) + out << "BitAsset "; + else + out << "User-Issue Asset "; + out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name; + return fee(op.fee); +} + +std::string operation_printer::operator()(const asset_dividend_distribution_operation& op)const +{ + asset_object dividend_paying_asset = wallet.get_asset(op.dividend_asset_id); + account_object receiver = wallet.get_account(op.account_id); + + out << receiver.name << " received dividend payments for " << dividend_paying_asset.symbol << ": "; + std::vector pretty_payout_amounts; + for (const asset& payment : op.amounts) + { + asset_object payout_asset = wallet.get_asset(payment.asset_id); + pretty_payout_amounts.push_back(payout_asset.amount_to_pretty_string(payment)); + } + out << boost::algorithm::join(pretty_payout_amounts, ", "); + return ""; +} + +std::string operation_printer::operator()(const tournament_payout_operation& op)const +{ + asset_object payout_asset = wallet.get_asset(op.payout_amount.asset_id); + + out << "Tournament #" << std::string(object_id_type(op.tournament_id)) << " Payout : " + << "Account '" << wallet.get_account(op.payout_account_id).name + << "', Amount " << payout_asset.amount_to_pretty_string(op.payout_amount) << ", Type " + << (op.type == payout_type::buyin_refund ? "buyin refund" : (op.type == payout_type::rake_fee ? "rake fee" : "prize award")) + << "."; + return ""; +} + +std::string operation_result_printer::operator()(const void_result& x) const +{ + return ""; +} + +std::string operation_result_printer::operator()(const object_id_type& oid) +{ + return std::string(oid); +} + +std::string operation_result_printer::operator()(const asset& a) +{ + return _wallet.get_asset(a.asset_id).amount_to_pretty_string(a); +} + +}}} namespace graphene { namespace wallet { - wallet_api::wallet_api(const wallet_data& initial_data, fc::api rapi) - : my(new detail::wallet_api_impl(*this, initial_data, rapi)) - { - } +wallet_api::wallet_api(const wallet_data& initial_data, fc::api rapi) + : my(new detail::wallet_api_impl(*this, initial_data, rapi)) +{ +} - wallet_api::~wallet_api() - { - } +wallet_api::~wallet_api() +{ +} - bool wallet_api::copy_wallet_file(string destination_filename) - { - return my->copy_wallet_file(destination_filename); - } +bool wallet_api::copy_wallet_file(string destination_filename) +{ + return my->copy_wallet_file(destination_filename); +} - optional wallet_api::get_block(uint32_t num) - { - return my->_remote_db->get_block(num); - } +optional wallet_api::get_block(uint32_t num) +{ + return my->_remote_db->get_block(num); +} - uint64_t wallet_api::get_account_count() const - { - return my->_remote_db->get_account_count(); - } +uint64_t wallet_api::get_account_count() const +{ + return my->_remote_db->get_account_count(); +} - vector wallet_api::list_my_accounts() - { - return vector(my->_wallet.my_accounts.begin(), my->_wallet.my_accounts.end()); - } +vector wallet_api::list_my_accounts() +{ + return vector(my->_wallet.my_accounts.begin(), my->_wallet.my_accounts.end()); +} - map wallet_api::list_accounts(const string& lowerbound, uint32_t limit) - { - return my->_remote_db->lookup_accounts(lowerbound, limit); - } +map wallet_api::list_accounts(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_accounts(lowerbound, limit); +} - vector wallet_api::list_account_balances(const string& id) - { - if( auto real_id = detail::maybe_id(id) ) - return my->_remote_db->get_account_balances(*real_id, flat_set()); - return my->_remote_db->get_account_balances(get_account(id).id, flat_set()); - } +vector wallet_api::list_account_balances(const string& id) +{ + if( auto real_id = detail::maybe_id(id) ) + return my->_remote_db->get_account_balances(*real_id, flat_set()); + return my->_remote_db->get_account_balances(get_account(id).id, flat_set()); +} - vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const - { - return my->_remote_db->list_assets( lowerbound, limit ); - } +vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const +{ + return my->_remote_db->list_assets( lowerbound, limit ); +} - vector wallet_api::get_account_history(string name, int limit)const - { - vector result; - auto account_id = get_account(name).get_id(); +vector wallet_api::get_account_history(string name, int limit)const +{ + vector result; + auto account_id = get_account(name).get_id(); - while( limit > 0 ) + while( limit > 0 ) + { + operation_history_id_type start; + if( result.size() ) + { + start = result.back().op.id; + start = start + 1; + } + + + vector current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(), std::min(100,limit), start); + for( auto& o : current ) { + std::stringstream ss; + auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + result.push_back( operation_detail{ memo, ss.str(), o } ); + } + if( (int)current.size() < std::min(100,limit) ) + break; + limit -= current.size(); + } + + return result; +} + + +vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const +{ + return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() ); +} + +vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const +{ + return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); +} + +vector wallet_api::get_call_orders(string a, uint32_t limit)const +{ + return my->_remote_db->get_call_orders(get_asset(a).id, limit); +} + +vector wallet_api::get_settle_orders(string a, uint32_t limit)const +{ + return my->_remote_db->get_settle_orders(get_asset(a).id, limit); +} + +brain_key_info wallet_api::suggest_brain_key()const +{ + brain_key_info result; + // create a private key for secure entropy + fc::sha256 sha_entropy1 = fc::ecc::private_key::generate().get_secret(); + fc::sha256 sha_entropy2 = fc::ecc::private_key::generate().get_secret(); + fc::bigint entropy1( sha_entropy1.data(), sha_entropy1.data_size() ); + fc::bigint entropy2( sha_entropy2.data(), sha_entropy2.data_size() ); + fc::bigint entropy(entropy1); + entropy <<= 8*sha_entropy1.data_size(); + entropy += entropy2; + string brain_key = ""; + + for( int i=0; i 0 ) + brain_key += " "; + brain_key += graphene::words::word_list[ choice.to_int64() ]; + } + + brain_key = normalize_brain_key(brain_key); + fc::ecc::private_key priv_key = derive_private_key( brain_key, 0 ); + result.brain_priv_key = brain_key; + result.wif_priv_key = key_to_wif( priv_key ); + result.pub_key = priv_key.get_public_key(); + return result; +} + +pair wallet_api::get_private_key_from_password( string account, string role, string password )const { + auto seed = password + account + role; + FC_ASSERT( seed.size() ); + auto secret = fc::sha256::hash( seed.c_str(), seed.size() ); + auto priv = fc::ecc::private_key::regenerate( secret ); + return std::make_pair( public_key_type( priv.get_public_key() ), key_to_wif( priv ) ); +} + +string wallet_api::serialize_transaction( signed_transaction tx )const +{ + return fc::to_hex(fc::raw::pack(tx)); +} + +variant wallet_api::get_object( object_id_type id ) const +{ + return my->_remote_db->get_objects({id}); +} + +string wallet_api::get_wallet_filename() const +{ + return my->get_wallet_filename(); +} + +transaction_handle_type wallet_api::begin_builder_transaction() +{ + return my->begin_builder_transaction(); +} + +void wallet_api::add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) +{ + my->add_operation_to_builder_transaction(transaction_handle, op); +} + +void wallet_api::replace_operation_in_builder_transaction(transaction_handle_type handle, unsigned operation_index, const operation& new_op) +{ + my->replace_operation_in_builder_transaction(handle, operation_index, new_op); +} + +asset wallet_api::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset) +{ + return my->set_fees_on_builder_transaction(handle, fee_asset); +} + +transaction wallet_api::preview_builder_transaction(transaction_handle_type handle) +{ + return my->preview_builder_transaction(handle); +} + +signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast) +{ + return my->sign_builder_transaction(transaction_handle, broadcast); +} + +signed_transaction wallet_api::propose_builder_transaction( + transaction_handle_type handle, + time_point_sec expiration, + uint32_t review_period_seconds, + bool broadcast) +{ + return my->propose_builder_transaction(handle, expiration, review_period_seconds, broadcast); +} + +signed_transaction wallet_api::propose_builder_transaction2( + transaction_handle_type handle, + string account_name_or_id, + time_point_sec expiration, + uint32_t review_period_seconds, + bool broadcast) +{ + return my->propose_builder_transaction2(handle, account_name_or_id, expiration, review_period_seconds, broadcast); +} + +void wallet_api::remove_builder_transaction(transaction_handle_type handle) +{ + return my->remove_builder_transaction(handle); +} + +account_object wallet_api::get_account(string account_name_or_id) const +{ + return my->get_account(account_name_or_id); +} + +asset_object wallet_api::get_asset(string asset_name_or_id) const +{ + auto a = my->find_asset(asset_name_or_id); + FC_ASSERT(a); + return *a; +} + +asset_bitasset_data_object wallet_api::get_bitasset_data(string asset_name_or_id) const +{ + auto asset = get_asset(asset_name_or_id); + FC_ASSERT(asset.is_market_issued() && asset.bitasset_data_id); + return my->get_object(*asset.bitasset_data_id); +} + +account_id_type wallet_api::get_account_id(string account_name_or_id) const +{ + return my->get_account_id(account_name_or_id); +} + +asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const +{ + return my->get_asset_id(asset_symbol_or_id); +} + +bool wallet_api::import_key(string account_name_or_id, string wif_key) +{ + FC_ASSERT(!is_locked()); + // backup wallet + fc::optional optional_private_key = wif_to_key(wif_key); + if (!optional_private_key) + FC_THROW("Invalid private key"); + string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key()); + copy_wallet_file( "before-import-key-" + shorthash ); + + if( my->import_key(account_name_or_id, wif_key) ) + { + save_wallet_file(); + copy_wallet_file( "after-import-key-" + shorthash ); + return true; + } + return false; +} + +map wallet_api::import_accounts( string filename, string password ) +{ + FC_ASSERT( !is_locked() ); + FC_ASSERT( fc::exists( filename ) ); + + const auto imported_keys = fc::json::from_file( filename ); + + const auto password_hash = fc::sha512::hash( password ); + FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum ); + + map result; + for( const auto& item : imported_keys.account_keys ) + { + const auto import_this_account = [ & ]() -> bool + { + try { - operation_history_id_type start; - if( result.size() ) - { - start = result.back().op.id; - start = start + 1; - } + const account_object account = get_account( item.account_name ); + const auto& owner_keys = account.owner.get_keys(); + const auto& active_keys = account.active.get_keys(); + for( const auto& public_key : item.public_keys ) + { + if( std::find( owner_keys.begin(), owner_keys.end(), public_key ) != owner_keys.end() ) + return true; - vector current = my->_remote_hist->get_account_history(account_id, operation_history_id_type(), std::min(100,limit), start); - for( auto& o : current ) { - std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); - result.push_back( operation_detail{ memo, ss.str(), o } ); - } - if( (int)current.size() < std::min(100,limit) ) - break; - limit -= current.size(); + if( std::find( active_keys.begin(), active_keys.end(), public_key ) != active_keys.end() ) + return true; + } } - - return result; - } - - - vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const - { - return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() ); - } - - vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const - { - return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); - } - - vector wallet_api::get_call_orders(string a, uint32_t limit)const - { - return my->_remote_db->get_call_orders(get_asset(a).id, limit); - } - - vector wallet_api::get_settle_orders(string a, uint32_t limit)const - { - return my->_remote_db->get_settle_orders(get_asset(a).id, limit); - } - - brain_key_info wallet_api::suggest_brain_key()const - { - brain_key_info result; - // create a private key for secure entropy - fc::sha256 sha_entropy1 = fc::ecc::private_key::generate().get_secret(); - fc::sha256 sha_entropy2 = fc::ecc::private_key::generate().get_secret(); - fc::bigint entropy1( sha_entropy1.data(), sha_entropy1.data_size() ); - fc::bigint entropy2( sha_entropy2.data(), sha_entropy2.data_size() ); - fc::bigint entropy(entropy1); - entropy <<= 8*sha_entropy1.data_size(); - entropy += entropy2; - string brain_key = ""; - - for( int i=0; i 0 ) - brain_key += " "; - brain_key += graphene::words::word_list[ choice.to_int64() ]; } - brain_key = normalize_brain_key(brain_key); - fc::ecc::private_key priv_key = derive_private_key( brain_key, 0 ); - result.brain_priv_key = brain_key; - result.wif_priv_key = key_to_wif( priv_key ); - result.pub_key = priv_key.get_public_key(); - return result; - } - - pair wallet_api::get_private_key_from_password( string account, string role, string password )const { - auto seed = password + account + role; - FC_ASSERT( seed.size() ); - auto secret = fc::sha256::hash( seed.c_str(), seed.size() ); - auto priv = fc::ecc::private_key::regenerate( secret ); - return std::make_pair( public_key_type( priv.get_public_key() ), key_to_wif( priv ) ); - } - - string wallet_api::serialize_transaction( signed_transaction tx )const - { - return fc::to_hex(fc::raw::pack(tx)); - } - - variant wallet_api::get_object( object_id_type id ) const - { - return my->_remote_db->get_objects({id}); - } - - string wallet_api::get_wallet_filename() const - { - return my->get_wallet_filename(); - } - - transaction_handle_type wallet_api::begin_builder_transaction() - { - return my->begin_builder_transaction(); - } - - void wallet_api::add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op) - { - my->add_operation_to_builder_transaction(transaction_handle, op); - } - - void wallet_api::replace_operation_in_builder_transaction(transaction_handle_type handle, unsigned operation_index, const operation& new_op) - { - my->replace_operation_in_builder_transaction(handle, operation_index, new_op); - } - - asset wallet_api::set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset) - { - return my->set_fees_on_builder_transaction(handle, fee_asset); - } - - transaction wallet_api::preview_builder_transaction(transaction_handle_type handle) - { - return my->preview_builder_transaction(handle); - } - - signed_transaction wallet_api::sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast) - { - return my->sign_builder_transaction(transaction_handle, broadcast); - } - - signed_transaction wallet_api::propose_builder_transaction( - transaction_handle_type handle, - time_point_sec expiration, - uint32_t review_period_seconds, - bool broadcast) - { - return my->propose_builder_transaction(handle, expiration, review_period_seconds, broadcast); - } - - signed_transaction wallet_api::propose_builder_transaction2( - transaction_handle_type handle, - string account_name_or_id, - time_point_sec expiration, - uint32_t review_period_seconds, - bool broadcast) - { - return my->propose_builder_transaction2(handle, account_name_or_id, expiration, review_period_seconds, broadcast); - } - - void wallet_api::remove_builder_transaction(transaction_handle_type handle) - { - return my->remove_builder_transaction(handle); - } - - account_object wallet_api::get_account(string account_name_or_id) const - { - return my->get_account(account_name_or_id); - } - - asset_object wallet_api::get_asset(string asset_name_or_id) const - { - auto a = my->find_asset(asset_name_or_id); - FC_ASSERT(a); - return *a; - } - - asset_bitasset_data_object wallet_api::get_bitasset_data(string asset_name_or_id) const - { - auto asset = get_asset(asset_name_or_id); - FC_ASSERT(asset.is_market_issued() && asset.bitasset_data_id); - return my->get_object(*asset.bitasset_data_id); - } - - account_id_type wallet_api::get_account_id(string account_name_or_id) const - { - return my->get_account_id(account_name_or_id); - } - - asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const - { - return my->get_asset_id(asset_symbol_or_id); - } - - bool wallet_api::import_key(string account_name_or_id, string wif_key) - { - FC_ASSERT(!is_locked()); - // backup wallet - fc::optional optional_private_key = wif_to_key(wif_key); - if (!optional_private_key) - FC_THROW("Invalid private key"); - string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key()); - copy_wallet_file( "before-import-key-" + shorthash ); - - if( my->import_key(account_name_or_id, wif_key) ) - { - save_wallet_file(); - copy_wallet_file( "after-import-key-" + shorthash ); - return true; - } - return false; - } - - map wallet_api::import_accounts( string filename, string password ) - { - FC_ASSERT( !is_locked() ); - FC_ASSERT( fc::exists( filename ) ); - - const auto imported_keys = fc::json::from_file( filename ); - - const auto password_hash = fc::sha512::hash( password ); - FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum ); - - map result; - for( const auto& item : imported_keys.account_keys ) - { - const auto import_this_account = [ & ]() -> bool - { - try - { - const account_object account = get_account( item.account_name ); - const auto& owner_keys = account.owner.get_keys(); - const auto& active_keys = account.active.get_keys(); - - for( const auto& public_key : item.public_keys ) - { - if( std::find( owner_keys.begin(), owner_keys.end(), public_key ) != owner_keys.end() ) - return true; - - if( std::find( active_keys.begin(), active_keys.end(), public_key ) != active_keys.end() ) - return true; - } - } - catch( ... ) - { - } - - return false; - }; - - const auto should_proceed = import_this_account(); - result[ item.account_name ] = should_proceed; - - if( should_proceed ) - { - uint32_t import_successes = 0; - uint32_t import_failures = 0; - // TODO: First check that all private keys match public keys - for( const auto& encrypted_key : item.encrypted_private_keys ) - { - try - { - const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key ); - const auto private_key = fc::raw::unpack( plain_text ); - - import_key( item.account_name, string( graphene::utilities::key_to_wif( private_key ) ) ); - ++import_successes; - } - catch( const fc::exception& e ) - { - elog( "Couldn't import key due to exception ${e}", ("e", e.to_detail_string()) ); - ++import_failures; - } - } - ilog( "successfully imported ${n} keys for account ${name}", ("n", import_successes)("name", item.account_name) ); - if( import_failures > 0 ) - elog( "failed to import ${n} keys for account ${name}", ("n", import_failures)("name", item.account_name) ); - } - } - - return result; - } - - bool wallet_api::import_account_keys( string filename, string password, string src_account_name, string dest_account_name ) - { - FC_ASSERT( !is_locked() ); - FC_ASSERT( fc::exists( filename ) ); - - bool is_my_account = false; - const auto accounts = list_my_accounts(); - for( const auto& account : accounts ) - { - if( account.name == dest_account_name ) - { - is_my_account = true; - break; - } - } - FC_ASSERT( is_my_account ); - - const auto imported_keys = fc::json::from_file( filename ); - - const auto password_hash = fc::sha512::hash( password ); - FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum ); - - bool found_account = false; - for( const auto& item : imported_keys.account_keys ) - { - if( item.account_name != src_account_name ) - continue; - - found_account = true; - - for( const auto& encrypted_key : item.encrypted_private_keys ) - { - const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key ); - const auto private_key = fc::raw::unpack( plain_text ); - - my->import_key( dest_account_name, string( graphene::utilities::key_to_wif( private_key ) ) ); - } - - return true; - } - save_wallet_file(); - - FC_ASSERT( found_account ); - return false; - } + }; - string wallet_api::normalize_brain_key(string s) const - { - return detail::normalize_brain_key( s ); - } + const auto should_proceed = import_this_account(); + result[ item.account_name ] = should_proceed; - variant wallet_api::info() - { - return my->info(); - } + if( should_proceed ) + { + uint32_t import_successes = 0; + uint32_t import_failures = 0; + // TODO: First check that all private keys match public keys + for( const auto& encrypted_key : item.encrypted_private_keys ) + { + try + { + const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key ); + const auto private_key = fc::raw::unpack( plain_text ); - variant_object wallet_api::about() const - { - return my->about(); - } + import_key( item.account_name, string( graphene::utilities::key_to_wif( private_key ) ) ); + ++import_successes; + } + catch( const fc::exception& e ) + { + elog( "Couldn't import key due to exception ${e}", ("e", e.to_detail_string()) ); + ++import_failures; + } + } + ilog( "successfully imported ${n} keys for account ${name}", ("n", import_successes)("name", item.account_name) ); + if( import_failures > 0 ) + elog( "failed to import ${n} keys for account ${name}", ("n", import_failures)("name", item.account_name) ); + } + } - fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_string, int sequence_number) const - { - return detail::derive_private_key( prefix_string, sequence_number ); - } + return result; +} - signed_transaction wallet_api::register_account(string name, - public_key_type owner_pubkey, - public_key_type active_pubkey, - string registrar_account, - string referrer_account, - uint32_t referrer_percent, - bool broadcast) - { - return my->register_account( name, owner_pubkey, active_pubkey, registrar_account, referrer_account, referrer_percent, broadcast ); - } - signed_transaction wallet_api::create_account_with_brain_key(string brain_key, string account_name, - string registrar_account, string referrer_account, - bool broadcast /* = false */) - { - return my->create_account_with_brain_key( - brain_key, account_name, registrar_account, - referrer_account, broadcast - ); - } - signed_transaction wallet_api::issue_asset(string to_account, string amount, string symbol, - string memo, bool broadcast) - { - return my->issue_asset(to_account, amount, symbol, memo, broadcast); - } +bool wallet_api::import_account_keys( string filename, string password, string src_account_name, string dest_account_name ) +{ + FC_ASSERT( !is_locked() ); + FC_ASSERT( fc::exists( filename ) ); - signed_transaction wallet_api::transfer(string from, string to, string amount, - string asset_symbol, string memo, bool broadcast /* = false */) - { - return my->transfer(from, to, amount, asset_symbol, memo, broadcast); - } - signed_transaction wallet_api::create_asset(string issuer, - string symbol, - uint8_t precision, - asset_options common, - fc::optional bitasset_opts, - bool broadcast) + bool is_my_account = false; + const auto accounts = list_my_accounts(); + for( const auto& account : accounts ) + { + if( account.name == dest_account_name ) + { + is_my_account = true; + break; + } + } + FC_ASSERT( is_my_account ); - { - return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast); - } + const auto imported_keys = fc::json::from_file( filename ); - signed_transaction wallet_api::update_asset(string symbol, - optional new_issuer, - asset_options new_options, - bool broadcast /* = false */) - { - return my->update_asset(symbol, new_issuer, new_options, broadcast); - } + const auto password_hash = fc::sha512::hash( password ); + FC_ASSERT( fc::sha512::hash( password_hash ) == imported_keys.password_checksum ); - signed_transaction wallet_api::update_bitasset(string symbol, - bitasset_options new_options, - bool broadcast /* = false */) - { - return my->update_bitasset(symbol, new_options, broadcast); - } + bool found_account = false; + for( const auto& item : imported_keys.account_keys ) + { + if( item.account_name != src_account_name ) + continue; - signed_transaction wallet_api::update_dividend_asset(string symbol, - dividend_asset_options new_options, + found_account = true; + + for( const auto& encrypted_key : item.encrypted_private_keys ) + { + const auto plain_text = fc::aes_decrypt( password_hash, encrypted_key ); + const auto private_key = fc::raw::unpack( plain_text ); + + my->import_key( dest_account_name, string( graphene::utilities::key_to_wif( private_key ) ) ); + } + + return true; + } + save_wallet_file(); + + FC_ASSERT( found_account ); + + return false; +} + +string wallet_api::normalize_brain_key(string s) const +{ + return detail::normalize_brain_key( s ); +} + +variant wallet_api::info() +{ + return my->info(); +} + +variant_object wallet_api::about() const +{ + return my->about(); +} + +fc::ecc::private_key wallet_api::derive_private_key(const std::string& prefix_string, int sequence_number) const +{ + return detail::derive_private_key( prefix_string, sequence_number ); +} + +signed_transaction wallet_api::register_account(string name, + public_key_type owner_pubkey, + public_key_type active_pubkey, + string registrar_account, + string referrer_account, + uint32_t referrer_percent, + bool broadcast) +{ + return my->register_account( name, owner_pubkey, active_pubkey, registrar_account, referrer_account, referrer_percent, broadcast ); +} +signed_transaction wallet_api::create_account_with_brain_key(string brain_key, string account_name, + string registrar_account, string referrer_account, bool broadcast /* = false */) - { - return my->update_dividend_asset(symbol, new_options, broadcast); - } +{ + return my->create_account_with_brain_key( + brain_key, account_name, registrar_account, + referrer_account, broadcast + ); +} +signed_transaction wallet_api::issue_asset(string to_account, string amount, string symbol, + string memo, bool broadcast) +{ + return my->issue_asset(to_account, amount, symbol, memo, broadcast); +} +signed_transaction wallet_api::transfer(string from, string to, string amount, + string asset_symbol, string memo, bool broadcast /* = false */) +{ + return my->transfer(from, to, amount, asset_symbol, memo, broadcast); +} +signed_transaction wallet_api::create_asset(string issuer, + string symbol, + uint8_t precision, + asset_options common, + fc::optional bitasset_opts, + bool broadcast) - signed_transaction wallet_api::update_asset_feed_producers(string symbol, - flat_set new_feed_producers, - bool broadcast /* = false */) - { - return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast); - } +{ + return my->create_asset(issuer, symbol, precision, common, bitasset_opts, broadcast); +} - signed_transaction wallet_api::publish_asset_feed(string publishing_account, - string symbol, - price_feed feed, - bool broadcast /* = false */) - { - return my->publish_asset_feed(publishing_account, symbol, feed, broadcast); - } +signed_transaction wallet_api::update_asset(string symbol, + optional new_issuer, + asset_options new_options, + bool broadcast /* = false */) +{ + return my->update_asset(symbol, new_issuer, new_options, broadcast); +} - signed_transaction wallet_api::fund_asset_fee_pool(string from, - string symbol, - string amount, - bool broadcast /* = false */) - { - return my->fund_asset_fee_pool(from, symbol, amount, broadcast); - } +signed_transaction wallet_api::update_bitasset(string symbol, + bitasset_options new_options, + bool broadcast /* = false */) +{ + return my->update_bitasset(symbol, new_options, broadcast); +} - signed_transaction wallet_api::reserve_asset(string from, - string amount, - string symbol, +signed_transaction wallet_api::update_dividend_asset(string symbol, + dividend_asset_options new_options, bool broadcast /* = false */) - { - return my->reserve_asset(from, amount, symbol, broadcast); - } +{ + return my->update_dividend_asset(symbol, new_options, broadcast); +} - signed_transaction wallet_api::global_settle_asset(string symbol, - price settle_price, + +signed_transaction wallet_api::update_asset_feed_producers(string symbol, + flat_set new_feed_producers, bool broadcast /* = false */) - { - return my->global_settle_asset(symbol, settle_price, broadcast); - } +{ + return my->update_asset_feed_producers(symbol, new_feed_producers, broadcast); +} - signed_transaction wallet_api::settle_asset(string account_to_settle, - string amount_to_settle, - string symbol, +signed_transaction wallet_api::publish_asset_feed(string publishing_account, + string symbol, + price_feed feed, + bool broadcast /* = false */) +{ + return my->publish_asset_feed(publishing_account, symbol, feed, broadcast); +} + +signed_transaction wallet_api::fund_asset_fee_pool(string from, + string symbol, + string amount, + bool broadcast /* = false */) +{ + return my->fund_asset_fee_pool(from, symbol, amount, broadcast); +} + +signed_transaction wallet_api::reserve_asset(string from, + string amount, + string symbol, + bool broadcast /* = false */) +{ + return my->reserve_asset(from, amount, symbol, broadcast); +} + +signed_transaction wallet_api::global_settle_asset(string symbol, + price settle_price, + bool broadcast /* = false */) +{ + return my->global_settle_asset(symbol, settle_price, broadcast); +} + +signed_transaction wallet_api::settle_asset(string account_to_settle, + string amount_to_settle, + string symbol, + bool broadcast /* = false */) +{ + return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast); +} + +signed_transaction wallet_api::whitelist_account(string authorizing_account, + string account_to_list, + account_whitelist_operation::account_listing new_listing_status, + bool broadcast /* = false */) +{ + return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); +} + +signed_transaction wallet_api::create_committee_member(string owner_account, string url, + bool broadcast /* = false */) +{ + return my->create_committee_member(owner_account, url, broadcast); +} + +map wallet_api::list_witnesses(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_witness_accounts(lowerbound, limit); +} + +map wallet_api::list_committee_members(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit); +} + +witness_object wallet_api::get_witness(string owner_account) +{ + return my->get_witness(owner_account); +} + +committee_member_object wallet_api::get_committee_member(string owner_account) +{ + return my->get_committee_member(owner_account); +} + +signed_transaction wallet_api::create_witness(string owner_account, + string url, + bool broadcast /* = false */) +{ + return my->create_witness(owner_account, url, broadcast); +} + +signed_transaction wallet_api::create_worker( + string owner_account, + time_point_sec work_begin_date, + time_point_sec work_end_date, + share_type daily_pay, + string name, + string url, + variant worker_settings, + bool broadcast /* = false */) +{ + return my->create_worker( owner_account, work_begin_date, work_end_date, + daily_pay, name, url, worker_settings, broadcast ); +} + +signed_transaction wallet_api::update_worker_votes( + string owner_account, + worker_vote_delta delta, + bool broadcast /* = false */) +{ + return my->update_worker_votes( owner_account, delta, broadcast ); +} + +signed_transaction wallet_api::update_witness( + string witness_name, + string url, + string block_signing_key, + bool broadcast /* = false */) +{ + return my->update_witness(witness_name, url, block_signing_key, broadcast); +} + +vector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( string account_name ) +{ + return my->get_vesting_balances( account_name ); +} + +signed_transaction wallet_api::withdraw_vesting( + string witness_name, + string amount, + string asset_symbol, + bool broadcast /* = false */) +{ + return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast ); +} + +signed_transaction wallet_api::vote_for_committee_member(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_committee_member(voting_account, witness, approve, broadcast); +} + +signed_transaction wallet_api::vote_for_witness(string voting_account, + string witness, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_witness(voting_account, witness, approve, broadcast); +} + +signed_transaction wallet_api::update_witness_votes(string voting_account, + std::vector witnesses_to_approve, + std::vector witnesses_to_reject, + uint16_t desired_number_of_witnesses, bool broadcast /* = false */) - { - return my->settle_asset(account_to_settle, amount_to_settle, symbol, broadcast); - } - - signed_transaction wallet_api::whitelist_account(string authorizing_account, - string account_to_list, - account_whitelist_operation::account_listing new_listing_status, - bool broadcast /* = false */) - { - return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); - } - - signed_transaction wallet_api::create_committee_member(string owner_account, string url, - bool broadcast /* = false */) - { - return my->create_committee_member(owner_account, url, broadcast); - } - - map wallet_api::list_witnesses(const string& lowerbound, uint32_t limit) - { - return my->_remote_db->lookup_witness_accounts(lowerbound, limit); - } - - map wallet_api::list_committee_members(const string& lowerbound, uint32_t limit) - { - return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit); - } - - witness_object wallet_api::get_witness(string owner_account) - { - return my->get_witness(owner_account); - } - - committee_member_object wallet_api::get_committee_member(string owner_account) - { - return my->get_committee_member(owner_account); - } - - signed_transaction wallet_api::create_witness(string owner_account, - string url, - bool broadcast /* = false */) - { - return my->create_witness(owner_account, url, broadcast); - } - - signed_transaction wallet_api::create_worker( - string owner_account, - time_point_sec work_begin_date, - time_point_sec work_end_date, - share_type daily_pay, - string name, - string url, - variant worker_settings, - bool broadcast /* = false */) - { - return my->create_worker( owner_account, work_begin_date, work_end_date, - daily_pay, name, url, worker_settings, broadcast ); - } - - signed_transaction wallet_api::update_worker_votes( - string owner_account, - worker_vote_delta delta, - bool broadcast /* = false */) - { - return my->update_worker_votes( owner_account, delta, broadcast ); - } - - signed_transaction wallet_api::update_witness( - string witness_name, - string url, - string block_signing_key, - bool broadcast /* = false */) - { - return my->update_witness(witness_name, url, block_signing_key, broadcast); - } - - vector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( string account_name ) - { - return my->get_vesting_balances( account_name ); - } - - signed_transaction wallet_api::withdraw_vesting( - string witness_name, - string amount, - string asset_symbol, - bool broadcast /* = false */) - { - return my->withdraw_vesting( witness_name, amount, asset_symbol, broadcast ); - } - - signed_transaction wallet_api::vote_for_committee_member(string voting_account, - string witness, - bool approve, - bool broadcast /* = false */) - { - return my->vote_for_committee_member(voting_account, witness, approve, broadcast); - } - - signed_transaction wallet_api::vote_for_witness(string voting_account, - string witness, - bool approve, - bool broadcast /* = false */) - { - return my->vote_for_witness(voting_account, witness, approve, broadcast); - } - - signed_transaction wallet_api::update_witness_votes(string voting_account, - std::vector witnesses_to_approve, - std::vector witnesses_to_reject, - uint16_t desired_number_of_witnesses, - bool broadcast /* = false */) - { - return my->update_witness_votes(voting_account, witnesses_to_approve, witnesses_to_reject, desired_number_of_witnesses, broadcast); - } - - signed_transaction wallet_api::set_voting_proxy(string account_to_modify, - optional voting_account, - bool broadcast /* = false */) - { - return my->set_voting_proxy(account_to_modify, voting_account, broadcast); - } - - signed_transaction wallet_api::set_desired_witness_and_committee_member_count(string account_to_modify, - uint16_t desired_number_of_witnesses, - uint16_t desired_number_of_committee_members, - bool broadcast /* = false */) - { - return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses, - desired_number_of_committee_members, broadcast); - } - - void wallet_api::set_wallet_filename(string wallet_filename) - { - my->_wallet_filename = wallet_filename; - } - - signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */) - { try { - return my->sign_transaction( tx, broadcast); - } FC_CAPTURE_AND_RETHROW( (tx) ) } - - operation wallet_api::get_prototype_operation(string operation_name) - { - return my->get_prototype_operation( operation_name ); - } - - void wallet_api::dbg_make_uia(string creator, string symbol) - { - FC_ASSERT(!is_locked()); - my->dbg_make_uia(creator, symbol); - } - - void wallet_api::dbg_make_mia(string creator, string symbol) - { - FC_ASSERT(!is_locked()); - my->dbg_make_mia(creator, symbol); - } - - void wallet_api::dbg_push_blocks( std::string src_filename, uint32_t count ) - { - my->dbg_push_blocks( src_filename, count ); - } - - void wallet_api::dbg_generate_blocks( std::string debug_wif_key, uint32_t count ) - { - my->dbg_generate_blocks( debug_wif_key, count ); - } - - void wallet_api::dbg_stream_json_objects( const std::string& filename ) - { - my->dbg_stream_json_objects( filename ); - } - - void wallet_api::dbg_update_object( fc::variant_object update ) - { - my->dbg_update_object( update ); - } - - void wallet_api::network_add_nodes( const vector& nodes ) - { - my->network_add_nodes( nodes ); - } - - vector< variant > wallet_api::network_get_connected_peers() - { - return my->network_get_connected_peers(); - } - - void wallet_api::flood_network(string prefix, uint32_t number_of_transactions) - { - FC_ASSERT(!is_locked()); - my->flood_network(prefix, number_of_transactions); - } - - signed_transaction wallet_api::propose_parameter_change( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_values, - bool broadcast /* = false */ - ) - { - return my->propose_parameter_change( proposing_account, expiration_time, changed_values, broadcast ); - } - - signed_transaction wallet_api::propose_fee_change( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_fees, - bool broadcast /* = false */ - ) - { - return my->propose_fee_change( proposing_account, expiration_time, changed_fees, broadcast ); - } - - signed_transaction wallet_api::propose_dividend_asset_update( - const string& proposing_account, - fc::time_point_sec expiration_time, - const variant_object& changed_fees, - bool broadcast /* = false */ - ) - { - return my->propose_dividend_asset_update( proposing_account, expiration_time, changed_fees, broadcast ); - } - - signed_transaction wallet_api::approve_proposal( - const string& fee_paying_account, - const string& proposal_id, - const approval_delta& delta, - bool broadcast /* = false */ - ) - { - return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast ); - } - - - - - global_property_object wallet_api::get_global_properties() const - { - return my->get_global_properties(); - } - - dynamic_global_property_object wallet_api::get_dynamic_global_properties() const - { - return my->get_dynamic_global_properties(); - } - - string wallet_api::help()const - { - std::vector method_names = my->method_documentation.get_method_names(); - std::stringstream ss; - for (const std::string method_name : method_names) - { - try - { - ss << my->method_documentation.get_brief_description(method_name); - } - catch (const fc::key_not_found_exception&) - { - ss << method_name << " (no help available)\n"; - } - } - return ss.str(); - } - - string wallet_api::gethelp(const string& method)const - { - fc::api tmp; - std::stringstream ss; - ss << "\n"; - - if( method == "import_key" ) - { - ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; - ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; - ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; - } - else if( method == "transfer" ) - { - ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n"; - ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n"; - ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n"; - } - else if( method == "create_account_with_brain_key" ) - { - ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n"; - ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n"; - ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n"; - ss << "\n"; - ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n"; - ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n"; - ss << "register_account if you already know the keys you know the public keys that you would like to register.\n"; - - } - else if( method == "register_account" ) - { - ss << "usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR REFERRER REFERRER_PERCENT BROADCAST\n\n"; - ss << "example: register_account \"newaccount\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"1.3.11\" \"1.3.11\" 50 true\n"; - ss << "\n"; - ss << "Use this method to register an account for which you do not know the private keys."; - } - else if( method == "create_asset" ) - { - ss << "usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\n\n"; - ss << "PRECISION_DIGITS: the number of digits after the decimal point\n\n"; - ss << "Example value of OPTIONS: \n"; - ss << fc::json::to_pretty_string( graphene::chain::asset_options() ); - ss << "\nExample value of BITASSET_OPTIONS: \n"; - ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() ); - ss << "\nBITASSET_OPTIONS may be null\n"; - } - else - { - std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); - if (!doxygenHelpString.empty()) - ss << doxygenHelpString; - else - ss << "No help defined for method " << method << "\n"; - } - - return ss.str(); - } - - bool wallet_api::load_wallet_file( string wallet_filename ) - { - return my->load_wallet_file( wallet_filename ); - } - - void wallet_api::save_wallet_file( string wallet_filename ) - { - my->save_wallet_file( wallet_filename ); - } - - std::map > - wallet_api::get_result_formatters() const - { - return my->get_result_formatters(); - } - - bool wallet_api::is_locked()const - { - return my->is_locked(); - } - bool wallet_api::is_new()const - { - return my->_wallet.cipher_keys.size() == 0; - } - - void wallet_api::encrypt_keys() - { - my->encrypt_keys(); - } - - void wallet_api::lock() - { try { - FC_ASSERT( !is_locked() ); - encrypt_keys(); - for( auto key : my->_keys ) - key.second = key_to_wif(fc::ecc::private_key()); - my->_keys.clear(); - my->_checksum = fc::sha512(); - my->self.lock_changed(true); - } FC_CAPTURE_AND_RETHROW() } - - void wallet_api::unlock(string password) - { try { - FC_ASSERT(password.size() > 0); - auto pw = fc::sha512::hash(password.c_str(), password.size()); - vector decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys); - auto pk = fc::raw::unpack(decrypted); - FC_ASSERT(pk.checksum == pw); - my->_keys = std::move(pk.keys); - my->_checksum = pk.checksum; - my->self.lock_changed(false); - my->resync_active_tournaments(); - } FC_CAPTURE_AND_RETHROW() } - - void wallet_api::set_password( string password ) - { - if( !is_new() ) - FC_ASSERT( !is_locked(), "The wallet must be unlocked before the password can be set" ); - my->_checksum = fc::sha512::hash( password.c_str(), password.size() ); - lock(); - } - - vector< signed_transaction > wallet_api::import_balance( string name_or_id, const vector& wif_keys, bool broadcast ) - { - return my->import_balance( name_or_id, wif_keys, broadcast ); - } - - namespace detail { - - vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id, const vector& wif_keys, bool broadcast ) - { try { - FC_ASSERT(!is_locked()); - const dynamic_global_property_object& dpo = _remote_db->get_dynamic_global_properties(); - account_object claimer = get_account( name_or_id ); - uint32_t max_ops_per_tx = 30; - - map< address, private_key_type > keys; // local index of address -> private key - vector< address > addrs; - bool has_wildcard = false; - addrs.reserve( wif_keys.size() ); - for( const string& wif_key : wif_keys ) - { - if( wif_key == "*" ) - { - if( has_wildcard ) - continue; - for( const public_key_type& pub : _wallet.extra_keys[ claimer.id ] ) - { - addrs.push_back( pub ); - auto it = _keys.find( pub ); - if( it != _keys.end() ) - { - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - FC_ASSERT( privkey ); - keys[ addrs.back() ] = *privkey; - } - else - { - wlog( "Somehow _keys has no private key for extra_keys public key ${k}", ("k", pub) ); - } - } - has_wildcard = true; - } - else - { - optional< private_key_type > key = wif_to_key( wif_key ); - FC_ASSERT( key.valid(), "Invalid private key" ); - fc::ecc::public_key pk = key->get_public_key(); - addrs.push_back( pk ); - keys[addrs.back()] = *key; - // see chain/balance_evaluator.cpp - addrs.push_back( pts_address( pk, false, 56 ) ); - keys[addrs.back()] = *key; - addrs.push_back( pts_address( pk, true, 56 ) ); - keys[addrs.back()] = *key; - addrs.push_back( pts_address( pk, false, 0 ) ); - keys[addrs.back()] = *key; - addrs.push_back( pts_address( pk, true, 0 ) ); - keys[addrs.back()] = *key; - } - } - - vector< balance_object > balances = _remote_db->get_balance_objects( addrs ); - wdump((balances)); - addrs.clear(); - - set bal_types; - for( auto b : balances ) bal_types.insert( b.balance.asset_id ); - - struct claim_tx - { - vector< balance_claim_operation > ops; - set< address > addrs; - }; - vector< claim_tx > claim_txs; - - for( const asset_id_type& a : bal_types ) - { - balance_claim_operation op; - op.deposit_to_account = claimer.id; - for( const balance_object& b : balances ) - { - if( b.balance.asset_id == a ) - { - op.total_claimed = b.available( dpo.time ); - if( op.total_claimed.amount == 0 ) - continue; - op.balance_to_claim = b.id; - op.balance_owner_key = keys[b.owner].get_public_key(); - if( (claim_txs.empty()) || (claim_txs.back().ops.size() >= max_ops_per_tx) ) - claim_txs.emplace_back(); - claim_txs.back().ops.push_back(op); - claim_txs.back().addrs.insert(b.owner); - } - } - } - - vector< signed_transaction > result; - - for( const claim_tx& ctx : claim_txs ) - { - signed_transaction tx; - tx.operations.reserve( ctx.ops.size() ); - for( const balance_claim_operation& op : ctx.ops ) - tx.operations.emplace_back( op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - signed_transaction signed_tx = sign_transaction( tx, false ); - for( const address& addr : ctx.addrs ) - signed_tx.sign( keys[addr], _chain_id ); - // if the key for a balance object was the same as a key for the account we're importing it into, - // we may end up with duplicate signatures, so remove those - boost::erase(signed_tx.signatures, boost::unique(boost::sort(signed_tx.signatures))); - result.push_back( signed_tx ); - if( broadcast ) - _remote_net_broadcast->broadcast_transaction(signed_tx); - } - - return result; - } FC_CAPTURE_AND_RETHROW( (name_or_id) ) } - - } - - map wallet_api::dump_private_keys() - { - FC_ASSERT(!is_locked()); - return my->_keys; - } - - signed_transaction wallet_api::upgrade_account( string name, bool broadcast ) - { - return my->upgrade_account(name,broadcast); - } - - signed_transaction wallet_api::sell_asset(string seller_account, - string amount_to_sell, - string symbol_to_sell, - string min_to_receive, - string symbol_to_receive, - uint32_t expiration, - bool fill_or_kill, - bool broadcast) - { - return my->sell_asset(seller_account, amount_to_sell, symbol_to_sell, min_to_receive, - symbol_to_receive, expiration, fill_or_kill, broadcast); - } - - signed_transaction wallet_api::sell( string seller_account, - string base, - string quote, - double rate, - double amount, - bool broadcast ) - { - return my->sell_asset( seller_account, std::to_string( amount ), base, - std::to_string( rate * amount ), quote, 0, false, broadcast ); - } - - signed_transaction wallet_api::buy( string buyer_account, - string base, - string quote, - double rate, - double amount, - bool broadcast ) - { - return my->sell_asset( buyer_account, std::to_string( rate * amount ), quote, - std::to_string( amount ), base, 0, false, broadcast ); - } - - signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, - string asset_symbol, string amount_of_collateral, bool broadcast) - { - FC_ASSERT(!is_locked()); - return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); - } - - signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast) - { - FC_ASSERT(!is_locked()); - return my->cancel_order(order_id, broadcast); - } - - string wallet_api::get_key_label( public_key_type key )const - { - auto key_itr = my->_wallet.labeled_keys.get().find(key); - if( key_itr != my->_wallet.labeled_keys.get().end() ) - return key_itr->label; - return string(); - } - - string wallet_api::get_private_key( public_key_type pubkey )const - { - return key_to_wif( my->get_private_key( pubkey ) ); - } - - public_key_type wallet_api::get_public_key( string label )const - { - try { return fc::variant(label).as(); } catch ( ... ){} - - auto key_itr = my->_wallet.labeled_keys.get().find(label); - if( key_itr != my->_wallet.labeled_keys.get().end() ) - return key_itr->key; - return public_key_type(); - } - - bool wallet_api::set_key_label( public_key_type key, string label ) - { - auto result = my->_wallet.labeled_keys.insert( key_label{label,key} ); - if( result.second ) return true; - - auto key_itr = my->_wallet.labeled_keys.get().find(key); - auto label_itr = my->_wallet.labeled_keys.get().find(label); - if( label_itr == my->_wallet.labeled_keys.get().end() ) - { - if( key_itr != my->_wallet.labeled_keys.get().end() ) - return my->_wallet.labeled_keys.get().modify( key_itr, [&]( key_label& obj ){ obj.label = label; } ); - } - return false; - } - map wallet_api::get_blind_accounts()const - { - map result; - for( const auto& item : my->_wallet.labeled_keys ) - result[item.label] = item.key; - return result; - } - map wallet_api::get_my_blind_accounts()const - { - FC_ASSERT( !is_locked() ); - map result; - for( const auto& item : my->_wallet.labeled_keys ) - { - if( my->_keys.find(item.key) != my->_keys.end() ) - result[item.label] = item.key; - } - return result; - } - - public_key_type wallet_api::create_blind_account( string label, string brain_key ) - { - FC_ASSERT( !is_locked() ); - - auto label_itr = my->_wallet.labeled_keys.get().find(label); - if( label_itr != my->_wallet.labeled_keys.get().end() ) - FC_ASSERT( !"Key with label already exists" ); - brain_key = fc::trim_and_normalize_spaces( brain_key ); - auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() ); - auto priv_key = fc::ecc::private_key::regenerate( secret ); - public_key_type pub_key = priv_key.get_public_key(); - - FC_ASSERT( set_key_label( pub_key, label ) ); - - my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key ); - - save_wallet_file(); - return pub_key; - } - - vector wallet_api::get_blind_balances( string key_or_label ) - { - vector result; - map balances; - - vector used; - - auto pub_key = get_public_key( key_or_label ); - auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); - auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) ); - auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) ); - while( start != end ) - { - if( !start->used ) - { - auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} ); - if( answer.size() ) - balances[start->amount.asset_id] += start->amount.amount; - else - used.push_back( start->commitment() ); - } - ++start; - } - for( const auto& u : used ) - { - auto itr = my->_wallet.blind_receipts.get().find( u ); - my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); - } - for( auto item : balances ) - result.push_back( asset( item.second, item.first ) ); - return result; - } - - blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label, - string to_account_id_or_name, - string amount_in, - string symbol, - bool broadcast ) - { try { - transfer_from_blind_operation from_blind; - - - auto fees = my->_remote_db->get_global_properties().parameters.current_fees; - fc::optional asset_obj = get_asset(symbol); - FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); - auto amount = asset_obj->amount_from_string(amount_in); - - from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); - - auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount ); - - - auto conf = blind_transfer_help( from_blind_account_key_or_label, - from_blind_account_key_or_label, - blind_in, symbol, false, true/*to_temp*/ ); - FC_ASSERT( conf.outputs.size() > 0 ); - - auto to_account = my->get_account( to_account_id_or_name ); - from_blind.to = to_account.id; - from_blind.amount = amount; - from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor; - from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } ); - from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); - - idump( (from_blind) ); - conf.trx.operations.push_back(from_blind); - ilog( "about to validate" ); - conf.trx.validate(); - - if( broadcast && conf.outputs.size() == 2 ) { - - // Save the change - blind_confirmation::output conf_output; - blind_confirmation::output change_output = conf.outputs[0]; - - // The wallet must have a private key for confirmation.to, this is used to decrypt the memo - public_key_type from_key = get_public_key(from_blind_account_key_or_label); - conf_output.confirmation.to = from_key; - conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; - conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; - conf_output.confirmation_receipt = conf_output.confirmation; - //try { - receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); - //} catch ( ... ){} - } - - ilog( "about to broadcast" ); - conf.trx = sign_transaction( conf.trx, broadcast ); - - return conf; - } FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) } - - blind_confirmation wallet_api::blind_transfer( string from_key_or_label, - string to_key_or_label, - string amount_in, - string symbol, - bool broadcast ) - { - return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false ); - } - blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, - string to_key_or_label, - string amount_in, - string symbol, - bool broadcast, - bool to_temp ) - { - blind_confirmation confirm; - try { - - FC_ASSERT( !is_locked() ); - public_key_type from_key = get_public_key(from_key_or_label); - public_key_type to_key = get_public_key(to_key_or_label); - - fc::optional asset_obj = get_asset(symbol); - FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); - - blind_transfer_operation blind_tr; - blind_tr.outputs.resize(2); - - auto fees = my->_remote_db->get_global_properties().parameters.current_fees; - - auto amount = asset_obj->amount_from_string(amount_in); - - asset total_amount = asset_obj->amount(0); - - vector blinding_factors; - - //auto from_priv_key = my->get_private_key( from_key ); - - blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate ); - - vector used; - - auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); - auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) ); - auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) ); - while( start != end ) - { - auto result = my->_remote_db->get_blinded_balances( {start->commitment() } ); - if( result.size() == 0 ) - { - used.push_back( start->commitment() ); - } - else - { - blind_tr.inputs.push_back({start->commitment(), start->control_authority}); - blinding_factors.push_back( start->data.blinding_factor ); - total_amount += start->amount; - - if( total_amount >= amount + blind_tr.fee ) - break; - } - ++start; - } - for( const auto& u : used ) - { - auto itr = my->_wallet.blind_receipts.get().find( u ); - my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); - } - - FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); - - auto one_time_key = fc::ecc::private_key::generate(); - auto secret = one_time_key.get_shared_secret( to_key ); - auto child = fc::sha256::hash( secret ); - auto nonce = fc::sha256::hash( one_time_key.get_secret() ); - auto blind_factor = fc::sha256::hash( child ); - - auto from_secret = one_time_key.get_shared_secret( from_key ); - auto from_child = fc::sha256::hash( from_secret ); - auto from_nonce = fc::sha256::hash( nonce ); - - auto change = total_amount - amount - blind_tr.fee; - fc::sha256 change_blind_factor; - fc::sha256 to_blind_factor; - if( change.amount > 0 ) - { - idump(("to_blind_factor")(blind_factor) ); - blinding_factors.push_back( blind_factor ); - change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 ); - wdump(("change_blind_factor")(change_blind_factor) ); - } - else // change == 0 - { - blind_tr.outputs.resize(1); - blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); - idump(("to_sum_blind_factor")(blind_factor) ); - blinding_factors.push_back( blind_factor ); - idump(("nochange to_blind_factor")(blind_factor) ); - } - fc::ecc::public_key from_pub_key = from_key; - fc::ecc::public_key to_pub_key = to_key; - - blind_output to_out; - to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); - to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); - idump(("to_out.blind")(blind_factor)(to_out.commitment) ); - - - if( blind_tr.outputs.size() > 1 ) - { - to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); - - blind_output change_out; - change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); - change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); - change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value ); - blind_tr.outputs[1] = change_out; - - - blind_confirmation::output conf_output; - conf_output.label = from_key_or_label; - conf_output.pub_key = from_key; - conf_output.decrypted_memo.from = from_key; - conf_output.decrypted_memo.amount = change; - conf_output.decrypted_memo.blinding_factor = change_blind_factor; - conf_output.decrypted_memo.commitment = change_out.commitment; - conf_output.decrypted_memo.check = from_secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = from_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.auth = change_out.owner; - conf_output.confirmation_receipt = conf_output.confirmation; - - confirm.outputs.push_back( conf_output ); - } - blind_tr.outputs[0] = to_out; - - blind_confirmation::output conf_output; - conf_output.label = to_key_or_label; - conf_output.pub_key = to_key; - conf_output.decrypted_memo.from = from_key; - conf_output.decrypted_memo.amount = amount; - conf_output.decrypted_memo.blinding_factor = blind_factor; - conf_output.decrypted_memo.commitment = to_out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = to_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.auth = to_out.owner; - conf_output.confirmation_receipt = conf_output.confirmation; - - confirm.outputs.push_back( conf_output ); - - /** commitments must be in sorted order */ - std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), - [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); - std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), - [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } ); - - confirm.trx.operations.emplace_back( std::move(blind_tr) ); - ilog( "validate before" ); - confirm.trx.validate(); - confirm.trx = sign_transaction(confirm.trx, broadcast); - - if( broadcast ) - { - for( const auto& out : confirm.outputs ) - { - try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} - } - } - - return confirm; - } FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) } +{ + return my->update_witness_votes(voting_account, witnesses_to_approve, witnesses_to_reject, desired_number_of_witnesses, broadcast); +} + +signed_transaction wallet_api::set_voting_proxy(string account_to_modify, + optional voting_account, + bool broadcast /* = false */) +{ + return my->set_voting_proxy(account_to_modify, voting_account, broadcast); +} + +signed_transaction wallet_api::set_desired_witness_and_committee_member_count(string account_to_modify, + uint16_t desired_number_of_witnesses, + uint16_t desired_number_of_committee_members, + bool broadcast /* = false */) +{ + return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses, + desired_number_of_committee_members, broadcast); +} + +void wallet_api::set_wallet_filename(string wallet_filename) +{ + my->_wallet_filename = wallet_filename; +} + +signed_transaction wallet_api::sign_transaction(signed_transaction tx, bool broadcast /* = false */) +{ try { + return my->sign_transaction( tx, broadcast); +} FC_CAPTURE_AND_RETHROW( (tx) ) } + +operation wallet_api::get_prototype_operation(string operation_name) +{ + return my->get_prototype_operation( operation_name ); +} + +void wallet_api::dbg_make_uia(string creator, string symbol) +{ + FC_ASSERT(!is_locked()); + my->dbg_make_uia(creator, symbol); +} + +void wallet_api::dbg_make_mia(string creator, string symbol) +{ + FC_ASSERT(!is_locked()); + my->dbg_make_mia(creator, symbol); +} + +void wallet_api::dbg_push_blocks( std::string src_filename, uint32_t count ) +{ + my->dbg_push_blocks( src_filename, count ); +} + +void wallet_api::dbg_generate_blocks( std::string debug_wif_key, uint32_t count ) +{ + my->dbg_generate_blocks( debug_wif_key, count ); +} + +void wallet_api::dbg_stream_json_objects( const std::string& filename ) +{ + my->dbg_stream_json_objects( filename ); +} + +void wallet_api::dbg_update_object( fc::variant_object update ) +{ + my->dbg_update_object( update ); +} + +void wallet_api::network_add_nodes( const vector& nodes ) +{ + my->network_add_nodes( nodes ); +} + +vector< variant > wallet_api::network_get_connected_peers() +{ + return my->network_get_connected_peers(); +} + +void wallet_api::flood_network(string prefix, uint32_t number_of_transactions) +{ + FC_ASSERT(!is_locked()); + my->flood_network(prefix, number_of_transactions); +} + +signed_transaction wallet_api::propose_parameter_change( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_values, + bool broadcast /* = false */ + ) +{ + return my->propose_parameter_change( proposing_account, expiration_time, changed_values, broadcast ); +} + +signed_transaction wallet_api::propose_fee_change( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_fees, + bool broadcast /* = false */ + ) +{ + return my->propose_fee_change( proposing_account, expiration_time, changed_fees, broadcast ); +} + +signed_transaction wallet_api::propose_dividend_asset_update( + const string& proposing_account, + fc::time_point_sec expiration_time, + const variant_object& changed_fees, + bool broadcast /* = false */ + ) +{ + return my->propose_dividend_asset_update( proposing_account, expiration_time, changed_fees, broadcast ); +} + +signed_transaction wallet_api::approve_proposal( + const string& fee_paying_account, + const string& proposal_id, + const approval_delta& delta, + bool broadcast /* = false */ + ) +{ + return my->approve_proposal( fee_paying_account, proposal_id, delta, broadcast ); +} + + + + +global_property_object wallet_api::get_global_properties() const +{ + return my->get_global_properties(); +} + +dynamic_global_property_object wallet_api::get_dynamic_global_properties() const +{ + return my->get_dynamic_global_properties(); +} + +string wallet_api::help()const +{ + std::vector method_names = my->method_documentation.get_method_names(); + std::stringstream ss; + for (const std::string method_name : method_names) + { + try + { + ss << my->method_documentation.get_brief_description(method_name); + } + catch (const fc::key_not_found_exception&) + { + ss << method_name << " (no help available)\n"; + } + } + return ss.str(); +} + +string wallet_api::gethelp(const string& method)const +{ + fc::api tmp; + std::stringstream ss; + ss << "\n"; + + if( method == "import_key" ) + { + ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; + ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; + ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; + } + else if( method == "transfer" ) + { + ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n"; + ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n"; + ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n"; + } + else if( method == "create_account_with_brain_key" ) + { + ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n"; + ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n"; + ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n"; + ss << "\n"; + ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n"; + ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n"; + ss << "register_account if you already know the keys you know the public keys that you would like to register.\n"; + + } + else if( method == "register_account" ) + { + ss << "usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR REFERRER REFERRER_PERCENT BROADCAST\n\n"; + ss << "example: register_account \"newaccount\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"1.3.11\" \"1.3.11\" 50 true\n"; + ss << "\n"; + ss << "Use this method to register an account for which you do not know the private keys."; + } + else if( method == "create_asset" ) + { + ss << "usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\n\n"; + ss << "PRECISION_DIGITS: the number of digits after the decimal point\n\n"; + ss << "Example value of OPTIONS: \n"; + ss << fc::json::to_pretty_string( graphene::chain::asset_options() ); + ss << "\nExample value of BITASSET_OPTIONS: \n"; + ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() ); + ss << "\nBITASSET_OPTIONS may be null\n"; + } + else + { + std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); + if (!doxygenHelpString.empty()) + ss << doxygenHelpString; + else + ss << "No help defined for method " << method << "\n"; + } + + return ss.str(); +} + +bool wallet_api::load_wallet_file( string wallet_filename ) +{ + return my->load_wallet_file( wallet_filename ); +} + +void wallet_api::save_wallet_file( string wallet_filename ) +{ + my->save_wallet_file( wallet_filename ); +} + +std::map > +wallet_api::get_result_formatters() const +{ + return my->get_result_formatters(); +} + +bool wallet_api::is_locked()const +{ + return my->is_locked(); +} +bool wallet_api::is_new()const +{ + return my->_wallet.cipher_keys.size() == 0; +} + +void wallet_api::encrypt_keys() +{ + my->encrypt_keys(); +} + +void wallet_api::lock() +{ try { + FC_ASSERT( !is_locked() ); + encrypt_keys(); + for( auto key : my->_keys ) + key.second = key_to_wif(fc::ecc::private_key()); + my->_keys.clear(); + my->_checksum = fc::sha512(); + my->self.lock_changed(true); +} FC_CAPTURE_AND_RETHROW() } + +void wallet_api::unlock(string password) +{ try { + FC_ASSERT(password.size() > 0); + auto pw = fc::sha512::hash(password.c_str(), password.size()); + vector decrypted = fc::aes_decrypt(pw, my->_wallet.cipher_keys); + auto pk = fc::raw::unpack(decrypted); + FC_ASSERT(pk.checksum == pw); + my->_keys = std::move(pk.keys); + my->_checksum = pk.checksum; + my->self.lock_changed(false); + my->resync_active_tournaments(); +} FC_CAPTURE_AND_RETHROW() } + +void wallet_api::set_password( string password ) +{ + if( !is_new() ) + FC_ASSERT( !is_locked(), "The wallet must be unlocked before the password can be set" ); + my->_checksum = fc::sha512::hash( password.c_str(), password.size() ); + lock(); +} + +vector< signed_transaction > wallet_api::import_balance( string name_or_id, const vector& wif_keys, bool broadcast ) +{ + return my->import_balance( name_or_id, wif_keys, broadcast ); +} + +namespace detail { + +vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id, const vector& wif_keys, bool broadcast ) +{ try { + FC_ASSERT(!is_locked()); + const dynamic_global_property_object& dpo = _remote_db->get_dynamic_global_properties(); + account_object claimer = get_account( name_or_id ); + uint32_t max_ops_per_tx = 30; + + map< address, private_key_type > keys; // local index of address -> private key + vector< address > addrs; + bool has_wildcard = false; + addrs.reserve( wif_keys.size() ); + for( const string& wif_key : wif_keys ) + { + if( wif_key == "*" ) + { + if( has_wildcard ) + continue; + for( const public_key_type& pub : _wallet.extra_keys[ claimer.id ] ) + { + addrs.push_back( pub ); + auto it = _keys.find( pub ); + if( it != _keys.end() ) + { + fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); + FC_ASSERT( privkey ); + keys[ addrs.back() ] = *privkey; + } + else + { + wlog( "Somehow _keys has no private key for extra_keys public key ${k}", ("k", pub) ); + } + } + has_wildcard = true; + } + else + { + optional< private_key_type > key = wif_to_key( wif_key ); + FC_ASSERT( key.valid(), "Invalid private key" ); + fc::ecc::public_key pk = key->get_public_key(); + addrs.push_back( pk ); + keys[addrs.back()] = *key; + // see chain/balance_evaluator.cpp + addrs.push_back( pts_address( pk, false, 56 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, true, 56 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, false, 0 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, true, 0 ) ); + keys[addrs.back()] = *key; + } + } + + vector< balance_object > balances = _remote_db->get_balance_objects( addrs ); + wdump((balances)); + addrs.clear(); + + set bal_types; + for( auto b : balances ) bal_types.insert( b.balance.asset_id ); + + struct claim_tx + { + vector< balance_claim_operation > ops; + set< address > addrs; + }; + vector< claim_tx > claim_txs; + + for( const asset_id_type& a : bal_types ) + { + balance_claim_operation op; + op.deposit_to_account = claimer.id; + for( const balance_object& b : balances ) + { + if( b.balance.asset_id == a ) + { + op.total_claimed = b.available( dpo.time ); + if( op.total_claimed.amount == 0 ) + continue; + op.balance_to_claim = b.id; + op.balance_owner_key = keys[b.owner].get_public_key(); + if( (claim_txs.empty()) || (claim_txs.back().ops.size() >= max_ops_per_tx) ) + claim_txs.emplace_back(); + claim_txs.back().ops.push_back(op); + claim_txs.back().addrs.insert(b.owner); + } + } + } + + vector< signed_transaction > result; + + for( const claim_tx& ctx : claim_txs ) + { + signed_transaction tx; + tx.operations.reserve( ctx.ops.size() ); + for( const balance_claim_operation& op : ctx.ops ) + tx.operations.emplace_back( op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + signed_transaction signed_tx = sign_transaction( tx, false ); + for( const address& addr : ctx.addrs ) + signed_tx.sign( keys[addr], _chain_id ); + // if the key for a balance object was the same as a key for the account we're importing it into, + // we may end up with duplicate signatures, so remove those + boost::erase(signed_tx.signatures, boost::unique(boost::sort(signed_tx.signatures))); + result.push_back( signed_tx ); + if( broadcast ) + _remote_net_broadcast->broadcast_transaction(signed_tx); + } + + return result; +} FC_CAPTURE_AND_RETHROW( (name_or_id) ) } + +} + +map wallet_api::dump_private_keys() +{ + FC_ASSERT(!is_locked()); + return my->_keys; +} + +signed_transaction wallet_api::upgrade_account( string name, bool broadcast ) +{ + return my->upgrade_account(name,broadcast); +} + +signed_transaction wallet_api::sell_asset(string seller_account, + string amount_to_sell, + string symbol_to_sell, + string min_to_receive, + string symbol_to_receive, + uint32_t expiration, + bool fill_or_kill, + bool broadcast) +{ + return my->sell_asset(seller_account, amount_to_sell, symbol_to_sell, min_to_receive, + symbol_to_receive, expiration, fill_or_kill, broadcast); +} + +signed_transaction wallet_api::sell( string seller_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ) +{ + return my->sell_asset( seller_account, std::to_string( amount ), base, + std::to_string( rate * amount ), quote, 0, false, broadcast ); +} + +signed_transaction wallet_api::buy( string buyer_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ) +{ + return my->sell_asset( buyer_account, std::to_string( rate * amount ), quote, + std::to_string( amount ), base, 0, false, broadcast ); +} + +signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, + string asset_symbol, string amount_of_collateral, bool broadcast) +{ + FC_ASSERT(!is_locked()); + return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); +} + +signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast) +{ + FC_ASSERT(!is_locked()); + return my->cancel_order(order_id, broadcast); +} + +string wallet_api::get_key_label( public_key_type key )const +{ + auto key_itr = my->_wallet.labeled_keys.get().find(key); + if( key_itr != my->_wallet.labeled_keys.get().end() ) + return key_itr->label; + return string(); +} + +string wallet_api::get_private_key( public_key_type pubkey )const +{ + return key_to_wif( my->get_private_key( pubkey ) ); +} + +public_key_type wallet_api::get_public_key( string label )const +{ + try { return fc::variant(label).as(); } catch ( ... ){} + + auto key_itr = my->_wallet.labeled_keys.get().find(label); + if( key_itr != my->_wallet.labeled_keys.get().end() ) + return key_itr->key; + return public_key_type(); +} + +bool wallet_api::set_key_label( public_key_type key, string label ) +{ + auto result = my->_wallet.labeled_keys.insert( key_label{label,key} ); + if( result.second ) return true; + + auto key_itr = my->_wallet.labeled_keys.get().find(key); + auto label_itr = my->_wallet.labeled_keys.get().find(label); + if( label_itr == my->_wallet.labeled_keys.get().end() ) + { + if( key_itr != my->_wallet.labeled_keys.get().end() ) + return my->_wallet.labeled_keys.get().modify( key_itr, [&]( key_label& obj ){ obj.label = label; } ); + } + return false; +} +map wallet_api::get_blind_accounts()const +{ + map result; + for( const auto& item : my->_wallet.labeled_keys ) + result[item.label] = item.key; + return result; +} +map wallet_api::get_my_blind_accounts()const +{ + FC_ASSERT( !is_locked() ); + map result; + for( const auto& item : my->_wallet.labeled_keys ) + { + if( my->_keys.find(item.key) != my->_keys.end() ) + result[item.label] = item.key; + } + return result; +} + +public_key_type wallet_api::create_blind_account( string label, string brain_key ) +{ + FC_ASSERT( !is_locked() ); + + auto label_itr = my->_wallet.labeled_keys.get().find(label); + if( label_itr != my->_wallet.labeled_keys.get().end() ) + FC_ASSERT( !"Key with label already exists" ); + brain_key = fc::trim_and_normalize_spaces( brain_key ); + auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() ); + auto priv_key = fc::ecc::private_key::regenerate( secret ); + public_key_type pub_key = priv_key.get_public_key(); + + FC_ASSERT( set_key_label( pub_key, label ) ); + + my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key ); + + save_wallet_file(); + return pub_key; +} + +vector wallet_api::get_blind_balances( string key_or_label ) +{ + vector result; + map balances; + + vector used; + + auto pub_key = get_public_key( key_or_label ); + auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); + auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) ); + auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) ); + while( start != end ) + { + if( !start->used ) + { + auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} ); + if( answer.size() ) + balances[start->amount.asset_id] += start->amount.amount; + else + used.push_back( start->commitment() ); + } + ++start; + } + for( const auto& u : used ) + { + auto itr = my->_wallet.blind_receipts.get().find( u ); + my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); + } + for( auto item : balances ) + result.push_back( asset( item.second, item.first ) ); + return result; +} + +blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label, + string to_account_id_or_name, + string amount_in, + string symbol, + bool broadcast ) +{ try { + transfer_from_blind_operation from_blind; + + + auto fees = my->_remote_db->get_global_properties().parameters.current_fees; + fc::optional asset_obj = get_asset(symbol); + FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); + auto amount = asset_obj->amount_from_string(amount_in); + + from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); + + auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount ); + + + auto conf = blind_transfer_help( from_blind_account_key_or_label, + from_blind_account_key_or_label, + blind_in, symbol, false, true/*to_temp*/ ); + FC_ASSERT( conf.outputs.size() > 0 ); + + auto to_account = my->get_account( to_account_id_or_name ); + from_blind.to = to_account.id; + from_blind.amount = amount; + from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor; + from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } ); + from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); + + idump( (from_blind) ); + conf.trx.operations.push_back(from_blind); + ilog( "about to validate" ); + conf.trx.validate(); + + if( broadcast && conf.outputs.size() == 2 ) { + + // Save the change + blind_confirmation::output conf_output; + blind_confirmation::output change_output = conf.outputs[0]; + + // The wallet must have a private key for confirmation.to, this is used to decrypt the memo + public_key_type from_key = get_public_key(from_blind_account_key_or_label); + conf_output.confirmation.to = from_key; + conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; + conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; + conf_output.confirmation_receipt = conf_output.confirmation; + //try { + receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); + //} catch ( ... ){} + } + + ilog( "about to broadcast" ); + conf.trx = sign_transaction( conf.trx, broadcast ); + + return conf; +} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) } + +blind_confirmation wallet_api::blind_transfer( string from_key_or_label, + string to_key_or_label, + string amount_in, + string symbol, + bool broadcast ) +{ + return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false ); +} +blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, + string to_key_or_label, + string amount_in, + string symbol, + bool broadcast, + bool to_temp ) +{ + blind_confirmation confirm; + try { + + FC_ASSERT( !is_locked() ); + public_key_type from_key = get_public_key(from_key_or_label); + public_key_type to_key = get_public_key(to_key_or_label); + + fc::optional asset_obj = get_asset(symbol); + FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); + + blind_transfer_operation blind_tr; + blind_tr.outputs.resize(2); + + auto fees = my->_remote_db->get_global_properties().parameters.current_fees; + + auto amount = asset_obj->amount_from_string(amount_in); + + asset total_amount = asset_obj->amount(0); + + vector blinding_factors; + + //auto from_priv_key = my->get_private_key( from_key ); + + blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate ); + + vector used; + + auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); + auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) ); + auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) ); + while( start != end ) + { + auto result = my->_remote_db->get_blinded_balances( {start->commitment() } ); + if( result.size() == 0 ) + { + used.push_back( start->commitment() ); + } + else + { + blind_tr.inputs.push_back({start->commitment(), start->control_authority}); + blinding_factors.push_back( start->data.blinding_factor ); + total_amount += start->amount; + + if( total_amount >= amount + blind_tr.fee ) + break; + } + ++start; + } + for( const auto& u : used ) + { + auto itr = my->_wallet.blind_receipts.get().find( u ); + my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); + } + + FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); + + auto one_time_key = fc::ecc::private_key::generate(); + auto secret = one_time_key.get_shared_secret( to_key ); + auto child = fc::sha256::hash( secret ); + auto nonce = fc::sha256::hash( one_time_key.get_secret() ); + auto blind_factor = fc::sha256::hash( child ); + + auto from_secret = one_time_key.get_shared_secret( from_key ); + auto from_child = fc::sha256::hash( from_secret ); + auto from_nonce = fc::sha256::hash( nonce ); + + auto change = total_amount - amount - blind_tr.fee; + fc::sha256 change_blind_factor; + fc::sha256 to_blind_factor; + if( change.amount > 0 ) + { + idump(("to_blind_factor")(blind_factor) ); + blinding_factors.push_back( blind_factor ); + change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 ); + wdump(("change_blind_factor")(change_blind_factor) ); + } + else // change == 0 + { + blind_tr.outputs.resize(1); + blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); + idump(("to_sum_blind_factor")(blind_factor) ); + blinding_factors.push_back( blind_factor ); + idump(("nochange to_blind_factor")(blind_factor) ); + } + fc::ecc::public_key from_pub_key = from_key; + fc::ecc::public_key to_pub_key = to_key; + + blind_output to_out; + to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); + to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); + idump(("to_out.blind")(blind_factor)(to_out.commitment) ); + + + if( blind_tr.outputs.size() > 1 ) + { + to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + + blind_output change_out; + change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); + change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); + change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value ); + blind_tr.outputs[1] = change_out; + + + blind_confirmation::output conf_output; + conf_output.label = from_key_or_label; + conf_output.pub_key = from_key; + conf_output.decrypted_memo.from = from_key; + conf_output.decrypted_memo.amount = change; + conf_output.decrypted_memo.blinding_factor = change_blind_factor; + conf_output.decrypted_memo.commitment = change_out.commitment; + conf_output.decrypted_memo.check = from_secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = from_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.auth = change_out.owner; + conf_output.confirmation_receipt = conf_output.confirmation; + + confirm.outputs.push_back( conf_output ); + } + blind_tr.outputs[0] = to_out; + + blind_confirmation::output conf_output; + conf_output.label = to_key_or_label; + conf_output.pub_key = to_key; + conf_output.decrypted_memo.from = from_key; + conf_output.decrypted_memo.amount = amount; + conf_output.decrypted_memo.blinding_factor = blind_factor; + conf_output.decrypted_memo.commitment = to_out.commitment; + conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = to_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.auth = to_out.owner; + conf_output.confirmation_receipt = conf_output.confirmation; + + confirm.outputs.push_back( conf_output ); + + /** commitments must be in sorted order */ + std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), + [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); + std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), + [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } ); + + confirm.trx.operations.emplace_back( std::move(blind_tr) ); + ilog( "validate before" ); + confirm.trx.validate(); + confirm.trx = sign_transaction(confirm.trx, broadcast); + + if( broadcast ) + { + for( const auto& out : confirm.outputs ) + { + try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} + } + } + + return confirm; +} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) } @@ -4719,356 +4719,356 @@ namespace graphene { namespace wallet { * Transfers a public balance from @from to one or more blinded balances using a * stealth transfer. */ - blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, - string asset_symbol, - /** map from key or label to amount */ - vector> to_amounts, - bool broadcast ) - { try { - FC_ASSERT( !is_locked() ); - idump((to_amounts)); +blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, + string asset_symbol, + /** map from key or label to amount */ + vector> to_amounts, + bool broadcast ) +{ try { + FC_ASSERT( !is_locked() ); + idump((to_amounts)); - blind_confirmation confirm; - account_object from_account = my->get_account(from_account_id_or_name); + blind_confirmation confirm; + account_object from_account = my->get_account(from_account_id_or_name); - fc::optional asset_obj = get_asset(asset_symbol); - FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); - transfer_to_blind_operation bop; - bop.from = from_account.id; + transfer_to_blind_operation bop; + bop.from = from_account.id; - vector blinding_factors; + vector blinding_factors; - asset total_amount = asset_obj->amount(0); + asset total_amount = asset_obj->amount(0); - for( auto item : to_amounts ) - { - auto one_time_key = fc::ecc::private_key::generate(); - auto to_key = get_public_key( item.first ); - auto secret = one_time_key.get_shared_secret( to_key ); - auto child = fc::sha256::hash( secret ); - auto nonce = fc::sha256::hash( one_time_key.get_secret() ); - auto blind_factor = fc::sha256::hash( child ); + for( auto item : to_amounts ) + { + auto one_time_key = fc::ecc::private_key::generate(); + auto to_key = get_public_key( item.first ); + auto secret = one_time_key.get_shared_secret( to_key ); + auto child = fc::sha256::hash( secret ); + auto nonce = fc::sha256::hash( one_time_key.get_secret() ); + auto blind_factor = fc::sha256::hash( child ); - blinding_factors.push_back( blind_factor ); + blinding_factors.push_back( blind_factor ); - auto amount = asset_obj->amount_from_string(item.second); - total_amount += amount; + auto amount = asset_obj->amount_from_string(item.second); + total_amount += amount; - fc::ecc::public_key to_pub_key = to_key; - blind_output out; - out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); - out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); - if( to_amounts.size() > 1 ) - out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + fc::ecc::public_key to_pub_key = to_key; + blind_output out; + out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); + out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); + if( to_amounts.size() > 1 ) + out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); - blind_confirmation::output conf_output; - conf_output.label = item.first; - conf_output.pub_key = to_key; - conf_output.decrypted_memo.amount = amount; - conf_output.decrypted_memo.blinding_factor = blind_factor; - conf_output.decrypted_memo.commitment = out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = to_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.confirmation_receipt = conf_output.confirmation; + blind_confirmation::output conf_output; + conf_output.label = item.first; + conf_output.pub_key = to_key; + conf_output.decrypted_memo.amount = amount; + conf_output.decrypted_memo.blinding_factor = blind_factor; + conf_output.decrypted_memo.commitment = out.commitment; + conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.confirmation.one_time_key = one_time_key.get_public_key(); + conf_output.confirmation.to = to_key; + conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); + conf_output.confirmation_receipt = conf_output.confirmation; - confirm.outputs.push_back( conf_output ); + confirm.outputs.push_back( conf_output ); - bop.outputs.push_back(out); - } - bop.amount = total_amount; - bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); + bop.outputs.push_back(out); + } + bop.amount = total_amount; + bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); - /** commitments must be in sorted order */ - std::sort( bop.outputs.begin(), bop.outputs.end(), - [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); + /** commitments must be in sorted order */ + std::sort( bop.outputs.begin(), bop.outputs.end(), + [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); - confirm.trx.operations.push_back( bop ); - my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees); - confirm.trx.validate(); - confirm.trx = sign_transaction(confirm.trx, broadcast); + confirm.trx.operations.push_back( bop ); + my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees); + confirm.trx.validate(); + confirm.trx = sign_transaction(confirm.trx, broadcast); - if( broadcast ) - { - for( const auto& out : confirm.outputs ) - { - try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} - } - } + if( broadcast ) + { + for( const auto& out : confirm.outputs ) + { + try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} + } + } - return confirm; - } FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } + return confirm; +} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } - blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) - { - FC_ASSERT( !is_locked() ); - stealth_confirmation conf(confirmation_receipt); - FC_ASSERT( conf.to ); +blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) +{ + FC_ASSERT( !is_locked() ); + stealth_confirmation conf(confirmation_receipt); + FC_ASSERT( conf.to ); - blind_receipt result; - result.conf = conf; + blind_receipt result; + result.conf = conf; - auto to_priv_key_itr = my->_keys.find( *conf.to ); - FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) ); + auto to_priv_key_itr = my->_keys.find( *conf.to ); + FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) ); - auto to_priv_key = wif_to_key( to_priv_key_itr->second ); - FC_ASSERT( to_priv_key ); + auto to_priv_key = wif_to_key( to_priv_key_itr->second ); + FC_ASSERT( to_priv_key ); - auto secret = to_priv_key->get_shared_secret( conf.one_time_key ); - auto child = fc::sha256::hash( secret ); + auto secret = to_priv_key->get_shared_secret( conf.one_time_key ); + auto child = fc::sha256::hash( secret ); - auto child_priv_key = to_priv_key->child( child ); - //auto blind_factor = fc::sha256::hash( child ); + auto child_priv_key = to_priv_key->child( child ); + //auto blind_factor = fc::sha256::hash( child ); - auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo ); - auto memo = fc::raw::unpack( plain_memo ); + auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo ); + auto memo = fc::raw::unpack( plain_memo ); - result.to_key = *conf.to; - result.to_label = get_key_label( result.to_key ); - if( memo.from ) - { - result.from_key = *memo.from; - result.from_label = get_key_label( result.from_key ); - if( result.from_label == string() ) - { - result.from_label = opt_from; - set_key_label( result.from_key, result.from_label ); - } - } - else - { - result.from_label = opt_from; - } - result.amount = memo.amount; - result.memo = opt_memo; + result.to_key = *conf.to; + result.to_label = get_key_label( result.to_key ); + if( memo.from ) + { + result.from_key = *memo.from; + result.from_label = get_key_label( result.from_key ); + if( result.from_label == string() ) + { + result.from_label = opt_from; + set_key_label( result.from_key, result.from_label ); + } + } + else + { + result.from_label = opt_from; + } + result.amount = memo.amount; + result.memo = opt_memo; - // confirm the amount matches the commitment (verify the blinding factor) - auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); - FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); + // confirm the amount matches the commitment (verify the blinding factor) + auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); + FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); - blind_balance bal; - bal.amount = memo.amount; - bal.to = *conf.to; - if( memo.from ) bal.from = *memo.from; - bal.one_time_key = conf.one_time_key; - bal.blinding_factor = memo.blinding_factor; - bal.commitment = memo.commitment; - bal.used = false; + blind_balance bal; + bal.amount = memo.amount; + bal.to = *conf.to; + if( memo.from ) bal.from = *memo.from; + bal.one_time_key = conf.one_time_key; + bal.blinding_factor = memo.blinding_factor; + bal.commitment = memo.commitment; + bal.used = false; - auto child_pubkey = child_priv_key.get_public_key(); - auto owner = authority(1, public_key_type(child_pubkey), 1); - result.control_authority = owner; - result.data = memo; + auto child_pubkey = child_priv_key.get_public_key(); + auto owner = authority(1, public_key_type(child_pubkey), 1); + result.control_authority = owner; + result.data = memo; - auto child_key_itr = owner.key_auths.find( child_pubkey ); - if( child_key_itr != owner.key_auths.end() ) - my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); + auto child_key_itr = owner.key_auths.find( child_pubkey ); + if( child_key_itr != owner.key_auths.end() ) + my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); - // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); + // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); - result.date = fc::time_point::now(); - my->_wallet.blind_receipts.insert( result ); - my->_keys[child_pubkey] = key_to_wif( child_priv_key ); + result.date = fc::time_point::now(); + my->_wallet.blind_receipts.insert( result ); + my->_keys[child_pubkey] = key_to_wif( child_priv_key ); - save_wallet_file(); + save_wallet_file(); - return result; - } + return result; +} - vector wallet_api::blind_history( string key_or_account ) - { - vector result; - auto pub_key = get_public_key( key_or_account ); +vector wallet_api::blind_history( string key_or_account ) +{ + vector result; + auto pub_key = get_public_key( key_or_account ); - if( pub_key == public_key_type() ) - return vector(); + if( pub_key == public_key_type() ) + return vector(); - for( auto& r : my->_wallet.blind_receipts ) - { - if( r.from_key == pub_key || r.to_key == pub_key ) - result.push_back( r ); - } - std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } ); - return result; - } + for( auto& r : my->_wallet.blind_receipts ) + { + if( r.from_key == pub_key || r.to_key == pub_key ) + result.push_back( r ); + } + std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } ); + return result; +} - signed_transaction wallet_api::tournament_create( string creator, tournament_options options, bool broadcast ) - { - FC_ASSERT( !is_locked() ); - account_object creator_account_obj = get_account(creator); +signed_transaction wallet_api::tournament_create( string creator, tournament_options options, bool broadcast ) +{ + FC_ASSERT( !is_locked() ); + account_object creator_account_obj = get_account(creator); - signed_transaction tx; - tournament_create_operation op; - op.creator = creator_account_obj.get_id(); - op.options = options; - tx.operations = {op}; - my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); + signed_transaction tx; + tournament_create_operation op; + op.creator = creator_account_obj.get_id(); + op.options = options; + tx.operations = {op}; + my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); - return my->sign_transaction( tx, broadcast ); - } + return my->sign_transaction( tx, broadcast ); +} - signed_transaction wallet_api::tournament_join( string payer_account, - string player_account, - tournament_id_type tournament_id, - string buy_in_amount, - string buy_in_asset_symbol, - bool broadcast ) - { - FC_ASSERT( !is_locked() ); - account_object payer_account_obj = get_account(payer_account); - account_object player_account_obj = get_account(player_account); - //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); +signed_transaction wallet_api::tournament_join( string payer_account, + string player_account, + tournament_id_type tournament_id, + string buy_in_amount, + string buy_in_asset_symbol, + bool broadcast ) +{ + FC_ASSERT( !is_locked() ); + account_object payer_account_obj = get_account(payer_account); + account_object player_account_obj = get_account(player_account); + //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); - fc::optional buy_in_asset_obj = get_asset(buy_in_asset_symbol); - FC_ASSERT(buy_in_asset_obj, "Could not find asset matching ${asset}", ("asset", buy_in_asset_symbol)); + fc::optional buy_in_asset_obj = get_asset(buy_in_asset_symbol); + FC_ASSERT(buy_in_asset_obj, "Could not find asset matching ${asset}", ("asset", buy_in_asset_symbol)); - signed_transaction tx; - tournament_join_operation op; - op.payer_account_id = payer_account_obj.get_id(); - op.player_account_id = player_account_obj.get_id(); - op.tournament_id = tournament_id; - op.buy_in = buy_in_asset_obj->amount_from_string(buy_in_amount); + signed_transaction tx; + tournament_join_operation op; + op.payer_account_id = payer_account_obj.get_id(); + op.player_account_id = player_account_obj.get_id(); + op.tournament_id = tournament_id; + op.buy_in = buy_in_asset_obj->amount_from_string(buy_in_amount); - tx.operations = {op}; - my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); + tx.operations = {op}; + my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); - return my->sign_transaction( tx, broadcast ); - } + return my->sign_transaction( tx, broadcast ); +} - signed_transaction wallet_api::tournament_leave( string canceling_account, - string player_account, - tournament_id_type tournament_id, - bool broadcast) - { - FC_ASSERT( !is_locked() ); - account_object player_account_obj = get_account(player_account); - account_object canceling_account_obj = get_account(canceling_account); - //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); - - signed_transaction tx; - tournament_leave_operation op; - op.canceling_account_id = canceling_account_obj.get_id(); - op.player_account_id = player_account_obj.get_id(); - op.tournament_id = tournament_id; - - tx.operations = {op}; - my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); - - return my->sign_transaction( tx, broadcast ); - } - - vector wallet_api::get_upcoming_tournaments(uint32_t limit) - { - return my->_remote_db->get_tournaments_in_state(tournament_state::accepting_registrations, limit); - } - vector wallet_api::get_tournaments(tournament_id_type stop, - unsigned limit, - tournament_id_type start) { - return my->_remote_db->get_tournaments(stop, limit, start); - } - - vector wallet_api::get_tournaments_by_state(tournament_id_type stop, - unsigned limit, - tournament_id_type start, - tournament_state state) { - return my->_remote_db->get_tournaments_by_state(stop, limit, start, state); - } - - tournament_object wallet_api::get_tournament(tournament_id_type id) - { - return my->_remote_db->get_objects({id})[0].as(); - } - - signed_transaction wallet_api::rps_throw(game_id_type game_id, +signed_transaction wallet_api::tournament_leave( string canceling_account, string player_account, - rock_paper_scissors_gesture gesture, + tournament_id_type tournament_id, bool broadcast) - { - FC_ASSERT( !is_locked() ); +{ + FC_ASSERT( !is_locked() ); + account_object player_account_obj = get_account(player_account); + account_object canceling_account_obj = get_account(canceling_account); + //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); - // check whether the gesture is appropriate for the game we're playing - graphene::chain::game_object game_obj = my->get_object(game_id); - graphene::chain::match_object match_obj = my->get_object(game_obj.match_id); - graphene::chain::tournament_object tournament_obj = my->get_object(match_obj.tournament_id); - graphene::chain::rock_paper_scissors_game_options game_options = - tournament_obj.options.game_options.get(); - if ((int)gesture >= game_options.number_of_gestures) - FC_THROW("Gesture ${gesture} not supported in this game", ("gesture", gesture)); + signed_transaction tx; + tournament_leave_operation op; + op.canceling_account_id = canceling_account_obj.get_id(); + op.player_account_id = player_account_obj.get_id(); + op.tournament_id = tournament_id; - account_object player_account_obj = get_account(player_account); + tx.operations = {op}; + my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); - // construct the complete throw, the commit, and reveal - rock_paper_scissors_throw full_throw; - fc::rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1)); - fc::rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2)); - full_throw.gesture = gesture; + return my->sign_transaction( tx, broadcast ); +} - rock_paper_scissors_throw_commit commit_throw; - commit_throw.nonce1 = full_throw.nonce1; - std::vector full_throw_packed(fc::raw::pack(full_throw)); - commit_throw.throw_hash = fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size()); +vector wallet_api::get_upcoming_tournaments(uint32_t limit) +{ + return my->_remote_db->get_tournaments_in_state(tournament_state::accepting_registrations, limit); +} +vector wallet_api::get_tournaments(tournament_id_type stop, + unsigned limit, + tournament_id_type start) { + return my->_remote_db->get_tournaments(stop, limit, start); +} - rock_paper_scissors_throw_reveal reveal_throw; - reveal_throw.nonce2 = full_throw.nonce2; - reveal_throw.gesture = full_throw.gesture; +vector wallet_api::get_tournaments_by_state(tournament_id_type stop, + unsigned limit, + tournament_id_type start, + tournament_state state) { + return my->_remote_db->get_tournaments_by_state(stop, limit, start, state); +} - // store off the reveal for transmitting after both players commit - my->_wallet.committed_game_moves[commit_throw] = reveal_throw; +tournament_object wallet_api::get_tournament(tournament_id_type id) +{ + return my->_remote_db->get_objects({id})[0].as(); +} - // broadcast the commit - signed_transaction tx; - game_move_operation move_operation; - move_operation.game_id = game_id; - move_operation.player_account_id = player_account_obj.id; - move_operation.move = commit_throw; - tx.operations = {move_operation}; - my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); - tx.validate(); +signed_transaction wallet_api::rps_throw(game_id_type game_id, + string player_account, + rock_paper_scissors_gesture gesture, + bool broadcast) +{ + FC_ASSERT( !is_locked() ); - return my->sign_transaction( tx, broadcast ); - } + // check whether the gesture is appropriate for the game we're playing + graphene::chain::game_object game_obj = my->get_object(game_id); + graphene::chain::match_object match_obj = my->get_object(game_obj.match_id); + graphene::chain::tournament_object tournament_obj = my->get_object(match_obj.tournament_id); + graphene::chain::rock_paper_scissors_game_options game_options = + tournament_obj.options.game_options.get(); + if ((int)gesture >= game_options.number_of_gestures) + FC_THROW("Gesture ${gesture} not supported in this game", ("gesture", gesture)); + + account_object player_account_obj = get_account(player_account); + + // construct the complete throw, the commit, and reveal + rock_paper_scissors_throw full_throw; + fc::rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1)); + fc::rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2)); + full_throw.gesture = gesture; + + rock_paper_scissors_throw_commit commit_throw; + commit_throw.nonce1 = full_throw.nonce1; + std::vector full_throw_packed(fc::raw::pack(full_throw)); + commit_throw.throw_hash = fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size()); + + rock_paper_scissors_throw_reveal reveal_throw; + reveal_throw.nonce2 = full_throw.nonce2; + reveal_throw.gesture = full_throw.gesture; + + // store off the reveal for transmitting after both players commit + my->_wallet.committed_game_moves[commit_throw] = reveal_throw; + + // broadcast the commit + signed_transaction tx; + game_move_operation move_operation; + move_operation.game_id = game_id; + move_operation.player_account_id = player_account_obj.id; + move_operation.move = commit_throw; + tx.operations = {move_operation}; + my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return my->sign_transaction( tx, broadcast ); +} // default ctor necessary for FC_REFLECT - signed_block_with_info::signed_block_with_info() - { - } +signed_block_with_info::signed_block_with_info() +{ +} - order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit ) - { - return( my->_remote_db->get_order_book( base, quote, limit ) ); - } +order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit ) +{ + return( my->_remote_db->get_order_book( base, quote, limit ) ); +} - signed_block_with_info::signed_block_with_info( const signed_block& block ) - : signed_block( block ) - { - block_id = id(); - signing_key = signee(); - transaction_ids.reserve( transactions.size() ); - for( const processed_transaction& tx : transactions ) - transaction_ids.push_back( tx.id() ); - } +signed_block_with_info::signed_block_with_info( const signed_block& block ) + : signed_block( block ) +{ + block_id = id(); + signing_key = signee(); + transaction_ids.reserve( transactions.size() ); + for( const processed_transaction& tx : transactions ) + transaction_ids.push_back( tx.id() ); +} - vesting_balance_object_with_info::vesting_balance_object_with_info() - : vesting_balance_object() - { - } +vesting_balance_object_with_info::vesting_balance_object_with_info() + : vesting_balance_object() +{ +} - vesting_balance_object_with_info::vesting_balance_object_with_info( const vesting_balance_object& vbo, fc::time_point_sec now ) - : vesting_balance_object( vbo ) - { - allowed_withdraw = get_allowed_withdraw( now ); - allowed_withdraw_time = now; - } +vesting_balance_object_with_info::vesting_balance_object_with_info( const vesting_balance_object& vbo, fc::time_point_sec now ) + : vesting_balance_object( vbo ) +{ + allowed_withdraw = get_allowed_withdraw( now ); + allowed_withdraw_time = now; +} - } } // graphene::wallet +} } // graphene::wallet void fc::to_variant(const account_multi_index_type& accts, fc::variant& vo) { @@ -5079,4 +5079,4 @@ void fc::from_variant(const fc::variant& var, account_multi_index_type& vo) { const vector& v = var.as>(); vo = account_multi_index_type(v.begin(), v.end()); -} \ No newline at end of file +} diff --git a/programs/genesis_util/genesis_update.cpp b/programs/genesis_util/genesis_update.cpp index 0dec0165..52329301 100644 --- a/programs/genesis_util/genesis_update.cpp +++ b/programs/genesis_util/genesis_update.cpp @@ -70,6 +70,8 @@ int main( int argc, char** argv ) ("dev-account-count", bpo::value()->default_value(0), "Prefix for dev accounts") ("dev-balance-count", bpo::value()->default_value(0), "Prefix for dev balances") ("dev-balance-amount", bpo::value()->default_value(uint64_t(1000)*uint64_t(1000)*uint64_t(100000)), "Amount in each dev balance") + ("nop", "just write the genesis file out after reading it in, do not alter any keys or add accounts or balances. used to pretty-print a genesis file") + ("replace-all-keys", bpo::value(), "Replace all keys/addresses in the genesis files with dev keys based on dev-key-prefix and dump the new keys to this filename.") ; bpo::variables_map options; @@ -116,54 +118,134 @@ int main( int argc, char** argv ) genesis = graphene::app::detail::create_example_genesis(); } - std::string dev_key_prefix = options["dev-key-prefix"].as(); - - auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type + if (!options.count("nop")) { - return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key(); - }; - - uint32_t dev_account_count = options["dev-account-count"].as(); - std::string dev_account_prefix = options["dev-account-prefix"].as(); - for(uint32_t i=0;i(); - uint64_t dev_balance_amount = options["dev-balance-amount"].as(); - for(uint32_t i=0;i name2index; - size_t num_accounts = genesis.initial_accounts.size(); - for( size_t i=0; i(); + auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type { - std::cerr << "need " << genesis.initial_active_witnesses << " init accounts as first entries in initial_active_witnesses\n"; - return 1; + return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key(); + }; + + if (options.count("replace-all-keys")) + { + unsigned dev_keys_used = 0; + std::map replacement_keys; + auto get_replacement_key = [&](const std::string& original_key) -> fc::ecc::private_key { + auto iter = replacement_keys.find(original_key); + if (iter != replacement_keys.end()) + return iter->second; + fc::ecc::private_key new_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(dev_key_prefix + std::to_string(dev_keys_used++))); + replacement_keys[original_key] = new_private_key; + return new_private_key; + }; + + for (genesis_state_type::initial_balance_type& initial_balance : genesis.initial_balances) + { + std::string address_string = (std::string)initial_balance.owner; + initial_balance.owner = address(get_replacement_key(address_string).get_public_key()); + } + + for (genesis_state_type::initial_vesting_balance_type& initial_balance : genesis.initial_vesting_balances) + { + std::string address_string = (std::string)initial_balance.owner; + initial_balance.owner = address(get_replacement_key(address_string).get_public_key()); + } + + for (genesis_state_type::initial_witness_type& initial_witness : genesis.initial_witness_candidates) + { + std::string public_key_string = (std::string)initial_witness.block_signing_key; + initial_witness.block_signing_key = get_replacement_key(public_key_string).get_public_key(); + } + + for (genesis_state_type::initial_account_type& initial_account : genesis.initial_accounts) + { + std::string public_key_string = (std::string)initial_account.owner_key; + initial_account.owner_key = get_replacement_key(public_key_string).get_public_key(); + public_key_string = (std::string)initial_account.active_key; + initial_account.active_key = get_replacement_key(public_key_string).get_public_key(); + } + + for (genesis_state_type::initial_bts_account_type& initial_account : genesis.initial_bts_accounts) + { + for (auto iter = initial_account.owner_authority.key_auths.begin(); + iter != initial_account.owner_authority.key_auths.end(); ++iter) + { + std::string public_key_string = (std::string)iter->first; + iter->first = get_replacement_key(public_key_string).get_public_key(); + } + for (auto iter = initial_account.active_authority.key_auths.begin(); + iter != initial_account.active_authority.key_auths.end(); ++iter) + { + std::string public_key_string = (std::string)iter->first; + iter->first = get_replacement_key(public_key_string).get_public_key(); + } + for (auto iter = initial_account.owner_authority.address_auths.begin(); + iter != initial_account.owner_authority.address_auths.end(); ++iter) + { + std::string address_string = (std::string)iter->first; + iter->first = address(get_replacement_key(address_string).get_public_key()); + } + for (auto iter = initial_account.active_authority.address_auths.begin(); + iter != initial_account.active_authority.address_auths.end(); ++iter) + { + std::string address_string = (std::string)iter->first; + iter->first = address(get_replacement_key(address_string).get_public_key()); + } + } + fc::path keys_csv_path = options["replace-all-keys"].as(); + std::ofstream keys_csv(keys_csv_path.string()); + keys_csv << "wif_private_key,public_key,address\n"; + for (const auto& value : replacement_keys) + keys_csv << graphene::utilities::key_to_wif(value.second) << "," << std::string(public_key_type(value.second.get_public_key())) + << "," << std::string(address(value.second.get_public_key())) << "\n"; + + + } + else + { + uint32_t dev_account_count = options["dev-account-count"].as(); + std::string dev_account_prefix = options["dev-account-prefix"].as(); + for(uint32_t i=0;i(); + uint64_t dev_balance_amount = options["dev-balance-amount"].as(); + for(uint32_t i=0;i name2index; + size_t num_accounts = genesis.initial_accounts.size(); + for( size_t i=0; i(); diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index bfbed6b4..7535d3ad 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_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${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/main.cpp b/programs/witness_node/main.cpp index 6359d5b4..2f41676c 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -49,14 +49,14 @@ #include #ifdef WIN32 -# include +# include #else # include #endif using namespace graphene; namespace bpo = boost::program_options; - + void write_default_logging_config_to_stream(std::ostream& out); fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename); @@ -67,9 +67,9 @@ int main(int argc, char** argv) { bpo::options_description app_options("Graphene Witness Node"); bpo::options_description cfg_options("Graphene Witness Node"); app_options.add_options() - ("help,h", "Print this help message and exit.") - ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; + ("help,h", "Print this help message and exit.") + ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") + ; bpo::variables_map options; @@ -89,8 +89,8 @@ int main(int argc, char** argv) { } catch (const boost::program_options::error& e) { - std::cerr << "Error parsing command line: " << e.what() << "\n"; - return 1; + std::cerr << "Error parsing command line: " << e.what() << "\n"; + return 1; } if( options.count("help") ) @@ -125,7 +125,7 @@ int main(int argc, char** argv) { wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string())); } } - else + else { ilog("Writing new config file at ${path}", ("path", config_ini_path)); if( !fc::exists(data_dir) ) @@ -154,7 +154,7 @@ int main(int argc, char** argv) { out_cfg << "\n"; } write_default_logging_config_to_stream(out_cfg); - out_cfg.close(); + out_cfg.close(); // read the default logging config we just wrote out to the file and start using it fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); if (logging_config) @@ -171,13 +171,13 @@ int main(int argc, char** argv) { fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); fc::set_signal_handler([&exit_promise](int signal) { - elog( "Caught SIGINT attempting to exit cleanly" ); - exit_promise->set_value(signal); + elog( "Caught SIGINT attempting to exit cleanly" ); + exit_promise->set_value(signal); }, SIGINT); fc::set_signal_handler([&exit_promise](int signal) { - elog( "Caught SIGTERM attempting to exit cleanly" ); - exit_promise->set_value(signal); + elog( "Caught SIGTERM attempting to exit cleanly" ); + exit_promise->set_value(signal); }, SIGTERM); ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); @@ -203,7 +203,7 @@ int main(int argc, char** argv) { } } -// logging config is too complicated to be parsed by boost::program_options, +// logging config is too complicated to be parsed by boost::program_options, // so we do it by hand // // Currently, you can only specify the filenames and logging levels, which @@ -213,21 +213,21 @@ int main(int argc, char** argv) { void write_default_logging_config_to_stream(std::ostream& out) { out << "# declare an appender named \"stderr\" that writes messages to the console\n" - "[log.console_appender.stderr]\n" - "stream=std_error\n\n" - "# declare an appender named \"p2p\" that writes messages to p2p.log\n" - "[log.file_appender.p2p]\n" - "filename=logs/p2p/p2p.log\n" - "# filename can be absolute or relative to this config file\n\n" - "# route any messages logged to the default logger to the \"stderr\" logger we\n" - "# declared above, if they are info level are higher\n" - "[logger.default]\n" - "level=info\n" - "appenders=stderr\n\n" - "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" - "[logger.p2p]\n" - "level=debug\n" - "appenders=p2p\n\n"; + "[log.console_appender.stderr]\n" + "stream=std_error\n\n" + "# declare an appender named \"p2p\" that writes messages to p2p.log\n" + "[log.file_appender.p2p]\n" + "filename=logs/p2p/p2p.log\n" + "# filename can be absolute or relative to this config file\n\n" + "# route any messages logged to the default logger to the \"stderr\" logger we\n" + "# declared above, if they are info level are higher\n" + "[logger.default]\n" + "level=info\n" + "appenders=stderr\n\n" + "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" + "[logger.p2p]\n" + "level=debug\n" + "appenders=p2p\n\n"; } fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) @@ -257,14 +257,14 @@ fc::optional load_logging_config_from_ini_file(const fc::pat // stdout/stderr will be taken from ini file, everything else hard-coded here fc::console_appender::config console_appender_config; console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::debug, - fc::console_appender::color::green)); + fc::console_appender::level_color(fc::log_level::debug, + fc::console_appender::color::green)); console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::warn, - fc::console_appender::color::brown)); + fc::console_appender::level_color(fc::log_level::warn, + fc::console_appender::color::brown)); console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::error, - fc::console_appender::color::cyan)); + fc::console_appender::level_color(fc::log_level::error, + fc::console_appender::color::cyan)); console_appender_config.stream = fc::variant(stream_name).as(); logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config))); found_logging_config = true; @@ -275,7 +275,7 @@ fc::optional load_logging_config_from_ini_file(const fc::pat fc::path file_name = section_tree.get("filename"); if (file_name.is_relative()) file_name = fc::absolute(config_ini_filename).parent_path() / file_name; - + // construct a default file appender config here // filename will be taken from ini file, everything else hard-coded here @@ -295,8 +295,8 @@ fc::optional load_logging_config_from_ini_file(const fc::pat std::string appenders_string = section_tree.get("appenders"); fc::logger_config logger_config(logger_name); logger_config.level = fc::variant(level_string).as(); - boost::split(logger_config.appenders, appenders_string, - boost::is_any_of(" ,"), + boost::split(logger_config.appenders, appenders_string, + boost::is_any_of(" ,"), boost::token_compress_on); logging_config.loggers.push_back(logger_config); found_logging_config = true; @@ -308,4 +308,4 @@ fc::optional load_logging_config_from_ini_file(const fc::pat return fc::optional(); } FC_RETHROW_EXCEPTIONS(warn, "") -} \ No newline at end of file +} diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 0ae9a961..d1b9119d 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -81,7 +81,7 @@ database_fixture::database_fixture() genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); genesis_state.initial_timestamp = time_point_sec( (fc::time_point::now().sec_since_epoch() / GRAPHENE_DEFAULT_BLOCK_INTERVAL) * GRAPHENE_DEFAULT_BLOCK_INTERVAL ); -// genesis_state.initial_parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; +// genesis_state.initial_parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; genesis_state.initial_active_witnesses = 10; for( unsigned i = 0; i < genesis_state.initial_active_witnesses; ++i )