Compare commits

..

168 commits

Author SHA1 Message Date
Bobinson K B
d3d967a2d7 Merge branch 'hotfix/bookie2024' into 'master'
Bookie 2024

See merge request PBSA/peerplays!259
2023-12-18 06:17:23 +00:00
serkixenos
178756bd34 Bookie 2024 2023-12-18 06:17:22 +00:00
Bobinson K B
1f70857d64 Merge branch 'beatrice' into 'master'
Mainnet release

See merge request PBSA/peerplays!251
2023-10-06 10:50:31 +00:00
Vlad Dobromyslov
97e85a849d Merge branch 'develop' into 'beatrice'
Set HARDFORK_SON_FOR_ETHEREUM_TIME to 24 of October

See merge request PBSA/peerplays!250
2023-10-04 16:51:45 +00:00
Vlad Dobromyslov
dc4cdd6e4b Set HARDFORK_SON_FOR_ETHEREUM_TIME to 24 of October 2023-10-04 16:51:45 +00:00
Vlad Dobromyslov
1472066af6 Merge branch 'develop' into 'beatrice'
Fixes for 1.5.25-beta

See merge request PBSA/peerplays!249
2023-10-03 16:24:00 +00:00
Vlad Dobromyslov
a641b8e93f Fixes for 1.5.25-beta 2023-10-03 16:23:59 +00:00
Vlad Dobromyslov
aa099f960f Merge branch 'develop' into 'beatrice'
Fixes for 1.5.24-beta

See merge request PBSA/peerplays!246
2023-08-23 14:31:39 +00:00
Vlad Dobromyslov
f0654e5ffd Fixes for 1.5.24-beta 2023-08-23 14:31:38 +00:00
Vlad Dobromyslov
9fe351300b Merge branch 'develop' into 'beatrice'
Set test-e2e as manual

See merge request PBSA/peerplays!242
2023-07-17 12:49:53 +00:00
Vlad Dobromyslov
5fd79c3e78 Set test-e2e as manual 2023-07-17 12:49:53 +00:00
Vlad Dobromyslov
b56818b8ae Merge branch 'develop' into 'beatrice'
Change DB_VERSION to PPY2.5

See merge request PBSA/peerplays!240
2023-07-13 16:44:03 +00:00
Vlad Dobromyslov
bc0fbeb707 Change DB_VERSION to PPY2.5 2023-07-13 16:44:03 +00:00
Vlad Dobromyslov
accd334a86 Merge branch 'develop' into 'beatrice'
NEW HARDFORK TIME FOR SON ETH

See merge request PBSA/peerplays!238
2023-07-07 12:10:44 +00:00
Vlad Dobromyslov
84a66c6722 NEW HARDFORK TIME FOR SON ETH 2023-07-07 12:10:44 +00:00
Vlad Dobromyslov
abd446d80b Merge branch 'develop' into 'beatrice'
Fix balance discrepancies in 1.5.23-beta

See merge request PBSA/peerplays!235
2023-07-06 05:31:28 +00:00
Vlad Dobromyslov
93fb57c080 Fix balance discrepancies in 1.5.23-beta 2023-07-06 05:31:28 +00:00
Bobinson K B
a8845ffde9 Merge branch 'develop' into 'beatrice'
Fix issue with balance discrepancies in 1.5.23-beta

See merge request PBSA/peerplays!232
2023-06-20 07:37:24 +00:00
Vlad Dobromyslov
435c1f8e96 Fix issue with balance discrepancies in 1.5.23-beta 2023-06-20 07:37:24 +00:00
Bobinson K B
1123ff6f93 Merge branch 'develop' into 'beatrice'
Fixes for public testnet

See merge request PBSA/peerplays!230
2023-06-09 08:10:26 +00:00
Vlad Dobromyslov
c34415b403 Fixes for public testnet 2023-06-09 08:10:26 +00:00
Christopher Sanborn
daca2813ef Merge branch 'testnet-set-hf-dates' into 'beatrice'
Set Hard Fork dates for testnet and mainnet
2023-05-25 13:25:09 -04:00
Christopher Sanborn
0b37a48b02 Set Hard Fork dates for testnet and main net. 2023-05-25 13:23:05 -04:00
Bobinson K B
e3b10cf1ec Merge branch 'testnet-builds' into 'beatrice'
Updated build rules for mainnet and testnet

See merge request PBSA/peerplays!223
2023-05-17 16:25:03 +00:00
Rily Dunlap
f5c6a6310b Updated build rules for mainnet and testnet 2023-05-17 14:50:10 +00:00
Bobinson K B
75ee6fbed3 Merge branch 'develop' into 'beatrice'
New set of functionality

See merge request PBSA/peerplays!220
2023-05-16 11:46:25 +00:00
Vlad Dobromyslov
7516126d01 New set of functionality 2023-05-16 11:46:25 +00:00
Vlad Dobromyslov
f32a51d03b Merge branch 'develop' into 'beatrice'
Alpha release 06.03.2023

See merge request PBSA/peerplays!215
2023-03-06 16:57:45 +00:00
Vlad Dobromyslov
c421453621 Merge branch 'bug/513-num_son_merge' into 'develop'
num_son no overwriting

See merge request PBSA/peerplays!213
2023-03-06 15:50:17 +00:00
Milo M
54ff842db1 num_son no overwriting 2023-03-06 15:50:17 +00:00
Vlad Dobromyslov
16ba10ffab Merge branch 'feature/SON-connection-pool' into 'develop'
SON connection pool

See merge request PBSA/peerplays!181
2023-03-01 06:01:52 +00:00
timur
79974280c0 SON connection pool 2023-03-01 06:01:52 +00:00
Vlad Dobromyslov
5867a8ae27 Merge branch 'bug/501-connection-pool' into 'develop'
#501 - concurrent_unordered_set for connection

See merge request PBSA/peerplays!212
2023-02-27 13:34:41 +00:00
serkixenos
741534c47f Merge branch 'bug/509-hive-withdrawal' into 'develop'
#509 - fix hive withdrawal processing

See merge request PBSA/peerplays!211
2023-02-24 09:37:55 +00:00
serkixenos
f3227fb33d Merge branch 'bug/421-fix-double' into 'develop'
automated test for nft_lottery

See merge request PBSA/peerplays!208
2023-02-24 09:36:10 +00:00
Milo M
bfb961c7be automated test for nft_lottery 2023-02-24 09:36:10 +00:00
serkixenos
5e08b793c5 Merge branch 'bug/495-hive-wallet-update' into 'develop'
#495 hive wallet update

See merge request PBSA/peerplays!210
2023-02-24 09:31:51 +00:00
Vlad Dobromyslov
7af3d037b5 #495 hive wallet update 2023-02-24 09:31:51 +00:00
Vlad Dobromyslov
2788281062 #501 - concurrent_unordered_set for connection 2023-02-23 17:55:49 +02:00
serkixenos
4e2850f826 Fix libbitcoin build in docker and related README instructions 2023-02-17 05:45:33 +01:00
Vlad Dobromyslov
f477af6771 #509 - fix hive withdrawal processing 2023-02-16 12:45:03 +02:00
serkixenos
80d168e5b6 Merge branch 'bug/421-fix-double' into 'develop'
#421 fix double in consensus of nft_lottery_token_purchase

See merge request PBSA/peerplays!205
2023-02-10 14:50:23 +00:00
Milos Milosevic
e44ed0cfe5 #421 fix double in consensus of nft_lottery_token_purchase 2023-02-10 14:50:23 +00:00
serkixenos
142cf5b903 Merge branch 'develop' into 'beatrice'
Merge develop to beatrice 2023-02

See merge request PBSA/peerplays!206
2023-02-10 13:56:17 +00:00
serkixenos
ebc1529c48 Merge branch 'bug/492-eth-withdrawal' into 'develop'
#492 - fix withdrawal encoders for big numbers

See merge request PBSA/peerplays!207
2023-02-10 13:54:53 +00:00
Vlad Dobromyslov
19e0911d64 #492 - fix withdrawal encoders for big numbers 2023-02-10 13:34:53 +02:00
serkixenos
70cd09495e Merge branch 'feature/libbitcoin-son-final' into 'develop'
Bitcoin SON based on libbitcoin

See merge request PBSA/peerplays!164
2023-02-06 22:48:40 +00:00
Davor Hirunda
e9c7021e16 Bitcoin SON based on libbitcoin 2023-02-06 22:48:40 +00:00
serkixenos
9c9aaa03d3 Merge branch 'bug/506-zero-fees' into 'develop'
#506 - fix load fees from genesis file

See merge request PBSA/peerplays!203
2023-02-06 20:57:51 +00:00
serkixenos
038fa37cc6 Merge branch 'bug/507/bitcoin_regression_detected_on_develop_branch' into 'develop'
Fix the issue for wrong number of signers

See merge request PBSA/peerplays!204
2023-02-06 20:48:11 +00:00
hirunda
73b2ba635b Fix the issue for wrong number of signers 2023-02-06 21:09:28 +01:00
Vlad Dobromyslov
936f13d2a1 #506 - fix load fees from genesis file 2023-02-06 08:14:05 +02:00
Vlad Dobromyslov
fc1cdf2629 Fix build errors active_sidechain_types 2023-02-01 20:54:44 +02:00
serkixenos
71f0806b25 Merge branch 'invalid_son_number_reflection' into 'develop'
Fix replay blockchain

See merge request PBSA/peerplays!202
2023-02-01 17:13:03 +00:00
Vlad Dobromyslov
da3a858aa6 Fix replay blockchain 2023-02-01 17:13:02 +00:00
serkixenos
1883f97be2 Merge branch 'bug/500-primary-wallet-transaction' into 'develop'
#500 - fix son_wallet_update_operation

See merge request PBSA/peerplays!201
2023-02-01 12:35:58 +00:00
serkixenos
559769db2b Merge branch 'feature/473-erc20-support' into 'develop'
#473 erc20-support

See merge request PBSA/peerplays!198
2023-01-31 10:48:45 +00:00
Vlad Dobromyslov
0b64f0cfcc #473 erc20-support 2023-01-31 10:48:45 +00:00
serkixenos
96d737fbc2 Merge branch 'master' into beatrice 2023-01-31 11:45:36 +01:00
Vlad Dobromyslov
d89e5e1f23 #500 - fix son_wallet_update_operation 2023-01-30 19:27:27 +02:00
Bobinson K B
6f472d3d3b Merge branch 'develop' into 'beatrice'
Merge develop to beatrice 2022-12

See merge request PBSA/peerplays!193
2023-01-03 07:33:40 +00:00
serkixenos
cb60cbe5d1 Merge branch 'hotfix/extend_get_block_api' into 'develop'
Streamline get_block API from database and cli wallet

See merge request PBSA/peerplays!197
2022-12-28 07:45:45 +00:00
serkixenos
576c54a260 Merge branch 'bug/499-eth-son_wallet_deposit_process_operation' into 'develop'
#499 - son_wallet_deposit_process_operation approve fix

See merge request PBSA/peerplays!196
2022-12-28 07:45:18 +00:00
serkixenos
2f5e12b28e Merge branch 'bug/496-process_primary_wallet' into 'develop'
#496 process primary wallet

See merge request PBSA/peerplays!195
2022-12-28 07:44:29 +00:00
Vlad Dobromyslov
68fbd6f40b #496 process primary wallet 2022-12-28 07:44:29 +00:00
serkixenos
674b38910d Streamline get_block API from database and cli wallet 2022-12-28 08:23:29 +01:00
Vlad Dobromyslov
d264398a6f #499 - son_wallet_deposit_process_operation approve fix 2022-12-23 15:37:15 +02:00
serkixenos
c1d5691ce2 Merge branch 'feature/nft_get_metadata_by_owner' into 'develop'
Add or revive a few NFT-listing APIs

See merge request PBSA/peerplays!194
2022-12-21 17:45:42 +00:00
timur
ca69a692cc Add or revive a few NFT-listing APIs 2022-12-21 17:45:42 +00:00
serkixenos
bd041bc13f Merge branch 'bug/489-error-message-gas-prediction' into 'develop'
#489 - Fix error message

See merge request PBSA/peerplays!190
2022-12-21 17:24:55 +00:00
serkixenos
6037e89df0 Merge branch 'hive-fix-son-account-owner-authority' into 'develop'
Update Hive's son-account owner authority on primary wallet update

See merge request PBSA/peerplays!192
2022-12-16 04:32:04 +00:00
serkixenos
bb7c534b10 Update Hive's son-account owner authority on primary wallet update 2022-12-16 04:27:31 +01:00
serkixenos
1be6636bbf Merge branch 'bug/api-doc-generation-hotfix-2' into 'develop'
Fix Ubuntu 18 build

See merge request PBSA/peerplays!185
2022-12-16 00:34:34 +00:00
timur
578edc56d8 Fix Ubuntu 18 build 2022-12-16 00:34:33 +00:00
Vlad Dobromyslov
d387e324fe #489 - Fix error message 2022-12-14 15:52:28 +02:00
serkixenos
1bf5c82101 Merge branch 'bug/484-multiple-eth-withdrawals' into 'develop'
#484 multiple eth withdrawals

See merge request PBSA/peerplays!189
2022-12-08 14:03:14 +00:00
Vlad Dobromyslov
4883dfe38d #484 multiple eth withdrawals 2022-12-08 14:03:14 +00:00
serkixenos
249276b009 Merge branch 'feature/479-one-bunch-transaction' into 'develop'
#479 one bunch transaction

See merge request PBSA/peerplays!188
2022-12-08 14:02:53 +00:00
Vlad Dobromyslov
ab1e08a756 #479 one bunch transaction 2022-12-08 14:02:53 +00:00
serkixenos
440e4fbb43 Merge branch 'bug/482-burn-eth-from-son-account' into 'develop'
#482 burn eth from son account

See merge request PBSA/peerplays!186
2022-12-06 12:17:09 +00:00
Vlad Dobromyslov
42b3890a7c #482 burn eth from son account 2022-12-06 12:17:09 +00:00
serkixenos
5dff0830fb Merge branch 'feature/479-one-bunch-transaction' into 'develop'
#479 - Send one transaction for all owners

See merge request PBSA/peerplays!183
2022-12-01 01:53:39 +00:00
Vlad Dobromyslov
c3eab0a80b #479 - Send one transaction for all owners 2022-12-01 01:53:39 +00:00
serkixenos
12c0c66f4b Merge branch 'feature/478-estimate-transaction-fee' into 'develop'
#478 - fix information warning

See merge request PBSA/peerplays!184
2022-12-01 01:53:01 +00:00
Vlad Dobromyslov
b7113c4ff3 #478 - fix information warning 2022-11-28 09:35:00 +02:00
serkixenos
d76b752c8c Merge branch 'develop' into 'beatrice'
Merge develop to beatrice 2022-11

See merge request PBSA/peerplays!180
2022-11-24 17:48:46 +00:00
serkixenos
804376b149 Merge branch 'beatrice' into develop 2022-11-23 17:35:57 +01:00
serkixenos
1b340345f3 Merge branch 'feature/478-estimate-transaction-fee' into 'develop'
#478 estimate transaction fee

See merge request PBSA/peerplays!179
2022-11-22 20:44:07 +00:00
Vlad Dobromyslov
022fdeb40a #478 estimate transaction fee 2022-11-22 20:44:07 +00:00
serkixenos
f6d22466fd Merge branch 'bug/462/investigate_trx_which_caused_mainnet_halt' into 'develop'
Fix for undo crash

See merge request PBSA/peerplays!172
2022-11-15 22:34:05 +00:00
Davor Hirunda
811d68ef4d Fix for undo crash 2022-11-15 22:34:05 +00:00
serkixenos
a6da2a6413 Merge branch 'bug/433-choosing-active-sons' into 'develop'
#433 Down active sons are not substituted

See merge request PBSA/peerplays!177
2022-11-15 22:33:04 +00:00
Milos Milosevic
8853a76752 #433 Down active sons are not substituted 2022-11-15 22:33:04 +00:00
serkixenos
f209ab8ee6 Merge branch 'bug/481-ethereum-listener' into 'develop'
#481 ethereum listener

See merge request PBSA/peerplays!178
2022-11-14 13:42:24 +00:00
Vlad Dobromyslov
9620e3c211 #481 ethereum listener 2022-11-14 13:42:23 +00:00
serkixenos
3ebcd29e10 Merge branch 'bug/476-fix-v-signing-value' into 'develop'
#476 - fix calculating v value from chain id

See merge request PBSA/peerplays!174
2022-11-10 11:35:14 +00:00
serkixenos
d5b2b7aeda Merge branch 'bug/470-get-network_id' into 'develop'
#470 Don't use admin_nodeInfo when get network_id

See merge request PBSA/peerplays!173
2022-11-10 11:35:07 +00:00
Vlad Dobromyslov
759dac5d41 #476 - fix calculating v value from chain id 2022-11-07 12:16:59 +02:00
Bobinson K B
058937a3ee Merge branch 'hotfix/sidechain-address' into 'master'
Hotfix: Fix sidechain address editing

See merge request PBSA/peerplays!171
2022-11-04 11:38:30 +00:00
serkixenos
da73e31038 Hotfix: Fix sidechain address editing 2022-11-04 11:38:30 +00:00
Vlad Dobromyslov
a30325660d #470 Don't use admin_nodeInfo when get network_id 2022-11-04 07:51:22 +02:00
serkixenos
d5d6390030 Merge branch 'bug/461-system_error-exception' into 'develop'
#461 - handle exception when execute POST request

See merge request PBSA/peerplays!165
2022-10-30 02:30:59 +00:00
serkixenos
400c3cfb89 Merge branch 'bug/467-prev-set-active-sons' into 'develop'
#467 - fix create_primary_wallet_transaction signers

See merge request PBSA/peerplays!169
2022-10-30 02:30:22 +00:00
Vlad Dobromyslov
aa90f715fd #467 - fix create_primary_wallet_transaction signers 2022-10-30 02:30:21 +00:00
Davor Hirunda
c3b2a598b4 Fix for undo crash 2022-10-30 03:26:53 +01:00
serkixenos
06bc65cc79 #450 Add documentation for undocumented methods 2022-10-30 03:21:18 +01:00
serkixenos
8e8142235a Merge branch 'bug/issue-463' into 'develop'
#463 Fix unused variable son_count_histogram_buffer in Release mode

See merge request PBSA/peerplays!166
2022-10-19 17:37:23 +00:00
Milos Milosevic
194fa6abfa #463 Fix unused variable son_count_histogram_buffer in Release mode 2022-10-19 17:37:23 +00:00
Vlad Dobromyslov
283fbd28f7 #461 - handle exception when execute POST request 2022-10-18 10:19:03 +03:00
serkixenos
71c113c190 Merge branch 'issue-436-cli_tests' into 'develop'
Updated CLI Tests [Issue 436]

See merge request PBSA/peerplays!159
2022-10-11 00:13:04 +00:00
Meheboob Khan
846366139f Updated CLI Tests [Issue 436] 2022-10-11 00:13:04 +00:00
serkixenos
0856e898bb Merge branch 'update-cli-wallet-docs' into 'develop'
Update cli wallet docs

See merge request PBSA/peerplays!162
2022-10-03 17:34:01 +00:00
serkixenos
4db9f3a15b Update cli wallet docs 2022-10-03 17:34:01 +00:00
serkixenos
1b1df25023 Merge branch 'bug/fix-api-doc-generation-for-map' into 'develop'
Fix API docs generation for map<> and flat_map<>

See merge request PBSA/peerplays!161
2022-10-03 17:31:59 +00:00
serkixenos
f9314a4c0c Merge branch 'bug/457-multithread-son-processing' into 'develop'
bug/457-multithread-son-processing

See merge request PBSA/peerplays!160
2022-10-03 17:04:56 +00:00
Vlad Dobromyslov
d4c015d400 bug/457-multithread-son-processing 2022-10-03 17:04:56 +00:00
timur
e6474f5f2a Fix API docs generation for map<> and flat_map<> 2022-10-01 19:11:54 -03:00
serkixenos
f2f4b57ced Merge branch 'bug/455-sidechain-enabled' into 'develop'
bug/455-sidechain-enabled

See merge request PBSA/peerplays!157
2022-09-27 13:58:07 +00:00
Vlad Dobromyslov
2c95ac0b9d bug/455-sidechain-enabled 2022-09-27 13:58:07 +00:00
serkixenos
9831579bfe Merge branch 'feature/update-debug-node' into 'develop'
Update debug_witness plugin

See merge request PBSA/peerplays!153
2022-09-27 12:25:33 +00:00
timur
2d6dec5943 Update debug_witness plugin 2022-09-27 12:25:33 +00:00
serkixenos
46f4770071 Merge branch 'bug/451-update-son-list-on-maintenance' into 'develop'
bug/451-update-son-list-on-maintenance

See merge request PBSA/peerplays!154
2022-09-27 10:41:35 +00:00
Vlad Dobromyslov
2accee53e2 bug/451-update-son-list-on-maintenance 2022-09-27 10:41:35 +00:00
serkixenos
54a11e7662 Merge branch 'issue-389' into 'develop'
Added functionality to check if the device size is less than 50 MB [Issue 389]

See merge request PBSA/peerplays!151
2022-09-26 20:14:32 +00:00
Meheboob Khan
c1f93f58ee Added functionality to check if the device size is less than 50 MB [Issue 389] 2022-09-26 20:14:31 +00:00
serkixenos
2fd6f60112 Merge branch 'feature/son-for-ethereum' into 'develop'
SON for Ethereum

See merge request PBSA/peerplays!133
2022-09-19 19:23:39 +00:00
serkixenos
5f97eb7662 SON for Ethereum 2022-09-19 19:23:39 +00:00
serkixenos
b4e8b76a30 Merge branch 'bug/fix-wallet-api-doc-generation' into 'develop'
Bug/fix wallet api doc generation

See merge request PBSA/peerplays!152
2022-09-16 02:42:31 +00:00
timur
a9267544de Bug/fix wallet api doc generation 2022-09-16 02:42:31 +00:00
serkixenos
e287d8a845 Merge branch 'issue-430' into 'develop'
Improved get_active_sons and get_son_network_status API/CLI [issue 430]

See merge request PBSA/peerplays!150
2022-09-14 18:03:40 +00:00
Meheboob Khan
0f64947f4a Improved get_active_sons and get_son_network_status API/CLI [issue 430] 2022-09-14 18:03:40 +00:00
serkixenos
b895b52b7b Cherrypick important fixes/cosmetics from feature/son-for-ethereum 2022-09-08 17:13:18 +02:00
serkixenos
9c5aab826d Merge branch 'feature/update-delayed-node' into 'develop'
Update delayed node feature

See merge request PBSA/peerplays!145
2022-09-07 13:57:17 +00:00
serkixenos
6a38fb2382 Update delayed node feature 2022-09-07 13:57:17 +00:00
serkixenos
0a9a324277 Merge branch 'port_net_library' into 'develop'
Port net library

See merge request PBSA/peerplays!147
2022-09-07 13:57:00 +00:00
Meheboob Khan
5c416e3a5b Port net library 2022-09-07 13:57:00 +00:00
serkixenos
9268c31ac4 Code formatting 2022-08-25 17:41:44 +02:00
serkixenos
c4f6f522a4 Merge branch 'bug/fix-unit-tests-develop' into 'develop'
Fix unit test failing on develop branch, #418

See merge request PBSA/peerplays!143
2022-08-25 11:14:17 +00:00
serkixenos
f127495c0e Fix unit test failing on develop branch, #418 2022-08-25 12:21:22 +02:00
serkixenos
9b2c60f76c Merge branch 'feature/son-for-hive-voting' into 'develop'
SON for Hive voting

See merge request PBSA/peerplays!81
2022-07-26 23:17:42 +00:00
serkixenos
22fc780a91 SON for Hive voting 2022-07-26 23:17:42 +00:00
Bobinson K B
611a63076b Merge branch 'beatrice' into 'master'
Merge beatrice to master 2022-06

See merge request PBSA/peerplays!131
2022-06-27 07:36:08 +00:00
serkixenos
d234c3a8f8 Set HARDFORK_SON3_TIME to 2022-07-16T00:00:00 2022-06-24 14:39:51 +02:00
serkixenos
d650e197a9 Add port to sync node endpoints 2022-06-20 14:10:08 +02:00
serkixenos
0a38927b0e Allow querying witness_node version by API 2022-06-17 19:08:59 +02:00
serkixenos
9012e86bd1 Add more mainnet seed nodes 2022-06-17 19:08:47 +02:00
serkixenos
1788038224 Add hardcoded seed nodes to the config file 2022-06-16 02:06:11 +02:00
serkixenos
bcdb355f48 Fix P2P port/endpoint error message 2022-06-15 06:31:38 +02:00
serkixenos
d78e0d0e48 Silence SON logs when not needed 2022-06-15 06:30:33 +02:00
hirunda
4d112936d2 Replace fc::random with std random generator 2022-06-15 05:43:00 +02:00
Vlad Dobromyslov
c102bef768 #345 double-free-or-corruption 2022-06-15 05:41:24 +02:00
serkixenos
092a46ae61 Remove unscheduled hardfork CORE 210 2022-06-15 05:41:17 +02:00
serkixenos
f03cc7ee90 Sidechain API, SONs listener log 2022-06-15 05:28:17 +02:00
serkixenos
3294480e20 Update GitLab CI file, more manual build options 2022-06-15 05:13:10 +02:00
Vlad Dobromyslov
7fd12ccce8 #386 - check valid() for optional<operation_history_object> 2022-06-15 05:09:21 +02:00
Davor Hirunda
03e37896d5 Cancel the thread for sync blocks 2022-06-15 05:09:15 +02:00
Davor Hirunda
bc7d03cb22 Fix for scheduler wrong state 2022-06-15 05:09:06 +02:00
serkixenos
b3e426999d Remove unused libreadline-dev library 2022-06-15 05:08:02 +02:00
serkixenos
ab32415d0c Update GitLab CI file, more manual build options 2022-06-15 05:07:56 +02:00
serkixenos
3152d47eea Update GitLab CI file, more manual build options 2022-06-15 05:07:46 +02:00
serkixenos
8f32e4cdb5 Update README instructions for starting docker containers 2022-06-15 05:05:19 +02:00
serkixenos
b4501167ee Update README instructions for docker build 2022-06-15 05:04:59 +02:00
serkixenos
d13551a277 Remove fc based RNG 2022-06-15 05:04:37 +02:00
serkixenos
23cdcec381 Update README instructions for starting docker containers 2022-05-13 03:26:30 +02:00
serkixenos
82a84a06da Update README instructions for docker build 2022-05-12 13:54:52 +02:00
Bobinson K B
6a59d9efba Merge branch 'beatrice' into 'master'
Merge beatrice to master 2022-04

See merge request PBSA/peerplays!84
2022-04-14 13:18:26 +00:00
Bobinson K B
9fd18b32c4 Merge branch 'beatrice' into 'master'
Hotfix: Revert change to a son_update_operation

See merge request PBSA/peerplays!50
2021-12-17 13:33:23 +00:00
serkixenos
632eb4a231 Hotfix: Revert change to a son_update_operation 2021-12-17 13:33:23 +00:00
132 changed files with 8888 additions and 4748 deletions

View file

@ -9,6 +9,8 @@ stages:
- build - build
- test - test
- dockerize - dockerize
- python-test
- deploy
build-mainnet: build-mainnet:
stage: build stage: build
@ -44,12 +46,11 @@ test-mainnet:
dockerize-mainnet: dockerize-mainnet:
stage: dockerize stage: dockerize
dependencies:
- test-mainnet
variables: variables:
IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
before_script: before_script:
- docker info - docker info
- docker builder prune -a -f
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script: script:
- docker build --no-cache -t $IMAGE . - docker build --no-cache -t $IMAGE .
@ -58,8 +59,6 @@ dockerize-mainnet:
- docker rmi $IMAGE - docker rmi $IMAGE
tags: tags:
- builder - builder
when:
manual
timeout: timeout:
3h 3h
@ -80,12 +79,27 @@ build-testnet:
- build/libraries/ - build/libraries/
- build/programs/ - build/programs/
- build/tests/ - build/tests/
when: manual
tags: tags:
- builder - builder
when:
manual deploy-testnet:
timeout: stage: deploy
3h dependencies:
- build-testnet
script:
- sudo systemctl stop witness
- rm $WORK_DIR/peerplays/witness_node || true
- cp build/programs/witness_node/witness_node $WORK_DIR/peerplays/
- sudo systemctl restart witness
rules:
- if: $CI_COMMIT_BRANCH == "master"
when: always
environment:
name: devnet
url: $DEVNET_URL
tags:
- devnet
test-testnet: test-testnet:
stage: test stage: test
@ -105,8 +119,6 @@ test-testnet:
dockerize-testnet: dockerize-testnet:
stage: dockerize stage: dockerize
dependencies:
- test-testnet
variables: variables:
IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
before_script: before_script:
@ -123,3 +135,37 @@ dockerize-testnet:
manual manual
timeout: timeout:
3h 3h
test-e2e:
stage: python-test
variables:
IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
before_script:
- docker info
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
- git clone https://gitlab.com/PBSA/tools-libs/peerplays-utils.git
- cd peerplays-utils/peerplays-qa-environment
- git checkout origin/feature/python-e2e-tests-for-CI
- cd e2e-tests/
- python3 -m venv venv
- source venv/bin/activate
- pip3 install -r requirements.txt
- docker-compose down --remove-orphans
- docker ps -a
- docker pull $IMAGE
- docker tag $IMAGE peerplays-base:latest
- docker image ls -a
- docker-compose build
- python3 main.py --start all
- docker ps -a
- python3 -m pytest test_btc_init_state.py test_hive_inital_state.py test_pp_inital_state.py
- python3 main.py --stop
- deactivate
- docker ps -a
after_script:
- docker rmi $(docker images -a | grep -v 'hive-for-peerplays\|ethereum-for-peerplays\|bitcoin-for-peerplays\|ubuntu-for-peerplays' | awk '{print $3}')
tags:
- python-tests
when:
manual

View file

@ -51,7 +51,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(GET arg_list 0 output) list(GET arg_list 0 output)
message("Ubuntu version is: ${output}") message("Ubuntu version is: ${output}")
add_definitions(-DPEERPLAYS_UBUNTU_VERSION=${output}) add_definitions(-DPEERPLAYS_UBUNTU_VERSION=${output})
endif() endif()
# function to help with cUrl # function to help with cUrl

View file

@ -1,5 +1,4 @@
FROM ubuntu:20.04 FROM ubuntu:20.04
MAINTAINER Peerplays Blockchain Standards Association
#=============================================================================== #===============================================================================
# Ubuntu setup # Ubuntu setup
@ -11,15 +10,14 @@ RUN \
apt-utils \ apt-utils \
autoconf \ autoconf \
bash \ bash \
bison \
build-essential \ build-essential \
ca-certificates \ ca-certificates \
cmake \
dnsutils \ dnsutils \
doxygen \
expect \ expect \
flex \
git \ git \
graphviz \ graphviz \
libboost-all-dev \
libbz2-dev \ libbz2-dev \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
libncurses-dev \ libncurses-dev \
@ -35,7 +33,6 @@ RUN \
ntp \ ntp \
openssh-server \ openssh-server \
pkg-config \ pkg-config \
perl \
python3 \ python3 \
python3-jinja2 \ python3-jinja2 \
sudo \ sudo \
@ -53,16 +50,105 @@ RUN echo 'peerplays:peerplays' | chpasswd
# SSH # SSH
EXPOSE 22 EXPOSE 22
WORKDIR /home/peerplays/src
#===============================================================================
# Boost setup
#===============================================================================
RUN \
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
tar -xzf boost_1_72_0.tar.gz && \
cd boost_1_72_0 && \
./bootstrap.sh && \
./b2 install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# cmake setup
#===============================================================================
RUN \
wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
cmake --version && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# libzmq setup # libzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
tar -xzvf v4.3.4.tar.gz && \
cd libzmq-4.3.4 && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc) && \
make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# cppzmq setup
#===============================================================================
RUN \ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
unzip v4.3.4.zip && \ tar -xzvf v4.9.0.tar.gz && \
cd libzmq-4.3.4 && \ cd cppzmq-4.9.0 && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc) && \
make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# gsl setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libpcre3-dev
RUN \
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \
tar -xzvf v4.1.4.tar.gz && \
cd gsl-4.1.4 && \
make -j$(nproc) && \
make install && \
rm -rf /home/peerplays/src/*
#===============================================================================
# libbitcoin-build setup
# libbitcoin-explorer setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libsodium-dev
RUN \
git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \
cd libbitcoin-explorer && \
./install.sh && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# Doxygen setup
#===============================================================================
RUN \
sudo apt install -y bison flex && \
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \
tar -xvf Release_1_8_17.tar.gz && \
cd doxygen-Release_1_8_17 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
@ -70,18 +156,14 @@ RUN \
ldconfig ldconfig
#=============================================================================== #===============================================================================
# cppzmq setup # Perl setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \
unzip v4.8.1.zip && \ tar -xvf v5.30.0.tar.gz && \
cd cppzmq-4.8.1 && \ cd perl5-5.30.0 && \
mkdir build && \ ./Configure -des && \
cd build && \
cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) install && \
ldconfig ldconfig
@ -89,8 +171,6 @@ RUN \
# Peerplays setup # Peerplays setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
## Clone Peerplays ## Clone Peerplays
#RUN \ #RUN \
# git clone https://gitlab.com/PBSA/peerplays.git && \ # git clone https://gitlab.com/PBSA/peerplays.git && \
@ -106,6 +186,8 @@ ADD . peerplays
# Configure Peerplays # Configure Peerplays
RUN \ RUN \
cd peerplays && \ cd peerplays && \
git submodule update --init --recursive && \
git log --oneline -n 5 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake -DCMAKE_BUILD_TYPE=Release .. cmake -DCMAKE_BUILD_TYPE=Release ..
@ -119,8 +201,8 @@ WORKDIR /home/peerplays/peerplays-network
# Setup Peerplays runimage # Setup Peerplays runimage
RUN \ RUN \
ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \
ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
RUN ./witness_node --create-genesis-json genesis.json && \ RUN ./witness_node --create-genesis-json genesis.json && \
rm genesis.json rm genesis.json

View file

@ -1,5 +1,4 @@
FROM ubuntu:18.04 FROM ubuntu:18.04
MAINTAINER Peerplays Blockchain Standards Association
#=============================================================================== #===============================================================================
# Ubuntu setup # Ubuntu setup
@ -11,11 +10,12 @@ RUN \
apt-utils \ apt-utils \
autoconf \ autoconf \
bash \ bash \
bison \
build-essential \ build-essential \
ca-certificates \ ca-certificates \
dnsutils \ dnsutils \
doxygen \
expect \ expect \
flex \
git \ git \
graphviz \ graphviz \
libbz2-dev \ libbz2-dev \
@ -33,7 +33,6 @@ RUN \
ntp \ ntp \
openssh-server \ openssh-server \
pkg-config \ pkg-config \
perl \
python3 \ python3 \
python3-jinja2 \ python3-jinja2 \
sudo \ sudo \
@ -51,41 +50,105 @@ RUN echo 'peerplays:peerplays' | chpasswd
# SSH # SSH
EXPOSE 22 EXPOSE 22
WORKDIR /home/peerplays/src
#=============================================================================== #===============================================================================
# Boost setup # Boost setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \ wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
tar xjf boost_1_71_0.tar.bz2 && \ tar -xzf boost_1_72_0.tar.gz && \
cd boost_1_71_0/ && \ cd boost_1_72_0 && \
./bootstrap.sh && \ ./bootstrap.sh && \
./b2 install ./b2 install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# cmake setup # cmake setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
cmake --version cmake --version && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# libzmq setup # libzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
tar -xzvf v4.3.4.tar.gz && \
cd libzmq-4.3.4 && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc) && \
make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# cppzmq setup
#===============================================================================
RUN \ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
unzip v4.3.4.zip && \ tar -xzvf v4.9.0.tar.gz && \
cd libzmq-4.3.4 && \ cd cppzmq-4.9.0 && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(nproc) && \
make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# gsl setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libpcre3-dev
RUN \
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \
tar -xzvf v4.1.4.tar.gz && \
cd gsl-4.1.4 && \
make -j$(nproc) && \
make install && \
rm -rf /home/peerplays/src/*
#===============================================================================
# libbitcoin-build setup
# libbitcoin-explorer setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libsodium-dev
RUN \
git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \
cd libbitcoin-explorer && \
./install.sh && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# Doxygen setup
#===============================================================================
RUN \
sudo apt install -y bison flex && \
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \
tar -xvf Release_1_8_17.tar.gz && \
cd doxygen-Release_1_8_17 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
@ -93,18 +156,14 @@ RUN \
ldconfig ldconfig
#=============================================================================== #===============================================================================
# cppzmq setup # Perl setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \
unzip v4.8.1.zip && \ tar -xvf v5.30.0.tar.gz && \
cd cppzmq-4.8.1 && \ cd perl5-5.30.0 && \
mkdir build && \ ./Configure -des && \
cd build && \
cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) install && \
ldconfig ldconfig
@ -112,8 +171,6 @@ RUN \
# Peerplays setup # Peerplays setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
## Clone Peerplays ## Clone Peerplays
#RUN \ #RUN \
# git clone https://gitlab.com/PBSA/peerplays.git && \ # git clone https://gitlab.com/PBSA/peerplays.git && \
@ -129,6 +186,9 @@ ADD . peerplays
# Configure Peerplays # Configure Peerplays
RUN \ RUN \
cd peerplays && \ cd peerplays && \
git submodule update --init --recursive && \
git symbolic-ref --short HEAD && \
git log --oneline -n 5 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake -DCMAKE_BUILD_TYPE=Release .. cmake -DCMAKE_BUILD_TYPE=Release ..
@ -142,8 +202,8 @@ WORKDIR /home/peerplays/peerplays-network
# Setup Peerplays runimage # Setup Peerplays runimage
RUN \ RUN \
ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \
ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
RUN ./witness_node --create-genesis-json genesis.json && \ RUN ./witness_node --create-genesis-json genesis.json && \
rm genesis.json rm genesis.json

150
README.md
View file

@ -8,100 +8,41 @@ This is a quick introduction to get new developers and witnesses up to speed on
Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04. Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04.
## Ubuntu 20.04 ## Ubuntu 20.04 and 18.04
Following dependencies are needed for a clean install of Ubuntu 20.04: Following dependencies are needed for a clean install of Ubuntu 20.04 and Ubuntu 18.04:
``` ```
sudo apt-get install \ sudo apt-get install \
apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ autoconf bash bison build-essential ca-certificates dnsutils expect flex git \
dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev libpcre3-dev \
libcurl4-openssl-dev libncurses-dev libsnappy-dev \ libsnappy-dev libsodium-dev libssl-dev libtool libzip-dev locales lsb-release \
libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ mc nano net-tools ntp openssh-server pkg-config python3 python3-jinja2 sudo \
openssh-server pkg-config perl python3 python3-jinja2 sudo \
systemd-coredump wget systemd-coredump wget
``` ```
Install libzmq from source: Boost libraries setup:
``` ```
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz
unzip v4.3.4.zip tar -xzf boost_1_72_0.tar.gz boost_1_72_0
cd libzmq-4.3.4 cd boost_1_72_0
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Install cppzmq from source:
```
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip
unzip v4.8.1.zip
cd cppzmq-4.8.1
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Building Peerplays
```
git clone https://gitlab.com/PBSA/peerplays.git
cd peerplays
git submodule update --init --recursive
# If you want to build Mainnet node
cmake -DCMAKE_BUILD_TYPE=Release
# If you want to build Testnet node
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1
# Update -j flag depending on your current system specs;
# Recommended 4GB of RAM per 1 CPU core
# make -j2 for 8GB RAM
# make -j4 for 16GB RAM
# make -j8 for 32GB RAM
make -j$(nproc)
sudo make install # this can install the executable files under /usr/local
```
## Ubuntu 18.04
Following dependencies are needed for a clean install of Ubuntu 18.04:
```
sudo apt-get install \
apt-utils autoconf bash build-essential ca-certificates clang-format \
dnsutils doxygen expect git graphviz libbz2-dev \
libcurl4-openssl-dev libncurses-dev libsnappy-dev \
libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \
openssh-server pkg-config perl python3 python3-jinja2 sudo \
systemd-coredump wget
```
Install Boost libraries from source
```
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2
tar xjf boost_1_67_0.tar.bz2
cd boost_1_67_0/
./bootstrap.sh ./bootstrap.sh
./b2
sudo ./b2 install sudo ./b2 install
sudo ldconfig
``` ```
Install cmake cmake setup:
``` ```
wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh
chmod 755 ./cmake-3.23.1-linux-x86_64.sh chmod 755 ./cmake-3.24.2-linux-x86_64.sh
sudo ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license sudo ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license
cmake --version
``` ```
Install libzmq from source: libzmq setup:
``` ```
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz
unzip v4.3.4.zip tar -xzvf v4.3.4.tar.gz
cd libzmq-4.3.4 cd libzmq-4.3.4
mkdir build mkdir build
cd build cd build
@ -111,11 +52,11 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
Install cppzmq from source: cppzmq setup:
``` ```
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz
unzip v4.8.1.zip tar -xzvf v4.9.0.tar.gz
cd cppzmq-4.8.1 cd cppzmq-4.9.0
mkdir build mkdir build
cd build cd build
cmake .. cmake ..
@ -124,6 +65,48 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
gsl setup:
```
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz
tar -xzvf v4.1.4.tar.gz
cd gsl-4.1.4
make -j$(nproc)
sudo make install
sudo ldconfig
```
libbitcoin-explorer setup:
```
git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git
cd libbitcoin-explorer
sudo ./install.sh
sudo ldconfig
```
Doxygen setup:
```
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz
tar -xvf Release_1_8_17.tar.gz
cd doxygen-Release_1_8_17
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Perl setup:
```
wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz
tar -xvf v5.30.0.tar.gz
cd perl5-5.30.0
./Configure -des
make -j$(nproc)
sudo make install
sudo ldconfig
```
Building Peerplays Building Peerplays
``` ```
git clone https://gitlab.com/PBSA/peerplays.git git clone https://gitlab.com/PBSA/peerplays.git
@ -146,7 +129,6 @@ make -j$(nproc)
sudo make install # this can install the executable files under /usr/local sudo make install # this can install the executable files under /usr/local
``` ```
## Docker images ## Docker images
Install docker, and add current user to docker group. Install docker, and add current user to docker group.

View file

@ -917,6 +917,7 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti
wanted.insert("accounts_list"); wanted.insert("accounts_list");
wanted.insert("affiliate_stats"); wanted.insert("affiliate_stats");
} }
if (!wanted.count("delayed_node") && !wanted.count("debug_witness") && !wanted.count("witness")) // explicitly requested delayed_node or debug_witness functionality suppresses witness functions
wanted.insert("witness"); wanted.insert("witness");
wanted.insert("bookie"); wanted.insert("bookie");
@ -949,7 +950,7 @@ void application::startup() {
} }
std::shared_ptr<abstract_plugin> application::get_plugin(const string &name) const { std::shared_ptr<abstract_plugin> application::get_plugin(const string &name) const {
return my->_active_plugins[name]; return is_plugin_enabled(name) ? my->_active_plugins[name] : nullptr;
} }
bool application::is_plugin_enabled(const string &name) const { bool application::is_plugin_enabled(const string &name) const {

View file

@ -71,6 +71,17 @@ std::string object_id_to_string(object_id_type id) {
return object_id; return object_id;
} }
signed_block_with_info::signed_block_with_info(){};
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());
}
class database_api_impl : public std::enable_shared_from_this<database_api_impl> { class database_api_impl : public std::enable_shared_from_this<database_api_impl> {
public: public:
database_api_impl(graphene::chain::database &db); database_api_impl(graphene::chain::database &db);
@ -89,6 +100,7 @@ public:
optional<block_header> get_block_header(uint32_t block_num) const; optional<block_header> get_block_header(uint32_t block_num) const;
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums) const; map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums) const;
optional<signed_block> get_block(uint32_t block_num) const; optional<signed_block> get_block(uint32_t block_num) const;
optional<signed_block_with_info> get_block2(uint32_t block_num) const;
vector<optional<signed_block>> get_blocks(uint32_t block_num_from, uint32_t block_num_to) const; vector<optional<signed_block>> get_blocks(uint32_t block_num_from, uint32_t block_num_to) const;
processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const; processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const;
@ -184,6 +196,10 @@ public:
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const; fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const; map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_son_count() const; uint64_t get_son_count() const;
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
// SON wallets // SON wallets
optional<son_wallet_object> get_active_son_wallet(); optional<son_wallet_object> get_active_son_wallet();
@ -265,8 +281,9 @@ public:
uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const; uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const;
nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const; nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const;
nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const; nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const;
vector<nft_object> nft_get_all_tokens() const; vector<nft_object> nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const;
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const; vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const;
vector<nft_metadata_object> nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const;
// Marketplace // Marketplace
vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const; vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const;
@ -287,6 +304,7 @@ public:
uint32_t api_limit_get_limit_orders_by_account = 101; uint32_t api_limit_get_limit_orders_by_account = 101;
uint32_t api_limit_get_order_book = 50; uint32_t api_limit_get_order_book = 50;
uint32_t api_limit_all_offers_count = 100; uint32_t api_limit_all_offers_count = 100;
uint32_t api_limit_nft_tokens = 100;
uint32_t api_limit_lookup_accounts = 1000; uint32_t api_limit_lookup_accounts = 1000;
uint32_t api_limit_lookup_witness_accounts = 1000; uint32_t api_limit_lookup_witness_accounts = 1000;
uint32_t api_limit_lookup_committee_member_accounts = 1000; uint32_t api_limit_lookup_committee_member_accounts = 1000;
@ -526,6 +544,17 @@ optional<signed_block> database_api_impl::get_block(uint32_t block_num) const {
return _db.fetch_block_by_number(block_num); return _db.fetch_block_by_number(block_num);
} }
optional<signed_block_with_info> database_api::get_block2(uint32_t block_num) const {
return my->get_block2(block_num);
}
optional<signed_block_with_info> database_api_impl::get_block2(uint32_t block_num) const {
auto result = _db.fetch_block_by_number(block_num);
if (result)
return signed_block_with_info(*result);
return {};
}
vector<optional<signed_block>> database_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const { vector<optional<signed_block>> database_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const {
return my->get_blocks(block_num_from, block_num_to); return my->get_blocks(block_num_from, block_num_to);
} }
@ -1848,6 +1877,80 @@ uint64_t database_api_impl::get_son_count() const {
return _db.get_index_type<son_index>().indices().size(); return _db.get_index_type<son_index>().indices().size();
} }
flat_map<sidechain_type, vector<son_sidechain_info>> database_api::get_active_sons() {
return my->get_active_sons();
}
flat_map<sidechain_type, vector<son_sidechain_info>> database_api_impl::get_active_sons() {
return get_global_properties().active_sons;
}
vector<son_sidechain_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) {
return my->get_active_sons_by_sidechain(sidechain);
}
vector<son_sidechain_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) {
const global_property_object &gpo = get_global_properties();
vector<son_sidechain_info> result;
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
result = gpo.active_sons.at(sidechain);
}
return result;
}
map<sidechain_type, map<son_id_type, string>> database_api::get_son_network_status() {
return my->get_son_network_status();
}
map<sidechain_type, map<son_id_type, string>> database_api_impl::get_son_network_status() {
map<sidechain_type, map<son_id_type, string>> result;
for (auto active_sidechain_type : active_sidechain_types(_db.head_block_time())) {
result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type);
}
return result;
}
map<son_id_type, string> database_api::get_son_network_status_by_sidechain(sidechain_type sidechain) {
return my->get_son_network_status_by_sidechain(sidechain);
}
map<son_id_type, string> database_api_impl::get_son_network_status_by_sidechain(sidechain_type sidechain) {
const global_property_object &gpo = get_global_properties();
map<son_id_type, string> result;
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
for (const auto si : gpo.active_sons.at(sidechain)) {
const auto son_obj = si.son_id(_db);
const auto sso = son_obj.statistics(_db);
string status;
if (sso.last_active_timestamp.find(sidechain) != sso.last_active_timestamp.end()) {
if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency())) > _db.head_block_time()) {
status = "OK, regular SON heartbeat";
} else {
if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time())) > _db.head_block_time()) {
status = "OK, irregular SON heartbeat, but not triggering SON down proposal";
} else {
status = "NOT OK, irregular SON heartbeat, triggering SON down proposal";
}
}
} else {
status = "No heartbeats sent";
}
result[si.son_id] = status;
}
}
return result;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// // // //
// SON Wallets // // SON Wallets //
@ -2083,7 +2186,9 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
const auto &committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>(); const auto &committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
const auto &for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>(); const auto &for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
const auto &against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>(); const auto &against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
const auto &son_idx = _db.get_index_type<son_index>().indices().get<by_vote_id>(); const auto &son_bictoin_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_bitcoin>();
const auto &son_hive_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_hive>();
const auto &son_ethereum_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_ethereum>();
vector<variant> result; vector<variant> result;
result.reserve(votes.size()); result.reserve(votes.size());
@ -2092,7 +2197,7 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
case vote_id_type::committee: { case vote_id_type::committee: {
auto itr = committee_idx.find(id); auto itr = committee_idx.find(id);
if (itr != committee_idx.end()) if (itr != committee_idx.end())
result.emplace_back(variant(*itr, 1)); result.emplace_back(variant(*itr, 2)); // Depth of committee_member_object is 1, add 1 to be safe
else else
result.emplace_back(variant()); result.emplace_back(variant());
break; break;
@ -2100,7 +2205,7 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
case vote_id_type::witness: { case vote_id_type::witness: {
auto itr = witness_idx.find(id); auto itr = witness_idx.find(id);
if (itr != witness_idx.end()) if (itr != witness_idx.end())
result.emplace_back(variant(*itr, 1)); result.emplace_back(variant(*itr, 2)); // Depth of witness_object is 1, add 1 here to be safe
else else
result.emplace_back(variant()); result.emplace_back(variant());
break; break;
@ -2108,26 +2213,45 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
case vote_id_type::worker: { case vote_id_type::worker: {
auto itr = for_worker_idx.find(id); auto itr = for_worker_idx.find(id);
if (itr != for_worker_idx.end()) { if (itr != for_worker_idx.end()) {
result.emplace_back(variant(*itr, 1)); result.emplace_back(variant(*itr, 4)); // Depth of worker_object is 3, add 1 here to be safe.
// If we want to extract the balance object inside,
// need to increase this value
} else { } else {
auto itr = against_worker_idx.find(id); auto itr = against_worker_idx.find(id);
if (itr != against_worker_idx.end()) { if (itr != against_worker_idx.end()) {
result.emplace_back(variant(*itr, 1)); result.emplace_back(variant(*itr, 4)); // Depth of worker_object is 3, add 1 here to be safe.
// If we want to extract the balance object inside,
// need to increase this value
} else { } else {
result.emplace_back(variant()); result.emplace_back(variant());
} }
} }
break; break;
} }
case vote_id_type::son: { case vote_id_type::son_bitcoin: {
auto itr = son_idx.find(id); auto itr = son_bictoin_idx.find(id);
if (itr != son_idx.end()) if (itr != son_bictoin_idx.end())
result.emplace_back(variant(*itr, 5));
else
result.emplace_back(variant());
break;
}
case vote_id_type::son_hive: {
auto itr = son_hive_idx.find(id);
if (itr != son_hive_idx.end())
result.emplace_back(variant(*itr, 5));
else
result.emplace_back(variant());
break;
}
case vote_id_type::son_ethereum: {
auto itr = son_ethereum_idx.find(id);
if (itr != son_ethereum_idx.end())
result.emplace_back(variant(*itr, 5)); result.emplace_back(variant(*itr, 5));
else else
result.emplace_back(variant()); result.emplace_back(variant());
break; break;
} }
case vote_id_type::VOTE_TYPE_COUNT: case vote_id_type::VOTE_TYPE_COUNT:
break; // supress unused enum value warnings break; // supress unused enum value warnings
default: default:
@ -2152,12 +2276,24 @@ vector<vote_id_type> database_api_impl::get_votes_ids(const string &account_name
votes_info database_api_impl::get_votes(const string &account_name_or_id) const { votes_info database_api_impl::get_votes(const string &account_name_or_id) const {
votes_info result; votes_info result;
const auto &votes_ids = get_votes_ids(account_name_or_id); const auto votes_ids = get_votes_ids(account_name_or_id);
const auto &committee_ids = get_votes_objects<committee_member_index, by_vote_id>(votes_ids); const auto committee_ids = get_votes_objects<committee_member_index, by_vote_id>(votes_ids);
const auto &witness_ids = get_votes_objects<witness_index, by_vote_id>(votes_ids); const auto witness_ids = get_votes_objects<witness_index, by_vote_id>(votes_ids);
const auto &for_worker_ids = get_votes_objects<worker_index, by_vote_for>(votes_ids); const auto for_worker_ids = get_votes_objects<worker_index, by_vote_for>(votes_ids);
const auto &against_worker_ids = get_votes_objects<worker_index, by_vote_against>(votes_ids); const auto against_worker_ids = get_votes_objects<worker_index, by_vote_against>(votes_ids);
const auto &son_ids = get_votes_objects<son_index, by_vote_id>(votes_ids, 5); const auto son_ids = [this, &votes_ids]() {
flat_map<sidechain_type, vector<variant>> son_ids;
const auto son_bitcoin_ids = get_votes_objects<son_index, by_vote_id_bitcoin>(votes_ids, 5);
if (!son_bitcoin_ids.empty())
son_ids[sidechain_type::bitcoin] = std::move(son_bitcoin_ids);
const auto son_hive_ids = get_votes_objects<son_index, by_vote_id_hive>(votes_ids, 5);
if (!son_hive_ids.empty())
son_ids[sidechain_type::hive] = std::move(son_hive_ids);
const auto son_ethereum_ids = get_votes_objects<son_index, by_vote_id_ethereum>(votes_ids, 5);
if (!son_ethereum_ids.empty())
son_ids[sidechain_type::ethereum] = std::move(son_ethereum_ids);
return son_ids;
}();
//! Fill votes info //! Fill votes info
if (!committee_ids.empty()) { if (!committee_ids.empty()) {
@ -2201,11 +2337,17 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const
} }
if (!son_ids.empty()) { if (!son_ids.empty()) {
vector<votes_info_object> votes_for_sons; flat_map<sidechain_type, vector<votes_info_object>> votes_for_sons;
votes_for_sons.reserve(son_ids.size()); for (const auto &son_sidechain_ids : son_ids) {
for (const auto &son : son_ids) { const auto &sidechain = son_sidechain_ids.first;
const auto &sidechain_ids = son_sidechain_ids.second;
votes_for_sons[sidechain].reserve(sidechain_ids.size());
for (const auto &son : sidechain_ids) {
const auto &son_obj = son.as<son_object>(6); const auto &son_obj = son.as<son_object>(6);
votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id}); if (son_obj.get_sidechain_vote_id(sidechain).valid()) {
votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
}
}
} }
result.votes_for_sons = std::move(votes_for_sons); result.votes_for_sons = std::move(votes_for_sons);
} }
@ -2379,12 +2521,16 @@ voters_info database_api_impl::get_voters(const string &account_name_or_id) cons
//! Info for son voters //! Info for son voters
if (son_object) { if (son_object) {
const auto &son_voters = get_voters_by_id(son_object->vote_id); flat_map<sidechain_type, voters_info_object> voters_for_son;
voters_info_object voters_for_son; for (const auto &vote_id : son_object->sidechain_vote_ids) {
voters_for_son.vote_id = son_object->vote_id; const auto &son_voters = get_voters_by_id(vote_id.second);
voters_for_son.voters.reserve(son_voters.size()); voters_info_object voters_for_sidechain_son;
voters_for_sidechain_son.vote_id = vote_id.second;
voters_for_sidechain_son.voters.reserve(son_voters.size());
for (const auto &voter : son_voters) { for (const auto &voter : son_voters) {
voters_for_son.voters.emplace_back(voter.get_id()); voters_for_sidechain_son.voters.emplace_back(voter.get_id());
}
voters_for_son[vote_id.first] = std::move(voters_for_sidechain_son);
} }
result.voters_for_son = std::move(voters_for_son); result.voters_for_son = std::move(voters_for_son);
} }
@ -2987,30 +3133,61 @@ nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_
return {}; return {};
} }
vector<nft_object> database_api::nft_get_all_tokens() const { vector<nft_object> database_api::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const {
return my->nft_get_all_tokens(); return my->nft_get_all_tokens(lower_id, limit);
} }
vector<nft_object> database_api_impl::nft_get_all_tokens() const { vector<nft_object> database_api_impl::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const {
FC_ASSERT(limit <= api_limit_nft_tokens,
"Number of queried nft tokens can not be greater than ${configured_limit}",
("configured_limit", api_limit_nft_tokens));
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>(); const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
vector<nft_object> result; vector<nft_object> result;
for (auto itr = idx_nft.begin(); itr != idx_nft.end(); ++itr) { result.reserve(limit);
result.push_back(*itr); auto itr = idx_nft.lower_bound(lower_id);
} while (limit-- && itr != idx_nft.end())
result.emplace_back(*itr++);
return result; return result;
} }
vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner) const { vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const {
return my->nft_get_tokens_by_owner(owner); return my->nft_get_tokens_by_owner(owner, lower_id, limit);
} }
vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner) const { vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const {
FC_ASSERT(limit <= api_limit_nft_tokens,
"Number of queried nft tokens can not be greater than ${configured_limit}",
("configured_limit", api_limit_nft_tokens));
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>(); const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
auto idx_nft_range = idx_nft.equal_range(owner); auto idx_nft_range = idx_nft.equal_range(owner);
vector<nft_object> result; vector<nft_object> result;
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) { result.reserve(limit);
result.push_back(*itr); auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_object &obj) {
return !(obj.id.instance() < lower_id.instance);
});
while (limit-- && itr != idx_nft_range.second)
result.emplace_back(*itr++);
return result;
} }
vector<nft_metadata_object> database_api::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const {
return my->nft_get_metadata_by_owner(owner, lower_id, limit);
}
vector<nft_metadata_object> database_api_impl::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const {
FC_ASSERT(limit <= api_limit_nft_tokens,
"Number of queried nft metadata objects can not be greater than ${configured_limit}",
("configured_limit", api_limit_nft_tokens));
const auto &idx_nft = _db.get_index_type<nft_metadata_index>().indices().get<by_owner>();
auto idx_nft_range = idx_nft.equal_range(owner);
vector<nft_metadata_object> result;
result.reserve(limit);
auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_metadata_object &obj) {
return !(obj.id.instance() < lower_id.instance);
});
while (limit-- && itr != idx_nft_range.second)
result.emplace_back(*itr++);
return result; return result;
} }

View file

@ -82,6 +82,15 @@ using namespace std;
class database_api_impl; class database_api_impl;
struct signed_block_with_info : public signed_block {
signed_block_with_info();
signed_block_with_info(const signed_block &block);
signed_block_with_info(const signed_block_with_info &block) = default;
block_id_type block_id;
public_key_type signing_key;
vector<transaction_id_type> transaction_ids;
};
struct order { struct order {
double price; double price;
double quote; double quote;
@ -202,6 +211,13 @@ public:
*/ */
optional<signed_block> get_block(uint32_t block_num) const; optional<signed_block> get_block(uint32_t block_num) const;
/**
* @brief Retrieve a full, signed block, with some extra info
* @param block_num Height of the block to be returned
* @return the referenced block, or null if no matching block was found
*/
optional<signed_block_with_info> get_block2(uint32_t block_num) const;
/** /**
* @brief Retrieve a list of signed blocks * @brief Retrieve a list of signed blocks
* @param block_num_from start * @param block_num_from start
@ -675,6 +691,32 @@ public:
*/ */
uint64_t get_son_count() const; uint64_t get_son_count() const;
/**
* @brief Get list of active sons
* @return List of active SONs
*/
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
/**
* @brief Get list of active sons
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
* @return List of active SONs
*/
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
/**
* @brief Get SON network status
* @return SON network status description for a given sidechain type
*/
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
/**
* @brief Get SON network status
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
* @return SON network status description for a given sidechain type
*/
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
///////////////////////// /////////////////////////
// SON Wallets // // SON Wallets //
///////////////////////// /////////////////////////
@ -1001,14 +1043,25 @@ public:
* @brief Returns list of all available NTF's * @brief Returns list of all available NTF's
* @return List of all available NFT's * @return List of all available NFT's
*/ */
vector<nft_object> nft_get_all_tokens() const; vector<nft_object> nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const;
/** /**
* @brief Returns NFT's owned by owner * @brief Returns NFT's owned by owner
* @param owner NFT owner * @param owner NFT owner
* @param lower_id ID of the first NFT to return
* @param limit Maximum number of results to return
* @return List of NFT owned by owner * @return List of NFT owned by owner
*/ */
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const; vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const;
/**
* @brief Returns NFT metadata owned by owner
* @param owner NFT owner
* @param lower_id ID of the first NFT metadata to return
* @param limit Maximum number of results to return
* @return List of NFT owned by owner
*/
vector<nft_metadata_object> nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const;
////////////////// //////////////////
// MARKET PLACE // // MARKET PLACE //
@ -1038,6 +1091,8 @@ extern template class fc::api<graphene::app::database_api>;
// clang-format off // clang-format off
FC_REFLECT_DERIVED(graphene::app::signed_block_with_info, (graphene::chain::signed_block), (block_id)(signing_key)(transaction_ids));
FC_REFLECT(graphene::app::order, (price)(quote)(base)); FC_REFLECT(graphene::app::order, (price)(quote)(base));
FC_REFLECT(graphene::app::order_book, (base)(quote)(bids)(asks)); FC_REFLECT(graphene::app::order_book, (base)(quote)(bids)(asks));
FC_REFLECT(graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume)); FC_REFLECT(graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume));
@ -1060,6 +1115,7 @@ FC_API(graphene::app::database_api,
(get_block_header) (get_block_header)
(get_block_header_batch) (get_block_header_batch)
(get_block) (get_block)
(get_block2)
(get_blocks) (get_blocks)
(get_transaction) (get_transaction)
(get_recent_transaction_by_id) (get_recent_transaction_by_id)
@ -1149,6 +1205,10 @@ FC_API(graphene::app::database_api,
(get_son_by_account) (get_son_by_account)
(lookup_son_accounts) (lookup_son_accounts)
(get_son_count) (get_son_count)
(get_active_sons)
(get_active_sons_by_sidechain)
(get_son_network_status)
(get_son_network_status_by_sidechain)
// SON wallets // SON wallets
(get_active_son_wallet) (get_active_son_wallet)
@ -1219,6 +1279,7 @@ FC_API(graphene::app::database_api,
(nft_token_of_owner_by_index) (nft_token_of_owner_by_index)
(nft_get_all_tokens) (nft_get_all_tokens)
(nft_get_tokens_by_owner) (nft_get_tokens_by_owner)
(nft_get_metadata_by_owner)
// Marketplace // Marketplace
(list_offers) (list_offers)

View file

@ -53,7 +53,54 @@ void verify_authority_accounts( const database& db, const authority& a )
} }
} }
void verify_account_votes( const database& db, const account_options& options ) // Overwrites the num_son values from the origin to the destination for those sidechains which are found in the origin.
// Keeps the values of num_son for the sidechains which are found in the destination, but not in the origin.
// Returns false if an error is detected.
bool merge_num_sons( flat_map<sidechain_type, uint16_t>& destination,
const flat_map<sidechain_type, uint16_t>& origin,
fc::optional<time_point_sec> head_block_time = {})
{
const auto active_sidechains = head_block_time.valid() ? active_sidechain_types(*head_block_time) : all_sidechain_types;
bool success = true;
for (const auto &ns : origin)
{
destination[ns.first] = ns.second;
if (active_sidechains.find(ns.first) == active_sidechains.end())
{
success = false;
}
}
return success;
}
flat_map<sidechain_type, uint16_t> count_SON_votes_per_sidechain( const flat_set<vote_id_type>& votes )
{
flat_map<sidechain_type, uint16_t> SON_votes_per_sidechain = account_options::ext::empty_num_son();
for (const auto &vote : votes)
{
switch (vote.type())
{
case vote_id_type::son_bitcoin:
SON_votes_per_sidechain[sidechain_type::bitcoin]++;
break;
case vote_id_type::son_hive:
SON_votes_per_sidechain[sidechain_type::hive]++;
break;
case vote_id_type::son_ethereum:
SON_votes_per_sidechain[sidechain_type::ethereum]++;
break;
default:
break;
}
}
return SON_votes_per_sidechain;
}
void verify_account_votes( const database& db, const account_options& options, fc::optional<account_object> account = {} )
{ {
// ensure account's votes satisfy requirements // ensure account's votes satisfy requirements
// NB only the part of vote checking that requires chain state is here, // NB only the part of vote checking that requires chain state is here,
@ -68,8 +115,42 @@ void verify_account_votes( const database& db, const account_options& options )
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
FC_ASSERT( options.num_son() <= chain_params.maximum_son_count(), FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" );
"Voted for more sons than currently allowed (${c})", ("c", chain_params.maximum_son_count()) );
flat_map<sidechain_type, uint16_t> merged_num_sons = account_options::ext::empty_num_son();
// Merge with existing account if exists
if ( account.valid() && account->options.extensions.value.num_son.valid())
{
merge_num_sons( merged_num_sons, *account->options.extensions.value.num_son, db.head_block_time() );
}
// Apply update operation on top
if ( options.extensions.value.num_son.valid() )
{
merge_num_sons( merged_num_sons, *options.extensions.value.num_son, db.head_block_time() );
}
for(const auto& num_sons : merged_num_sons)
{
FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count,
"Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) );
}
// Count the votes for SONs and confirm that the account did not vote for less SONs than num_son
flat_map<sidechain_type, uint16_t> SON_votes_per_sidechain = count_SON_votes_per_sidechain(options.votes);
for (const auto& number_of_votes : SON_votes_per_sidechain)
{
// Number of votes of account_options are also checked in account_options::do_evaluate,
// but there we are checking the value before merging num_sons, so the values should be checked again
const auto sidechain = number_of_votes.first;
FC_ASSERT( number_of_votes.second >= merged_num_sons[sidechain],
"Voted for less sons than specified in num_son (votes ${v} < num_son ${ns}) for sidechain ${s}",
("v", number_of_votes.second) ("ns", merged_num_sons[sidechain]) ("s", sidechain) );
}
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
uint32_t max_vote_id = gpo.next_available_vote_id; uint32_t max_vote_id = gpo.next_available_vote_id;
bool has_worker_votes = false; bool has_worker_votes = false;
@ -182,6 +263,13 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
obj.owner = o.owner; obj.owner = o.owner;
obj.active = o.active; obj.active = o.active;
obj.options = o.options; obj.options = o.options;
obj.options.extensions.value.num_son = account_options::ext::empty_num_son();
if ( o.options.extensions.value.num_son.valid() )
{
merge_num_sons( *obj.options.extensions.value.num_son, *o.options.extensions.value.num_son );
}
obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){ obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){
s.owner = obj.id; s.owner = obj.id;
s.name = obj.name; s.name = obj.name;
@ -281,7 +369,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
acnt = &o.account(d); acnt = &o.account(d);
if( o.new_options.valid() ) if( o.new_options.valid() )
verify_account_votes( d, *o.new_options ); verify_account_votes( d, *o.new_options, *acnt );
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
@ -320,7 +408,31 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
a.active = *o.active; a.active = *o.active;
a.top_n_control_flags = 0; a.top_n_control_flags = 0;
} }
if( o.new_options ) a.options = *o.new_options;
// New num_son structure initialized to 0
flat_map<sidechain_type, uint16_t> new_num_son = account_options::ext::empty_num_son();
// If num_son of existing object is valid, we should merge the existing data
if ( a.options.extensions.value.num_son.valid() )
{
merge_num_sons( new_num_son, *a.options.extensions.value.num_son );
}
// If num_son of the operation are valid, they should merge the existing data
if ( o.new_options )
{
const auto new_options = *o.new_options;
if ( new_options.extensions.value.num_son.valid() )
{
merge_num_sons( new_num_son, *new_options.extensions.value.num_son );
}
a.options = *o.new_options;
}
a.options.extensions.value.num_son = new_num_son;
if( o.extensions.value.owner_special_authority.valid() ) if( o.extensions.value.owner_special_authority.valid() )
{ {
a.owner_special_authority = *(o.extensions.value.owner_special_authority); a.owner_special_authority = *(o.extensions.value.owner_special_authority);

View file

@ -33,45 +33,163 @@ namespace graphene { namespace chain {
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o ) void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
{ try { { try {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME )
{
const auto& atype = o.amount.asset_id(d);
FC_ASSERT( atype.allow_confidential() );
FC_ASSERT( !atype.is_transfer_restricted() );
FC_ASSERT( !(atype.options.flags & white_list) );
for( const auto& out : o.outputs )
{
for( const auto& a : out.owner.account_auths )
a.first(d); // verify all accounts exist and are valid
}
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o ) void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
{ try { { try {
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
db().adjust_balance(o.from, -o.amount);
const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify(add, [&](asset_dynamic_data_object &obj) {
obj.confidential_supply += o.amount.amount;
FC_ASSERT(obj.confidential_supply >= 0);
});
for (const auto &out : o.outputs) {
db().create<blinded_balance_object>([&](blinded_balance_object &obj) {
obj.asset_id = o.amount.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_to_blind_evaluator::pay_fee() void transfer_to_blind_evaluator::pay_fee()
{ {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
if (d.head_block_time() >= HARDFORK_563_TIME)
pay_fba_fee(fba_accumulator_id_transfer_to_blind);
else
generic_evaluator::pay_fee();
}
} }
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o ) void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
{ try { { try {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
o.fee.asset_id(d); // verify fee is a legit asset
const auto &bbi = d.get_index_type<blinded_balance_index>();
const auto &cidx = bbi.indices().get<by_commitment>();
for (const auto &in : o.inputs) {
auto itr = cidx.find(in.commitment);
FC_ASSERT(itr != cidx.end());
FC_ASSERT(itr->asset_id == o.fee.asset_id);
FC_ASSERT(itr->owner == in.owner);
}
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o ) void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
{ try { { try {
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
db().adjust_balance(o.fee_payer(), o.fee);
db().adjust_balance(o.to, o.amount);
const auto &bbi = db().get_index_type<blinded_balance_index>();
const auto &cidx = bbi.indices().get<by_commitment>();
for (const auto &in : o.inputs) {
auto itr = cidx.find(in.commitment);
FC_ASSERT(itr != cidx.end());
db().remove(*itr);
}
const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
db().modify(add, [&](asset_dynamic_data_object &obj) {
obj.confidential_supply -= o.amount.amount + o.fee.amount;
FC_ASSERT(obj.confidential_supply >= 0);
});
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void transfer_from_blind_evaluator::pay_fee() void transfer_from_blind_evaluator::pay_fee()
{ {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
if (d.head_block_time() >= HARDFORK_563_TIME)
pay_fba_fee(fba_accumulator_id_transfer_from_blind);
else
generic_evaluator::pay_fee();
}
} }
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o ) void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
{ try { { try {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
o.fee.asset_id(d); // verify fee is a legit asset
const auto &bbi = d.get_index_type<blinded_balance_index>();
const auto &cidx = bbi.indices().get<by_commitment>();
for (const auto &out : o.outputs) {
for (const auto &a : out.owner.account_auths)
a.first(d); // verify all accounts exist and are valid
}
for (const auto &in : o.inputs) {
auto itr = cidx.find(in.commitment);
GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment));
FC_ASSERT(itr->asset_id == o.fee.asset_id);
FC_ASSERT(itr->owner == in.owner);
}
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o ) void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
{ try { { try {
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
db().adjust_balance(o.fee_payer(), o.fee); // deposit the fee to the temp account
const auto &bbi = db().get_index_type<blinded_balance_index>();
const auto &cidx = bbi.indices().get<by_commitment>();
for (const auto &in : o.inputs) {
auto itr = cidx.find(in.commitment);
GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment));
db().remove(*itr);
}
for (const auto &out : o.outputs) {
db().create<blinded_balance_object>([&](blinded_balance_object &obj) {
obj.asset_id = o.fee.asset_id;
obj.owner = out.owner;
obj.commitment = out.commitment;
});
}
const auto &add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
db().modify(add, [&](asset_dynamic_data_object &obj) {
obj.confidential_supply -= o.fee.amount;
FC_ASSERT(obj.confidential_supply >= 0);
});
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void blind_transfer_evaluator::pay_fee() void blind_transfer_evaluator::pay_fee()
{ {
const auto& d = db();
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
if (d.head_block_time() >= HARDFORK_563_TIME)
pay_fba_fee(fba_accumulator_id_blind_transfer);
else
generic_evaluator::pay_fee();
}
} }
} } // graphene::chain } } // graphene::chain

View file

@ -40,8 +40,10 @@
#include <graphene/chain/exceptions.hpp> #include <graphene/chain/exceptions.hpp>
#include <graphene/chain/evaluator.hpp> #include <graphene/chain/evaluator.hpp>
#include <graphene/chain/witness_schedule_object.hpp> #include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/db/object_database.hpp>
#include <fc/crypto/digest.hpp> #include <fc/crypto/digest.hpp>
#include <boost/filesystem.hpp>
namespace { namespace {
@ -160,11 +162,14 @@ void database::check_transaction_for_duplicated_operations(const signed_transact
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() ); existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
}); });
{
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
for (auto &pending_transaction : _pending_tx) for (auto &pending_transaction : _pending_tx)
{ {
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction); auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end()); existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
} }
}
auto proposed_operations_digests = gather_proposed_operations_digests(trx); auto proposed_operations_digests = gather_proposed_operations_digests(trx);
for (auto& digest: proposed_operations_digests) for (auto& digest: proposed_operations_digests)
@ -185,7 +190,12 @@ bool database::push_block(const signed_block& new_block, uint32_t skip)
bool result; bool result;
detail::with_skip_flags( *this, skip, [&]() detail::with_skip_flags( *this, skip, [&]()
{ {
detail::without_pending_transactions( *this, std::move(_pending_tx), std::vector<processed_transaction> pending_tx = [this] {
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
return std::move(_pending_tx);
}();
detail::without_pending_transactions( *this, std::move(pending_tx),
[&]() [&]()
{ {
result = _push_block(new_block); result = _push_block(new_block);
@ -196,6 +206,9 @@ bool database::push_block(const signed_block& new_block, uint32_t skip)
bool database::_push_block(const signed_block& new_block) bool database::_push_block(const signed_block& new_block)
{ try { { try {
boost::filesystem::space_info si = boost::filesystem::space(get_data_dir());
FC_ASSERT((si.available) > 104857600, "Rejecting block due to low disk space"); // 104857600 bytes = 100 MB
uint32_t skip = get_node_properties().skip_flags; uint32_t skip = get_node_properties().skip_flags;
const auto now = fc::time_point::now().sec_since_epoch(); const auto now = fc::time_point::now().sec_since_epoch();
@ -382,17 +395,26 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
{ {
// If this is the first transaction pushed after applying a block, start a new undo session. // If this is the first transaction pushed after applying a block, start a new undo session.
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
if( !_pending_tx_session.valid() ) {
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
if (!_pending_tx_session.valid()) {
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
_pending_tx_session = _undo_db.start_undo_session(); _pending_tx_session = _undo_db.start_undo_session();
}
}
// Create a temporary undo session as a child of _pending_tx_session. // Create a temporary undo session as a child of _pending_tx_session.
// The temporary session will be discarded by the destructor if // The temporary session will be discarded by the destructor if
// _apply_transaction fails. If we make it to merge(), we // _apply_transaction fails. If we make it to merge(), we
// apply the changes. // apply the changes.
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
auto temp_session = _undo_db.start_undo_session(); auto temp_session = _undo_db.start_undo_session();
auto processed_trx = _apply_transaction(trx); auto processed_trx = _apply_transaction(trx);
{
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
_pending_tx.push_back(processed_trx); _pending_tx.push_back(processed_trx);
}
// notify_changed_objects(); // notify_changed_objects();
// The transaction applied successfully. Merge its changes into the pending block session. // The transaction applied successfully. Merge its changes into the pending block session.
@ -405,6 +427,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
processed_transaction database::validate_transaction( const signed_transaction& trx ) processed_transaction database::validate_transaction( const signed_transaction& trx )
{ {
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
auto session = _undo_db.start_undo_session(); auto session = _undo_db.start_undo_session();
return _apply_transaction( trx ); return _apply_transaction( trx );
} }
@ -504,24 +527,26 @@ signed_block database::_generate_block(
// the value of the "when" variable is known, which means we need to // the value of the "when" variable is known, which means we need to
// re-apply pending transactions in this method. // re-apply pending transactions in this method.
// //
{
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
_pending_tx_session.reset(); _pending_tx_session.reset();
_pending_tx_session = _undo_db.start_undo_session(); _pending_tx_session = _undo_db.start_undo_session();
}
uint64_t postponed_tx_count = 0; uint64_t postponed_tx_count = 0;
// pop pending state (reset to head block state) // pop pending state (reset to head block state)
for( const processed_transaction& tx : _pending_tx )
{ {
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
for (const processed_transaction &tx : _pending_tx) {
size_t new_total_size = total_block_size + fc::raw::pack_size(tx); size_t new_total_size = total_block_size + fc::raw::pack_size(tx);
// postpone transaction if it would make block too big // postpone transaction if it would make block too big
if( new_total_size >= maximum_block_size ) if (new_total_size >= maximum_block_size) {
{
postponed_tx_count++; postponed_tx_count++;
continue; continue;
} }
try try {
{
auto temp_session = _undo_db.start_undo_session(); auto temp_session = _undo_db.start_undo_session();
processed_transaction ptx = _apply_transaction(tx); processed_transaction ptx = _apply_transaction(tx);
temp_session.merge(); temp_session.merge();
@ -531,20 +556,23 @@ signed_block database::_generate_block(
// their size) // their size)
total_block_size += fc::raw::pack_size(ptx); total_block_size += fc::raw::pack_size(ptx);
pending_block.transactions.push_back(ptx); pending_block.transactions.push_back(ptx);
} } catch (const fc::exception &e) {
catch ( const fc::exception& e )
{
// Do nothing, transaction will not be re-applied // Do nothing, transaction will not be re-applied
wlog("Transaction was not processed while generating block due to ${e}", ("e", e)); wlog("Transaction was not processed while generating block due to ${e}", ("e", e));
wlog("The transaction was ${t}", ("t", tx)); wlog("The transaction was ${t}", ("t", tx));
} }
} }
}
if( postponed_tx_count > 0 ) if( postponed_tx_count > 0 )
{ {
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) ); wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
} }
{
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
_pending_tx_session.reset(); _pending_tx_session.reset();
}
// We have temporarily broken the invariant that // We have temporarily broken the invariant that
// _pending_tx_session is the result of applying _pending_tx, as // _pending_tx_session is the result of applying _pending_tx, as
@ -592,7 +620,11 @@ signed_block database::_generate_block(
*/ */
void database::pop_block() void database::pop_block()
{ try { { try {
{
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
_pending_tx_session.reset(); _pending_tx_session.reset();
}
auto head_id = head_block_id(); auto head_id = head_block_id();
optional<signed_block> head_block = fetch_block_by_id( head_id ); optional<signed_block> head_block = fetch_block_by_id( head_id );
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
@ -606,6 +638,8 @@ void database::pop_block()
void database::clear_pending() void database::clear_pending()
{ try { { try {
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() ); assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
_pending_tx.clear(); _pending_tx.clear();
_pending_tx_session.reset(); _pending_tx_session.reset();
@ -705,8 +739,11 @@ void database::_apply_block( const signed_block& next_block )
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) {
update_witness_schedule(next_block); update_witness_schedule(next_block);
if(global_props.active_sons.size() > 0) {
update_son_schedule(next_block); for(const auto& active_sons : global_props.active_sons) {
if(!active_sons.second.empty()) {
update_son_schedule(active_sons.first, next_block);
}
} }
} }
@ -739,11 +776,15 @@ void database::_apply_block( const signed_block& next_block )
// TODO: figure out if we could collapse this function into // TODO: figure out if we could collapse this function into
// update_global_dynamic_data() as perhaps these methods only need // update_global_dynamic_data() as perhaps these methods only need
// to be called for header validation? // to be called for header validation?
update_maintenance_flag( maint_needed ); update_maintenance_flag( maint_needed );
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) {
update_witness_schedule(); update_witness_schedule();
if(global_props.active_sons.size() > 0) {
update_son_schedule(); for(const auto& active_sidechain_type : active_sidechain_types(dynamic_global_props.time)) {
if(global_props.active_sons.at(active_sidechain_type).size() > 0) {
update_son_schedule(active_sidechain_type);
}
} }
} }

View file

@ -222,18 +222,33 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
for( auto& son : son_idx ) for( auto& son : son_idx )
{ {
if(son.status == son_status::in_maintenance) bool need_to_be_deregistered = true;
for(const auto& status : son.statuses)
{
const auto& sidechain = status.first;
if(status.second != son_status::in_maintenance)
need_to_be_deregistered = false;
if(need_to_be_deregistered)
{ {
auto stats = son.statistics(*this); auto stats = son.statistics(*this);
// TODO : We need to add a function that returns if we can deregister SON // TODO : We need to add a function that returns if we can deregister SON
// i.e. with introduction of PW code, we have to make a decision if the SON // i.e. with introduction of PW code, we have to make a decision if the SON
// is needed for release of funds from the PW // is needed for release of funds from the PW
if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())) if(stats.last_active_timestamp.contains(sidechain)) {
if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
need_to_be_deregistered = false;
}
}
}
}
if(need_to_be_deregistered)
{ {
ret.insert(son.id); ret.insert(son.id);
} }
} }
}
return ret; return ret;
} }
@ -289,28 +304,50 @@ bool database::is_son_dereg_valid( son_id_type son_id )
return false; return false;
} }
return (son->status == son_status::in_maintenance && bool status_son_dereg_valid = true;
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); for (const auto &active_sidechain_type : active_sidechain_types(head_block_time())) {
if(son->statuses.at(active_sidechain_type) != son_status::in_maintenance)
status_son_dereg_valid = false;
if(status_son_dereg_valid)
{
if(son->statistics(*this).last_active_timestamp.contains(active_sidechain_type)) {
if (head_block_time() - son->statistics(*this).last_active_timestamp.at(active_sidechain_type) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
status_son_dereg_valid = false;
}
}
}
} }
bool database::is_son_active( son_id_type son_id ) return status_son_dereg_valid;
}
bool database::is_son_active( sidechain_type type, son_id_type son_id )
{ {
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >(); const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
auto son = son_idx.find( son_id ); auto son = son_idx.find( son_id );
if(son == son_idx.end()) if(son == son_idx.end()) {
{
return false; return false;
} }
const global_property_object& gpo = get_global_properties(); const global_property_object& gpo = get_global_properties();
if(!gpo.active_sons.contains(type)) {
return false;
}
const auto& gpo_as = gpo.active_sons.at(type);
vector<son_id_type> active_son_ids; vector<son_id_type> active_son_ids;
active_son_ids.reserve(gpo.active_sons.size()); active_son_ids.reserve(gpo_as.size());
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), std::transform(gpo_as.cbegin(), gpo_as.cend(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info& swi) { [](const son_sidechain_info& swi) {
return swi.son_id; return swi.son_id;
}); });
if(active_son_ids.empty()) {
return false;
}
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
return (it_son != active_son_ids.end()); return (it_son != active_son_ids.end());
} }
@ -349,23 +386,14 @@ vector<uint64_t> database::get_random_numbers(uint64_t minimum, uint64_t maximum
bool database::is_asset_creation_allowed(const string &symbol) bool database::is_asset_creation_allowed(const string &symbol)
{ {
time_point_sec now = head_block_time();
std::unordered_set<std::string> post_son_hf_symbols = {"ETH", "USDT", "BNB", "ADA", "DOGE", "XRP", "USDC", "DOT", "UNI", "BUSD", "BCH", "LTC", "SOL", "LINK", "MATIC", "THETA",
"WBTC", "XLM", "ICP", "DAI", "VET", "ETC", "TRX", "FIL", "XMR", "EGR", "EOS", "SHIB", "AAVE", "CRO", "ALGO", "AMP", "BTCB",
"BSV", "KLAY", "CAKE", "FTT", "LEO", "XTZ", "TFUEL", "MIOTA", "LUNA", "NEO", "ATOM", "MKR", "FEI", "WBNB", "UST", "AVAX",
"STEEM", "HIVE", "HBD", "SBD", "BTS"};
if (symbol == "BTC") if (symbol == "BTC")
{ {
if (now < HARDFORK_SON_TIME) if (head_block_time() < HARDFORK_SON_TIME)
return false;
}
if (post_son_hf_symbols.find(symbol) != post_son_hf_symbols.end())
{
if (now >= HARDFORK_SON_TIME)
return false; return false;
} }
return true; return true;
} }
} } }
}

View file

@ -365,7 +365,7 @@ void database::initialize_hardforks()
_hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME); _hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME);
_hardfork_times.emplace_back(HARDFORK_SON_TIME); _hardfork_times.emplace_back(HARDFORK_SON_TIME);
_hardfork_times.emplace_back(HARDFORK_SON2_TIME); _hardfork_times.emplace_back(HARDFORK_SON2_TIME);
_hardfork_times.emplace_back(HARDFORK_SON3_TIME); _hardfork_times.emplace_back(HARDFORK_SON_FOR_ETHEREUM_TIME);
_hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME); _hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME);
std::sort(_hardfork_times.begin(), _hardfork_times.end()); std::sort(_hardfork_times.begin(), _hardfork_times.end());
@ -374,6 +374,8 @@ void database::initialize_hardforks()
void database::initialize_indexes() void database::initialize_indexes()
{ {
reset_indexes(); reset_indexes();
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
_undo_db.set_max_size(GRAPHENE_MIN_UNDO_HISTORY); _undo_db.set_max_size(GRAPHENE_MIN_UNDO_HISTORY);
//Protocol object indexes //Protocol object indexes
@ -474,7 +476,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(), FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),
"initial_active_witnesses is larger than the number of candidate witnesses."); "initial_active_witnesses is larger than the number of candidate witnesses.");
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
_undo_db.disable(); _undo_db.disable();
struct auth_inhibitor { struct auth_inhibitor {
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
{ db.node_properties().skip_flags |= skip_authority_check; } { db.node_properties().skip_flags |= skip_authority_check; }
@ -1100,8 +1104,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() );
// Initialize witness schedule // Initialize witness schedule
#ifndef NDEBUG #ifndef NDEBUG
const son_schedule_object& sso = const son_schedule_object& ssobitcoin =
#endif #endif
create<son_schedule_object>([&](son_schedule_object& _sso) create<son_schedule_object>([&](son_schedule_object& _sso)
{ {
@ -1110,24 +1115,64 @@ void database::init_genesis(const genesis_state_type& genesis_state)
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_witnesses = get_global_properties().active_witnesses; auto init_bitcoin_sons = get_global_properties().active_sons.at(sidechain_type::bitcoin);
_sso.scheduler = son_scheduler(); _sso.scheduler = son_scheduler();
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); _sso.scheduler._min_token_count = std::max(int(init_bitcoin_sons.size()) / 2, 1);
_sso.last_scheduling_block = 0; _sso.last_scheduling_block = 0;
_sso.recent_slots_filled = fc::uint128::max_value(); _sso.recent_slots_filled = fc::uint128::max_value();
}); });
assert( sso.id == son_schedule_id_type() ); assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) );
#ifndef NDEBUG
const son_schedule_object& ssoethereum =
#endif
create<son_schedule_object>([&](son_schedule_object& _sso)
{
// for scheduled
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_ethereum_sons = get_global_properties().active_sons.at(sidechain_type::ethereum);
_sso.scheduler = son_scheduler();
_sso.scheduler._min_token_count = std::max(int(init_ethereum_sons.size()) / 2, 1);
_sso.last_scheduling_block = 0;
_sso.recent_slots_filled = fc::uint128::max_value();
});
assert( ssoethereum.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::ethereum)) );
#ifndef NDEBUG
const son_schedule_object& ssohive =
#endif
create<son_schedule_object>([&](son_schedule_object& _sso)
{
// for scheduled
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_hive_sons = get_global_properties().active_sons.at(sidechain_type::hive);
_sso.scheduler = son_scheduler();
_sso.scheduler._min_token_count = std::max(int(init_hive_sons.size()) / 2, 1);
_sso.last_scheduling_block = 0;
_sso.recent_slots_filled = fc::uint128::max_value();
});
assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) );
// Enable fees // Enable fees
modify(get_global_properties(), [&genesis_state](global_property_object& p) { modify(get_global_properties(), [&genesis_state](global_property_object& p) {
p.parameters.current_fees = genesis_state.initial_parameters.current_fees; p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
}); });
// Create FBA counters // Create FBA counters
create<fba_accumulator_object>([&]( fba_accumulator_object& acc ) create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{ {

View file

@ -78,25 +78,34 @@ vector<std::reference_wrapper<const typename Index::object_type>> database::sort
} }
template<> template<>
vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<son_index>(size_t count) const vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<son_index>(sidechain_type sidechain, size_t count) const
{ {
const auto& all_sons = get_index_type<son_index>().indices().get< by_id >(); const auto& all_sons = get_index_type<son_index>().indices().get< by_id >();
std::vector<std::reference_wrapper<const son_object>> refs; std::vector<std::reference_wrapper<const son_object>> refs;
for( auto& son : all_sons ) for( auto& son : all_sons )
{ {
if(son.has_valid_config(head_block_time()) && son.status != son_status::deregistered) if(son.has_valid_config(head_block_time(), sidechain) && son.statuses.at(sidechain) != son_status::deregistered)
{ {
refs.push_back(std::cref(son)); refs.push_back(std::cref(son));
} }
} }
count = std::min(count, refs.size()); count = std::min(count, refs.size());
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
[this](const son_object& a, const son_object& b)->bool { [this, sidechain](const son_object& a, const son_object& b)->bool {
share_type oa_vote = _vote_tally_buffer[a.vote_id]; FC_ASSERT(sidechain == sidechain_type::bitcoin ||
share_type ob_vote = _vote_tally_buffer[b.vote_id]; sidechain == sidechain_type::ethereum ||
sidechain == sidechain_type::hive,
"Unexpected sidechain type");
FC_ASSERT(a.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", a));
FC_ASSERT(b.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", b));
const share_type oa_vote = _vote_tally_buffer.size() > *a.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*a.get_sidechain_vote_id(sidechain)] : 0;
const share_type ob_vote = _vote_tally_buffer.size() > *b.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*b.get_sidechain_vote_id(sidechain)] : 0;
if( oa_vote != ob_vote ) if( oa_vote != ob_vote )
return oa_vote > ob_vote; return oa_vote > ob_vote;
return a.vote_id < b.vote_id;
return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain);
}); });
refs.resize(count, refs.front()); refs.resize(count, refs.front());
@ -161,6 +170,7 @@ struct worker_pay_visitor
worker.pay_worker(pay, db); worker.pay_worker(pay, db);
} }
}; };
void database::update_worker_votes() void database::update_worker_votes()
{ {
auto& idx = get_index_type<worker_index>(); auto& idx = get_index_type<worker_index>();
@ -176,82 +186,95 @@ void database::update_worker_votes()
} }
} }
void database::pay_sons() void database::hotfix_2024()
{ {
time_point_sec now = head_block_time(); if (head_block_time() >= HARDFORK_HOTFIX_2024_TIME)
{
if (get_chain_id().str() == "6b6b5f0ce7a36d323768e534f3edb41c6d6332a541a95725b98e28d140850134")
{
const auto& vb_idx = get_index_type<vesting_balance_index>().indices().get<by_id>();
auto vbo = vb_idx.find(vesting_balance_id_type(388));
if (vbo != vb_idx.end())
{
if (vbo->owner == account_id_type(14786))
{
modify(*vbo, [&]( vesting_balance_object& _vbo)
{
_vbo.owner = account_id_type(0);
});
}
}
}
}
}
void database::pay_sons_before_hf_ethereum()
{
const auto now = head_block_time();
const dynamic_global_property_object& dpo = get_dynamic_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties();
// Current requirement is that we have to pay every 24 hours, so the following check // Current requirement is that we have to pay every 24 hours, so the following check
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
assert( _son_count_histogram_buffer.size() > 0 );
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2;
/// accounts that vote for 0 or 1 son do not get to express an opinion on
/// the number of sons to have (they abstain and are non-voting accounts)
share_type stake_tally = 0;
size_t son_count = 0;
if( stake_target > 0 )
{ {
while( (son_count < _son_count_histogram_buffer.size() - 1) const sidechain_type st = sidechain_type::bitcoin;
&& (stake_tally <= stake_target) ) const auto sons = sort_votable_objects<son_index>(st, get_global_properties().parameters.maximum_son_count());
{
stake_tally += _son_count_histogram_buffer[++son_count];
}
}
const vector<std::reference_wrapper<const son_object>> sons = [this, &son_count]{
if(head_block_time() >= HARDFORK_SON3_TIME)
return sort_votable_objects<son_index>(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count));
else
return sort_votable_objects<son_index>(get_global_properties().parameters.maximum_son_count());
}();
// After SON2 HF // After SON2 HF
uint64_t total_votes = 0; uint64_t total_votes = 0;
for( const son_object& son : sons ) for( const son_object& son : sons )
{ {
total_votes += _vote_tally_buffer[son.vote_id]; FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son));
total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)];
} }
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
return weight; return weight;
}; };
// Before SON2 HF // Before SON2 HF
auto get_weight_before_son2_hf = []( uint64_t son_votes ) { auto get_weight_before_son2_hf = []( uint64_t son_votes ) {
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0);
uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
return weight; return weight;
}; };
uint64_t weighted_total_txs_signed = 0; uint64_t weighted_total_txs_signed = 0;
share_type son_budget = dpo.son_budget; const share_type son_budget = dpo.son_budget;
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf](const object& o) { get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &st](const object& o) {
const son_statistics_object& s = static_cast<const son_statistics_object&>(o); const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
const auto& idx = get_index_type<son_index>().indices().get<by_id>(); const auto& idx = get_index_type<son_index>().indices().get<by_id>();
auto son_obj = idx.find( s.owner ); const auto son_obj = idx.find( s.owner );
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); uint16_t son_weight = 0;
if( now < HARDFORK_SON2_TIME ) { FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); if( now >= HARDFORK_SON2_TIME ) {
son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
uint64_t txs_signed = 0; else {
for (const auto &ts : s.txs_signed) { son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
txs_signed = txs_signed + ts.second;
} }
weighted_total_txs_signed += (txs_signed * son_weight); const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0;
const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0;
weighted_total_txs_signed += ((txs_signed_bitcoin + txs_signed_hive) * son_weight);
}); });
// Now pay off each SON proportional to the number of transactions signed. // Now pay off each SON proportional to the number of transactions signed.
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now](const object& o) { get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &st](const object& o) {
const son_statistics_object& s = static_cast<const son_statistics_object&>(o); const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
uint64_t txs_signed = 0; const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0;
for (const auto &ts : s.txs_signed) { const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0;
txs_signed = txs_signed + ts.second;
}
if(txs_signed > 0){ if(txs_signed_bitcoin > 0 || txs_signed_hive > 0) {
const auto& idx = get_index_type<son_index>().indices().get<by_id>(); const auto& idx = get_index_type<son_index>().indices().get<by_id>();
auto son_obj = idx.find( s.owner ); auto son_obj = idx.find( s.owner );
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); uint16_t son_weight = 0;
if( now < HARDFORK_SON2_TIME ) { FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); if( now >= HARDFORK_SON2_TIME ) {
son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; else {
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
}
const share_type pay = ((txs_signed_bitcoin + txs_signed_hive) * son_weight * son_budget.value)/weighted_total_txs_signed;
modify( *son_obj, [&]( son_object& _son_obj) modify( *son_obj, [&]( son_object& _son_obj)
{ {
_son_obj.pay_son_fee(pay, *this); _son_obj.pay_son_fee(pay, *this);
@ -264,12 +287,14 @@ void database::pay_sons()
//Reset the tx counter in each son statistics object //Reset the tx counter in each son statistics object
modify( s, [&]( son_statistics_object& _s) modify( s, [&]( son_statistics_object& _s)
{ {
for (const auto &ts : s.txs_signed) { if(_s.txs_signed.contains(sidechain_type::bitcoin))
_s.txs_signed.at(ts.first) = 0; _s.txs_signed.at(sidechain_type::bitcoin) = 0;
} if(_s.txs_signed.contains(sidechain_type::hive))
_s.txs_signed.at(sidechain_type::hive) = 0;
}); });
} }
}); });
//Note the last son pay out time //Note the last son pay out time
modify( dpo, [&]( dynamic_global_property_object& _dpo ) modify( dpo, [&]( dynamic_global_property_object& _dpo )
{ {
@ -278,122 +303,228 @@ void database::pay_sons()
} }
} }
void database::update_son_metrics(const vector<son_info>& curr_active_sons) void database::pay_sons_after_hf_ethereum()
{ {
const time_point_sec now = head_block_time();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
// Current requirement is that we have to pay every 24 hours, so the following check
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
{
flat_map<sidechain_type, int8_t> bits_to_drop;
for(const auto& active_sidechain_type : active_sidechain_types(now))
{
assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 );
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
/// accounts that vote for 0 or 1 son do not get to express an opinion on
/// the number of sons to have (they abstain and are non-voting accounts)
share_type stake_tally = 0;
size_t son_count = 0;
if( stake_target > 0 )
{
while( (son_count < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1)
&& (stake_tally <= stake_target) )
{
stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[++son_count];
}
}
const auto sons = sort_votable_objects<son_index>(active_sidechain_type, (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)));
// After SON2 HF
uint64_t total_votes = 0;
for( const son_object& son : sons )
{
FC_ASSERT(son.get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", son));
total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(active_sidechain_type)];
}
bits_to_drop[active_sidechain_type] = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
}
auto get_weight = [&bits_to_drop]( sidechain_type sidechain, uint64_t son_votes ) {
const uint16_t weight = std::max((son_votes >> bits_to_drop.at(sidechain)), uint64_t(1) );
return weight;
};
// Calculate weighted_total_txs_signed
uint64_t weighted_total_txs_signed = 0;
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now](const object& o) {
for(const auto& active_sidechain_type : active_sidechain_types(now)) {
const son_statistics_object &s = static_cast<const son_statistics_object &>(o);
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
const auto son_obj = idx.find(s.owner);
FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj));
const uint16_t son_weight = get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]);
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
weighted_total_txs_signed += (txs_signed * son_weight);
}
});
// Now pay off each SON proportional to the number of transactions signed
const share_type son_budget = dpo.son_budget;
get_index_type<son_stats_index>().inspect_all_objects([this, &now, &get_weight, &weighted_total_txs_signed, &dpo, &son_budget](const object& o) {
for(const auto& active_sidechain_type : active_sidechain_types(now)) {
const son_statistics_object &s = static_cast<const son_statistics_object &>(o);
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
if (txs_signed > 0) {
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
auto son_obj = idx.find(s.owner);
uint16_t son_weight = 0;
FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj));
son_weight += get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]);
const share_type pay = (txs_signed * son_weight * son_budget.value) / weighted_total_txs_signed;
modify(*son_obj, [&](son_object &_son_obj) {
_son_obj.pay_son_fee(pay, *this);
});
// Remove the amount paid out to SON from global SON Budget
modify(dpo, [&](dynamic_global_property_object &_dpo) {
_dpo.son_budget -= pay;
});
// Reset the tx counter in each son statistics object
modify(s, [&](son_statistics_object &_s) {
if (_s.txs_signed.contains(active_sidechain_type))
_s.txs_signed.at(active_sidechain_type) = 0;
});
}
}
});
//Note the last son pay out time
modify( dpo, [&]( dynamic_global_property_object& _dpo )
{
_dpo.last_son_payout_time = now;
});
}
}
void database::update_son_metrics(const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons)
{
for(const auto& curr_active_sidechain_sons : curr_active_sons) {
const auto& sidechain = curr_active_sidechain_sons.first;
const auto& _curr_active_sidechain_sons = curr_active_sidechain_sons.second;
vector<son_id_type> current_sons; vector<son_id_type> current_sons;
current_sons.reserve(curr_active_sons.size()); current_sons.reserve(_curr_active_sidechain_sons.size());
std::transform(curr_active_sons.begin(), curr_active_sons.end(), std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(),
std::inserter(current_sons, current_sons.end()), std::inserter(current_sons, current_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
const auto &son_idx = get_index_type<son_index>().indices().get<by_id>(); const auto &son_idx = get_index_type<son_index>().indices().get<by_id>();
for( auto& son : son_idx ) for (auto &son : son_idx) {
{
auto &stats = son.statistics(*this); auto &stats = son.statistics(*this);
bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end());
modify( stats, [&]( son_statistics_object& _stats ) modify(stats, [&](son_statistics_object &_stats) {
{
if (is_active_son) { if (is_active_son) {
_stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval; _stats.total_voted_time[sidechain] = _stats.total_voted_time[sidechain] + get_global_properties().parameters.maintenance_interval;
}
_stats.total_downtime += _stats.current_interval_downtime;
_stats.current_interval_downtime = 0;
for (const auto &str : _stats.sidechain_txs_reported) {
_stats.sidechain_txs_reported.at(str.first) = 0;
} }
if(!_stats.current_interval_downtime.contains(sidechain))
_stats.current_interval_downtime[sidechain] = 0;
_stats.total_downtime[sidechain] += _stats.current_interval_downtime.at(sidechain);
_stats.current_interval_downtime[sidechain] = 0;
_stats.sidechain_txs_reported[sidechain] = 0;
}); });
} }
} }
}
void database::update_son_statuses(const vector<son_info>& curr_active_sons, const vector<son_info>& new_active_sons) void database::update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons )
{ {
for(const auto& new_active_sidechain_sons : new_active_sons) {
const auto& sidechain = new_active_sidechain_sons.first;
vector<son_id_type> current_sons, new_sons; vector<son_id_type> current_sons, new_sons;
vector<son_id_type> sons_to_remove, sons_to_add; vector<son_id_type> sons_to_remove, sons_to_add;
const auto &idx = get_index_type<son_index>().indices().get<by_id>(); const auto &idx = get_index_type<son_index>().indices().get<by_id>();
current_sons.reserve(curr_active_sons.size()); if(curr_active_sons.contains(sidechain)) {
std::transform(curr_active_sons.begin(), curr_active_sons.end(), current_sons.reserve(curr_active_sons.at(sidechain).size());
std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(),
std::inserter(current_sons, current_sons.end()), std::inserter(current_sons, current_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
}
new_sons.reserve(new_active_sons.size()); new_sons.reserve(new_active_sons.at(sidechain).size());
std::transform(new_active_sons.begin(), new_active_sons.end(), std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(),
std::inserter(new_sons, new_sons.end()), std::inserter(new_sons, new_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
// find all cur_active_sons members that is not in new_active_sons // find all cur_active_sons members that is not in new_active_sons
for_each(current_sons.begin(), current_sons.end(), for_each(current_sons.begin(), current_sons.end(),
[&sons_to_remove, &new_sons](const son_id_type& si) [&sons_to_remove, &new_sons](const son_id_type &si) {
{
if (std::find(new_sons.begin(), new_sons.end(), si) == if (std::find(new_sons.begin(), new_sons.end(), si) ==
new_sons.end()) new_sons.end()) {
{
sons_to_remove.push_back(si); sons_to_remove.push_back(si);
} }
} });
);
for( const auto& sid : sons_to_remove ) for (const auto &sid : sons_to_remove) {
{
auto son = idx.find(sid); auto son = idx.find(sid);
if (son == idx.end()) // SON is deleted already if (son == idx.end()) // SON is deleted already
continue; continue;
// keep maintenance status for nodes becoming inactive // keep maintenance status for nodes becoming inactive
if(son->status == son_status::active) if (son->statuses.at(sidechain) == son_status::active) {
{
modify(*son, [&](son_object &obj) { modify(*son, [&](son_object &obj) {
obj.status = son_status::inactive; obj.statuses.at(sidechain) = son_status::inactive;
}); });
} }
} }
// find all new_active_sons members that is not in cur_active_sons // find all new_active_sons members that is not in cur_active_sons
for_each(new_sons.begin(), new_sons.end(), for_each(new_sons.begin(), new_sons.end(),
[&sons_to_add, &current_sons](const son_id_type& si) [&sons_to_add, &current_sons](const son_id_type &si) {
{
if (std::find(current_sons.begin(), current_sons.end(), si) == if (std::find(current_sons.begin(), current_sons.end(), si) ==
current_sons.end()) current_sons.end()) {
{
sons_to_add.push_back(si); sons_to_add.push_back(si);
} }
} });
);
for( const auto& sid : sons_to_add ) for (const auto &sid : sons_to_add) {
{
auto son = idx.find(sid); auto son = idx.find(sid);
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); FC_ASSERT(son != idx.end(), "Invalid SON in active list, id = ${sonid}.", ("sonid", sid));
// keep maintenance status for new nodes // keep maintenance status for new nodes
if(son->status == son_status::inactive) if (son->statuses.at(sidechain) == son_status::inactive) {
{
modify(*son, [&](son_object &obj) { modify(*son, [&](son_object &obj) {
obj.status = son_status::active; obj.statuses.at(sidechain) = son_status::active;
}); });
} }
} }
ilog("New SONS"); ilog("New SONS for sidechain = ${sidechain}", ("sidechain", sidechain));
for (size_t i = 0; i < new_sons.size(); i++) { for (size_t i = 0; i < new_sons.size(); i++) {
auto son = idx.find(new_sons[i]); auto son = idx.find(new_sons[i]);
if (son == idx.end()) // SON is deleted already if (son == idx.end()) // SON is deleted already
continue; continue;
ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) ); ilog("${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->statuses.at(sidechain))("sv", son->total_votes));
}
} }
if( sons_to_remove.size() > 0 ) //! Remove inactive sons (when all sidechain inactive)
{ vector<son_id_type> sons_to_remove;
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
for(const auto& son : idx) {
bool inactive_son = true;
for(const auto& status : son.statuses) {
if (status.second != son_status::inactive)
inactive_son = false;
}
if (inactive_son)
sons_to_remove.emplace_back(son.id);
}
if (sons_to_remove.size() > 0) {
remove_inactive_son_proposals(sons_to_remove); remove_inactive_son_proposals(sons_to_remove);
} }
} }
void database::update_son_wallet(const vector<son_info>& new_active_sons) void database::update_son_wallet(const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons)
{ {
bool should_recreate_pw = true; bool should_recreate_pw = true;
@ -406,8 +537,16 @@ void database::update_son_wallet(const vector<son_info>& new_active_sons)
bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size());
if (wallet_son_sets_equal) { if (wallet_son_sets_equal) {
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) {
wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); const auto& sidechain = cur_wallet_sidechain_sons.first;
const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second;
wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_active_sons.at(sidechain).size());
if (wallet_son_sets_equal) {
for (size_t i = 0; i < _cur_wallet_sidechain_sons.size(); i++) {
wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i));
}
}
} }
} }
@ -420,14 +559,24 @@ void database::update_son_wallet(const vector<son_info>& new_active_sons)
} }
} }
should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count); bool should_recreate_pw_sidechain = false;
for(const auto& new_active_sidechain_sons : new_active_sons) {
if(new_active_sidechain_sons.second.size() >= get_chain_properties().immutable_parameters.min_son_count)
should_recreate_pw_sidechain = true;
}
should_recreate_pw = should_recreate_pw && should_recreate_pw_sidechain;
if (should_recreate_pw) { if (should_recreate_pw) {
// Create new son_wallet_object, to initiate wallet recreation // Create new son_wallet_object, to initiate wallet recreation
create<son_wallet_object>( [&]( son_wallet_object& obj ) { create<son_wallet_object>( [&]( son_wallet_object& obj ) {
obj.valid_from = head_block_time(); obj.valid_from = head_block_time();
obj.expires = time_point_sec::maximum(); obj.expires = time_point_sec::maximum();
obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); for(const auto& new_active_sidechain_sons : new_active_sons){
const auto& sidechain = new_active_sidechain_sons.first;
const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second;
obj.sons[sidechain].insert(obj.sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend());
}
}); });
} }
} }
@ -684,47 +833,70 @@ void database::update_active_sons()
} }
assert( _son_count_histogram_buffer.size() > 0 ); assert( _son_count_histogram_buffer.size() > 0 );
share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; #ifndef NDEBUG
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
assert( son_count_histogram_buffer.second.size() > 0 );
}
#endif
const auto supported_active_sidechain_types = active_sidechain_types(head_block_time());
flat_map<sidechain_type, size_t> son_count;
for(const auto& active_sidechain_type : supported_active_sidechain_types)
{
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
/// accounts that vote for 0 or 1 son do not get to express an opinion on /// accounts that vote for 0 or 1 son do not get to express an opinion on
/// the number of sons to have (they abstain and are non-voting accounts) /// the number of sons to have (they abstain and are non-voting accounts)
share_type stake_tally = 0; share_type stake_tally = 0;
son_count[active_sidechain_type] = 0;
size_t son_count = 0;
if( stake_target > 0 ) if( stake_target > 0 )
{ {
while( (son_count < _son_count_histogram_buffer.size() - 1) while( (son_count.at(active_sidechain_type) < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1)
&& (stake_tally <= stake_target) ) && (stake_tally <= stake_target) )
{ {
stake_tally += _son_count_histogram_buffer[++son_count]; stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[ ++son_count[active_sidechain_type] ];
}
} }
} }
const global_property_object& gpo = get_global_properties(); const global_property_object& gpo = get_global_properties();
const vector<std::reference_wrapper<const son_object>> sons = [this, &son_count]{ const chain_property_object& cpo = get_chain_properties();
if(head_block_time() >= HARDFORK_SON3_TIME)
return sort_votable_objects<son_index>(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count));
else
return sort_votable_objects<son_index>(get_global_properties().parameters.maximum_son_count());
}();
const auto& all_sons = get_index_type<son_index>().indices(); const auto& all_sons = get_index_type<son_index>().indices();
flat_map<sidechain_type, vector<std::reference_wrapper<const son_object> > > sons;
for(const auto& active_sidechain_type : supported_active_sidechain_types)
{
if(head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type,
(std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count)));
}
else {
sons[active_sidechain_type] = sort_votable_objects<son_index>(sidechain_type::bitcoin, get_global_properties().parameters.maximum_son_count());
}
}
auto& local_vote_buffer_ref = _vote_tally_buffer;
for( const son_object& son : all_sons ) for( const son_object& son : all_sons )
{ {
if(son.status == son_status::request_maintenance) for(const auto& status: son.statuses)
{
const auto& sidechain = status.first;
if(status.second == son_status::in_maintenance)
{ {
auto &stats = son.statistics(*this); auto &stats = son.statistics(*this);
modify(stats, [&](son_statistics_object &_s) { modify(stats, [&](son_statistics_object &_s) {
_s.last_down_timestamp = head_block_time(); _s.last_down_timestamp[sidechain] = head_block_time();
}); });
} }
modify( son, [local_vote_buffer_ref]( son_object& obj ){ }
obj.total_votes = local_vote_buffer_ref[obj.vote_id];
if(obj.status == son_status::request_maintenance) modify( son, [this]( son_object& obj ){
obj.status = son_status::in_maintenance; for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){
obj.total_votes[sidechain_vote_id.first] = _vote_tally_buffer.size() > sidechain_vote_id.second ? _vote_tally_buffer[sidechain_vote_id.second] : 0;
}
for(auto& status: obj.statuses)
{
if (status.second == son_status::request_maintenance)
status.second = son_status::in_maintenance;
}
}); });
} }
@ -733,21 +905,24 @@ void database::update_active_sons()
{ {
modify( get(gpo.parameters.son_account()), [&]( account_object& a ) modify( get(gpo.parameters.son_account()), [&]( account_object& a )
{ {
set<account_id_type> account_ids;
for(const auto& sidechain_sons : sons)
{
for( const son_object& son : sidechain_sons.second )
{
account_ids.emplace(son.son_account);
}
}
if( head_block_time() < HARDFORK_533_TIME ) if( head_block_time() < HARDFORK_533_TIME )
{ {
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0; a.active.weight_threshold = 0;
a.active.account_auths.clear(); a.active.account_auths.clear();
for( const son_object& son : sons ) for( const auto& account_id : account_ids )
{
weights.emplace(son.son_account, uint64_t(1));
}
for( const auto& weight : weights )
{ {
// Ensure that everyone has at least one vote. Zero weights aren't allowed. // Ensure that everyone has at least one vote. Zero weights aren't allowed.
a.active.account_auths[weight.first] += 1; a.active.account_auths[account_id] += 1;
a.active.weight_threshold += 1; a.active.weight_threshold += 1;
} }
@ -758,8 +933,10 @@ void database::update_active_sons()
else else
{ {
vote_counter vc; vote_counter vc;
for( const son_object& son : sons ) for( const auto& account_id : account_ids )
vc.add( son.son_account, UINT64_C(1) ); {
vc.add(account_id, UINT64_C(1));
}
vc.finish_2_3( a.active ); vc.finish_2_3( a.active );
} }
} ); } );
@ -767,22 +944,37 @@ void database::update_active_sons()
// Compare current and to-be lists of active sons // Compare current and to-be lists of active sons
auto cur_active_sons = gpo.active_sons; const auto cur_active_sons = gpo.active_sons;
vector<son_info> new_active_sons; flat_map<sidechain_type, vector<son_sidechain_info> > new_active_sons;
const auto &acc = get(gpo.parameters.son_account()); const auto &acc = get(gpo.parameters.son_account());
for( const son_object& son : sons ) { for( const auto& sidechain_sons : sons ){
son_info swi; const auto& sidechain = sidechain_sons.first;
const auto& sons_array = sidechain_sons.second;
new_active_sons[sidechain].reserve(sons_array.size());
for( const son_object& son : sons_array ) {
son_sidechain_info swi;
swi.son_id = son.id; swi.son_id = son.id;
swi.weight = acc.active.account_auths.at(son.son_account); swi.weight = acc.active.account_auths.at(son.son_account);
swi.signing_key = son.signing_key; swi.signing_key = son.signing_key;
swi.sidechain_public_keys = son.sidechain_public_keys; if (son.sidechain_public_keys.find(sidechain) != son.sidechain_public_keys.end())
new_active_sons.push_back(swi); swi.public_key = son.sidechain_public_keys.at(sidechain);
new_active_sons[sidechain].push_back(swi);
}
} }
bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size()); bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size());
if (son_sets_equal) { if (son_sets_equal) {
for( size_t i = 0; i < cur_active_sons.size(); i++ ) { for( const auto& cur_active_sidechain_sons : cur_active_sons ){
son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i); const auto& sidechain = cur_active_sidechain_sons.first;
const auto& _cur_active_sidechain_sons = cur_active_sidechain_sons.second;
son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.size() == new_active_sons.at(sidechain).size());
if (son_sets_equal) {
for (size_t i = 0; i < _cur_active_sidechain_sons.size(); i++) {
son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i));
}
}
} }
} }
@ -797,28 +989,38 @@ void database::update_active_sons()
modify(gpo, [&]( global_property_object& gp ){ modify(gpo, [&]( global_property_object& gp ){
gp.active_sons.clear(); gp.active_sons.clear();
gp.active_sons.reserve(new_active_sons.size()); gp.active_sons.reserve(new_active_sons.size());
gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end()); for( const auto& new_active_sidechain_sons : new_active_sons ) {
const auto& sidechain = new_active_sidechain_sons.first;
const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second;
gp.active_sons[sidechain].reserve(_new_active_sidechain_sons.size());
gp.active_sons[sidechain].insert(gp.active_sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend());
}
}); });
const son_schedule_object& sso = son_schedule_id_type()(*this); for(const auto& active_sidechain_type : supported_active_sidechain_types)
modify(sso, [&](son_schedule_object& _sso) {
const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this);
modify(sidechain_sso, [&](son_schedule_object& _sso)
{ {
flat_set<son_id_type> active_sons; flat_set<son_id_type> active_sons;
active_sons.reserve(gpo.active_sons.size()); active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size());
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
std::inserter(active_sons, active_sons.end()), std::inserter(active_sons, active_sons.end()),
[](const son_info& swi) { [](const son_sidechain_info& swi) {
return swi.son_id; return swi.son_id;
}); });
_sso.scheduler.update(active_sons); _sso.scheduler.update(active_sons);
// similar to witness, produce schedule for sons // similar to witness, produce schedule for sons
if(cur_active_sons.size() == 0 && new_active_sons.size() > 0) if( ((cur_active_sons.contains(active_sidechain_type) && cur_active_sons.at(active_sidechain_type).size() == 0) ||
!cur_active_sons.contains(active_sidechain_type)) && new_active_sons.at(active_sidechain_type).size() > 0 )
{ {
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
for( size_t i=0; i<new_active_sons.size(); ++i ) for( size_t i=0; i<new_active_sons.at(active_sidechain_type).size(); ++i )
_sso.scheduler.produce_schedule(rng); _sso.scheduler.produce_schedule(rng);
} }
}); });
}
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
@ -1358,17 +1560,19 @@ void rolling_period_start(database& db)
{ {
if(db.head_block_time() >= HARDFORK_GPOS_TIME) if(db.head_block_time() >= HARDFORK_GPOS_TIME)
{ {
auto gpo = db.get_global_properties(); const auto gpo = db.get_global_properties();
auto period_start = db.get_global_properties().parameters.gpos_period_start(); auto period_start = db.get_global_properties().parameters.gpos_period_start();
auto vesting_period = db.get_global_properties().parameters.gpos_period(); const auto vesting_period = db.get_global_properties().parameters.gpos_period();
auto now = db.head_block_time(); const auto now = db.head_block_time();
if(now.sec_since_epoch() >= (period_start + vesting_period)) while(now.sec_since_epoch() >= (period_start + vesting_period))
{ {
// roll // roll
db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) { db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
p.parameters.extensions.value.gpos_period_start = period_start + vesting_period; p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
}); });
period_start = db.get_global_properties().parameters.gpos_period_start();
} }
} }
} }
@ -2079,26 +2283,69 @@ void database::perform_son_tasks()
gpo.pending_parameters->extensions.value.hive_asset = hive_asset.get_id(); gpo.pending_parameters->extensions.value.hive_asset = hive_asset.get_id();
}); });
} }
// Pay the SONs // Pay the SONs
if (head_block_time() >= HARDFORK_SON_TIME) if (head_block_time() >= HARDFORK_SON_TIME)
{ {
// Before making a budget we should pay out SONs // Before making a budget we should pay out SONs
// This function should check if its time to pay sons // This function should check if its time to pay sons
// and modify the global son funds accordingly, whatever is left is passed on to next budget // and modify the global son funds accordingly, whatever is left is passed on to next budget
pay_sons(); if(head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME)
pay_sons_before_hf_ethereum();
else
pay_sons_after_hf_ethereum();
}
// Split vote_ids
if (head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
// Get SON 1.33.0 and check if it has HIVE vote_id
const son_id_type sid = son_id_type(0);
const auto p_son = find(sid);
if(p_son != nullptr) {
if (p_son->sidechain_vote_ids.find(sidechain_type::hive) == p_son->sidechain_vote_ids.end()) {
// Add vote_ids for HIVE and ETHEREUM to all existing SONs
const auto &all_sons = get_index_type<son_index>().indices().get<by_id>();
for (const son_object &son : all_sons) {
const auto existing_vote_id_bitcoin = son.get_bitcoin_vote_id();
vote_id_type new_vote_id_hive;
vote_id_type new_vote_id_eth;
modify(gpo, [&new_vote_id_hive, &new_vote_id_eth](global_property_object &p) {
new_vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
new_vote_id_eth = get_next_vote_id(p, vote_id_type::son_ethereum);
});
modify(son, [new_vote_id_hive, new_vote_id_eth](son_object &obj) {
obj.sidechain_vote_ids[sidechain_type::hive] = new_vote_id_hive;
obj.sidechain_vote_ids[sidechain_type::ethereum] = new_vote_id_eth;
});
// Duplicate all votes from bitcoin to hive
const auto &all_accounts = get_index_type<account_index>().indices().get<by_id>();
for (const auto &account : all_accounts) {
if (existing_vote_id_bitcoin.valid() && account.options.votes.count(*existing_vote_id_bitcoin) != 0) {
modify(account, [new_vote_id_hive](account_object &a) {
a.options.votes.insert(new_vote_id_hive);
});
}
}
}
}
}
} }
} }
void update_son_params(database& db) void update_son_params(database& db)
{ {
if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) ) if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME) )
{ {
const auto& gpo = db.get_global_properties(); const auto& gpo = db.get_global_properties();
db.modify( gpo, []( global_property_object& gpo ) { db.modify( gpo, []( global_property_object& gpo ) {
gpo.parameters.extensions.value.maximum_son_count = 7; gpo.parameters.extensions.value.maximum_son_count = 7;
}); });
} }
else
if( (db.head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) )
{ {
const auto& gpo = db.get_global_properties(); const auto& gpo = db.get_global_properties();
db.modify( gpo, []( global_property_object& gpo ) { db.modify( gpo, []( global_property_object& gpo ) {
@ -2131,7 +2378,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
d._vote_tally_buffer.resize(props.next_available_vote_id); d._vote_tally_buffer.resize(props.next_available_vote_id);
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1); d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1); d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count() / 2 + 1); for( auto& son_count_histogram_buffer : d._son_count_histogram_buffer ){
son_count_histogram_buffer.second.resize(props.parameters.maximum_son_count() / 2 + 1);
}
d._total_voting_stake = 0; d._total_voting_stake = 0;
auto balance_type = vesting_balance_type::normal; auto balance_type = vesting_balance_type::normal;
@ -2231,17 +2480,24 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// same rationale as for witnesses // same rationale as for witnesses
d._committee_count_histogram_buffer[offset] += voting_stake; d._committee_count_histogram_buffer[offset] += voting_stake;
} }
if( opinion_account.options.num_son() <= props.parameters.maximum_son_count() )
if ( opinion_account.options.extensions.value.num_son.valid() )
{ {
uint16_t offset = std::min(size_t(opinion_account.options.num_son()/2), for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) {
d._son_count_histogram_buffer.size() - 1); const auto sidechain = num_sidechain_son.first;
const auto& num_son = num_sidechain_son.second;
if (num_son <= props.parameters.maximum_son_count()) {
uint16_t offset = std::min(size_t(num_son / 2),
d._son_count_histogram_buffer.at(sidechain).size() - 1);
// votes for a number greater than maximum_son_count // votes for a number greater than maximum_son_count
// are turned into votes for maximum_son_count. // are turned into votes for maximum_son_count.
// //
// in particular, this takes care of the case where a // in particular, this takes care of the case where a
// member was voting for a high number, then the // member was voting for a high number, then the
// parameter was lowered. // parameter was lowered.
d._son_count_histogram_buffer[offset] += voting_stake; d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake;
}
}
} }
d._total_voting_stake += voting_stake; d._total_voting_stake += voting_stake;
@ -2256,10 +2512,20 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
private: private:
vector<uint64_t>& target; vector<uint64_t>& target;
}; };
struct clear_canary_map {
clear_canary_map(flat_map<sidechain_type, vector<uint64_t> >& target): target(target){}
~clear_canary_map() {
for(auto& sidechain_target : target){
sidechain_target.second.clear();
}
}
private:
flat_map<sidechain_type, vector<uint64_t> >& target;
};
clear_canary a(_witness_count_histogram_buffer), clear_canary a(_witness_count_histogram_buffer),
b(_committee_count_histogram_buffer), b(_committee_count_histogram_buffer),
d(_son_count_histogram_buffer),
c(_vote_tally_buffer); c(_vote_tally_buffer);
clear_canary_map d{_son_count_histogram_buffer};
perform_son_tasks(); perform_son_tasks();
update_top_n_authorities(*this); update_top_n_authorities(*this);
@ -2267,6 +2533,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
update_active_committee_members(); update_active_committee_members();
update_active_sons(); update_active_sons();
update_worker_votes(); update_worker_votes();
hotfix_2024();
const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); const dynamic_global_property_object& dgpo = get_dynamic_global_properties();

View file

@ -74,21 +74,32 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
return wid; return wid;
} }
son_id_type database::get_scheduled_son( uint32_t slot_num )const unsigned_int database::get_son_schedule_id( sidechain_type type )const
{
static const map<sidechain_type, unsigned_int> schedule_map = {
{ sidechain_type::bitcoin, 0 },
{ sidechain_type::ethereum, 1 },
{ sidechain_type::hive, 2 }
};
return schedule_map.at(type);
}
son_id_type database::get_scheduled_son( sidechain_type type, uint32_t slot_num )const
{ {
son_id_type sid; son_id_type sid;
const global_property_object& gpo = get_global_properties(); const global_property_object& gpo = get_global_properties();
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
{ {
const dynamic_global_property_object& dpo = get_dynamic_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties();
const son_schedule_object& sso = son_schedule_id_type()(*this); const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this);
uint64_t current_aslot = dpo.current_aslot + slot_num; uint64_t current_aslot = dpo.current_aslot + slot_num;
return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ];
} }
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
slot_num != 0 ) slot_num != 0 )
{ {
const son_schedule_object& sso = son_schedule_id_type()(*this); const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this);
// ask the near scheduler who goes in the given slot // ask the near scheduler who goes in the given slot
bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid);
if(! slot_is_near) if(! slot_is_near)
@ -189,22 +200,25 @@ void database::update_witness_schedule()
} }
} }
void database::update_son_schedule() void database::update_son_schedule(sidechain_type type)
{ {
const son_schedule_object& sso = son_schedule_id_type()(*this);
const global_property_object& gpo = get_global_properties(); const global_property_object& gpo = get_global_properties();
if( head_block_num() % gpo.active_sons.size() == 0 ) const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type)));
if( gpo.active_sons.at(type).size() != 0 &&
head_block_num() % gpo.active_sons.at(type).size() == 0)
{ {
modify( sso, [&]( son_schedule_object& _sso ) modify( sidechain_sso, [&]( son_schedule_object& _sso )
{ {
_sso.current_shuffled_sons.clear(); _sso.current_shuffled_sons.clear();
_sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); _sso.current_shuffled_sons.reserve( gpo.active_sons.at(type).size() );
for( const son_info& w : gpo.active_sons ) for ( const auto &w : gpo.active_sons.at(type) ) {
_sso.current_shuffled_sons.push_back(w.son_id); _sso.current_shuffled_sons.push_back(w.son_id);
}
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i) for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i)
{ {
/// High performance random generator /// High performance random generator
@ -304,13 +318,15 @@ void database::update_witness_schedule(const signed_block& next_block)
idump( ( double(total_time/1000000.0)/calls) ); idump( ( double(total_time/1000000.0)/calls) );
} }
void database::update_son_schedule(const signed_block& next_block) void database::update_son_schedule(sidechain_type type, const signed_block& next_block)
{ {
auto start = fc::time_point::now(); auto start = fc::time_point::now();
const global_property_object& gpo = get_global_properties(); #ifndef NDEBUG
const son_schedule_object& sso = get(son_schedule_id_type()); const son_schedule_object& sso = get(son_schedule_id_type());
uint32_t schedule_needs_filled = gpo.active_sons.size(); #endif
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); const global_property_object& gpo = get_global_properties();
const uint32_t schedule_needs_filled = gpo.active_sons.at(type).size();
const uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
// We shouldn't be able to generate _pending_block with timestamp // We shouldn't be able to generate _pending_block with timestamp
// in the past, and incoming blocks from the network with timestamp // in the past, and incoming blocks from the network with timestamp
@ -319,22 +335,22 @@ void database::update_son_schedule(const signed_block& next_block)
assert( schedule_slot > 0 ); assert( schedule_slot > 0 );
son_id_type first_son;
bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son );
son_id_type son;
const dynamic_global_property_object& dpo = get_dynamic_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties();
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
modify(sso, [&](son_schedule_object& _sso) const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type)));
son_id_type first_son;
bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son );
son_id_type son_id;
modify(sidechain_sso, [&](son_schedule_object& _sso)
{ {
_sso.slots_since_genesis += schedule_slot; _sso.slots_since_genesis += schedule_slot;
witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis);
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(type).size()) / 2, 1);
if( slot_is_near ) if( slot_is_near )
{ {
@ -351,7 +367,7 @@ void database::update_son_schedule(const signed_block& next_block)
{ {
_sso.scheduler.reset_schedule( first_son ); _sso.scheduler.reset_schedule( first_son );
} }
while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) while( !_sso.scheduler.get_slot(schedule_needs_filled, son_id) )
{ {
if( _sso.scheduler.produce_schedule(rng) & emit_turn ) if( _sso.scheduler.produce_schedule(rng) & emit_turn )
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
@ -361,6 +377,7 @@ void database::update_son_schedule(const signed_block& next_block)
(_sso.recent_slots_filled << 1) (_sso.recent_slots_filled << 1)
+ 1) << (schedule_slot - 1); + 1) << (schedule_slot - 1);
}); });
auto end = fc::time_point::now(); auto end = fc::time_point::now();
static uint64_t total_time = 0; static uint64_t total_time = 0;
static uint64_t calls = 0; static uint64_t calls = 0;

View file

@ -0,0 +1,7 @@
#ifndef HARDFORK_HOTFIX_2024_TIME
#ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_HOTFIX_2024_TIME (fc::time_point_sec::from_iso_string("2023-12-20T00:00:00"))
#else
#define HARDFORK_HOTFIX_2024_TIME (fc::time_point_sec::from_iso_string("2023-12-20T00:00:00"))
#endif
#endif

View file

@ -0,0 +1,7 @@
#ifndef HARDFORK_SIDECHAIN_DELETE_TIME
#ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00"))
#else
#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00"))
#endif
#endif

View file

@ -1,7 +0,0 @@
#ifndef HARDFORK_SON3_TIME
#ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00"))
#else
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00"))
#endif
#endif

View file

@ -1,7 +1,7 @@
#ifndef HARDFORK_SON_FOR_ETHEREUM_TIME #ifndef HARDFORK_SON_FOR_ETHEREUM_TIME
#ifdef BUILD_PEERPLAYS_TESTNET #ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) #define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-17T12:00:00"))
#else #else
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) #define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-10-24T12:00:00"))
#endif #endif
#endif #endif

View file

@ -158,7 +158,7 @@
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
#define GRAPHENE_CURRENT_DB_VERSION "PPY2.4" #define GRAPHENE_CURRENT_DB_VERSION "PPY2.5"
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)

View file

@ -245,7 +245,16 @@ namespace graphene { namespace chain {
witness_id_type get_scheduled_witness(uint32_t slot_num)const; witness_id_type get_scheduled_witness(uint32_t slot_num)const;
/** /**
* @brief Get the son scheduled for block production in a slot. * @brief Get son schedule id for the given sidechain_type.
*
* type sidechain_type we getting schedule.
*
* returns Id of the schedule object.
*/
unsigned_int get_son_schedule_id(sidechain_type type)const;
/**
* @brief Get the bitcoin or hive son scheduled for block production in a slot.
* *
* slot_num always corresponds to a time in the future. * slot_num always corresponds to a time in the future.
* *
@ -258,7 +267,7 @@ namespace graphene { namespace chain {
* *
* Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS
*/ */
son_id_type get_scheduled_son(uint32_t slot_num)const; son_id_type get_scheduled_son(sidechain_type type, uint32_t slot_num)const;
/** /**
* Get the time at which the given slot occurs. * Get the time at which the given slot occurs.
@ -283,8 +292,8 @@ namespace graphene { namespace chain {
vector<witness_id_type> get_near_witness_schedule()const; vector<witness_id_type> get_near_witness_schedule()const;
void update_witness_schedule(); void update_witness_schedule();
void update_witness_schedule(const signed_block& next_block); void update_witness_schedule(const signed_block& next_block);
void update_son_schedule(); void update_son_schedule(sidechain_type type);
void update_son_schedule(const signed_block& next_block); void update_son_schedule(sidechain_type type, const signed_block& next_block);
void check_lottery_end_by_participants( asset_id_type asset_id ); void check_lottery_end_by_participants( asset_id_type asset_id );
void check_ending_lotteries(); void check_ending_lotteries();
@ -313,7 +322,7 @@ namespace graphene { namespace chain {
fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son );
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
bool is_son_dereg_valid( son_id_type son_id ); bool is_son_dereg_valid( son_id_type son_id );
bool is_son_active( son_id_type son_id ); bool is_son_active( sidechain_type type, son_id_type son_id );
bool is_asset_creation_allowed(const string& symbol); bool is_asset_creation_allowed(const string& symbol);
time_point_sec head_block_time()const; time_point_sec head_block_time()const;
@ -511,12 +520,16 @@ namespace graphene { namespace chain {
void notify_changed_objects(); void notify_changed_objects();
private: private:
std::mutex _pending_tx_session_mutex;
optional<undo_database::session> _pending_tx_session; optional<undo_database::session> _pending_tx_session;
vector< unique_ptr<op_evaluator> > _operation_evaluators; vector< unique_ptr<op_evaluator> > _operation_evaluators;
template<class Index> template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const; vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const;
template<class Index>
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(sidechain_type sidechain, size_t count)const;
//////////////////// db_block.cpp //////////////////// //////////////////// db_block.cpp ////////////////////
public: public:
@ -566,19 +579,22 @@ namespace graphene { namespace chain {
void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const; void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;
void process_budget(); void process_budget();
void pay_workers( share_type& budget ); void pay_workers( share_type& budget );
void pay_sons(); void pay_sons_before_hf_ethereum();
void pay_sons_after_hf_ethereum();
void perform_son_tasks(); void perform_son_tasks();
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses(); void update_active_witnesses();
void update_active_committee_members(); void update_active_committee_members();
void update_son_metrics( const vector<son_info>& curr_active_sons ); void update_son_metrics( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons );
void update_active_sons(); void update_active_sons();
void remove_son_proposal( const proposal_object& proposal ); void remove_son_proposal( const proposal_object& proposal );
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove ); void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove ); void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons ); void update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
void update_son_wallet( const vector<son_info>& new_active_sons ); const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
void update_son_wallet( const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
void update_worker_votes(); void update_worker_votes();
void hotfix_2024();
public: public:
double calculate_vesting_factor(const account_object& stake_account); double calculate_vesting_factor(const account_object& stake_account);
@ -589,6 +605,7 @@ namespace graphene { namespace chain {
///@} ///@}
///@} ///@}
std::mutex _pending_tx_mutex;
vector< processed_transaction > _pending_tx; vector< processed_transaction > _pending_tx;
fork_database _fork_db; fork_database _fork_db;
@ -619,7 +636,13 @@ namespace graphene { namespace chain {
vector<uint64_t> _vote_tally_buffer; vector<uint64_t> _vote_tally_buffer;
vector<uint64_t> _witness_count_histogram_buffer; vector<uint64_t> _witness_count_histogram_buffer;
vector<uint64_t> _committee_count_histogram_buffer; vector<uint64_t> _committee_count_histogram_buffer;
vector<uint64_t> _son_count_histogram_buffer; flat_map<sidechain_type, vector<uint64_t> > _son_count_histogram_buffer = []{
flat_map<sidechain_type, vector<uint64_t> > son_count_histogram_buffer;
for(const auto& active_sidechain_type : all_sidechain_types){
son_count_histogram_buffer[active_sidechain_type] = vector<uint64_t>{};
}
return son_count_histogram_buffer;
}();
uint64_t _total_voting_stake; uint64_t _total_voting_stake;
flat_map<uint32_t,block_id_type> _checkpoints; flat_map<uint32_t,block_id_type> _checkpoints;

View file

@ -182,6 +182,9 @@ namespace graphene { namespace chain {
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer ); GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" ) GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" );
/* /*
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" ) FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" ) FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )

View file

@ -27,7 +27,7 @@
#include <graphene/chain/protocol/chain_parameters.hpp> #include <graphene/chain/protocol/chain_parameters.hpp>
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/db/object.hpp> #include <graphene/db/object.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -52,7 +52,15 @@ namespace graphene { namespace chain {
uint32_t next_available_vote_id = 0; uint32_t next_available_vote_id = 0;
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
vector<son_info> active_sons; // updated once per maintenance interval flat_map<sidechain_type, vector<son_sidechain_info> > active_sons = []() // updated once per maintenance interval
{
flat_map<sidechain_type, vector<son_sidechain_info> > active_sons;
for(const auto& active_sidechain_type : all_sidechain_types)
{
active_sons[active_sidechain_type] = vector<son_sidechain_info>();
}
return active_sons;
}();
// n.b. witness scheduling is done by witness_schedule object // n.b. witness scheduling is done by witness_schedule object
}; };

View file

@ -130,6 +130,9 @@ namespace graphene { namespace chain {
std::greater< uint32_t >, std::greater< uint32_t >,
std::greater< object_id_type > std::greater< object_id_type >
> >
>,
ordered_non_unique< tag<by_owner>,
member<nft_metadata_object, account_id_type, &nft_metadata_object::owner>
> >
> >
>; >;

View file

@ -28,6 +28,7 @@
#include <graphene/chain/protocol/special_authority.hpp> #include <graphene/chain/protocol/special_authority.hpp>
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/vote.hpp> #include <graphene/chain/protocol/vote.hpp>
#include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -39,9 +40,24 @@ namespace graphene { namespace chain {
{ {
struct ext struct ext
{ {
optional< uint16_t > num_son = 0; /// The number of active son members this account votes the blockchain should appoint
/// Must not exceed the actual number of son members voted for in @ref votes
optional< flat_map<sidechain_type, uint16_t> > num_son;
/// Returns and empty num_son map with all sidechains
static flat_map<sidechain_type, uint16_t> empty_num_son()
{
flat_map<sidechain_type, uint16_t> num_son;
for(const auto& active_sidechain_type : all_sidechain_types)
{
num_son[active_sidechain_type] = 0;
}
return num_son;
}
}; };
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or /// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// multiple keys in it. /// multiple keys in it.
@ -57,9 +73,6 @@ namespace graphene { namespace chain {
/// The number of active committee members this account votes the blockchain should appoint /// The number of active committee members this account votes the blockchain should appoint
/// Must not exceed the actual number of committee members voted for in @ref votes /// Must not exceed the actual number of committee members voted for in @ref votes
uint16_t num_committee = 0; uint16_t num_committee = 0;
/// The number of active son members this account votes the blockchain should appoint
/// Must not exceed the actual number of son members voted for in @ref votes
uint16_t num_son() const { return extensions.value.num_son.valid() ? *extensions.value.num_son : 0; }
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
/// account's balance of core asset. /// account's balance of core asset.
flat_set<vote_id_type> votes; flat_set<vote_id_type> votes;

View file

@ -158,9 +158,7 @@ struct transfer_to_blind_operation : public base_operation
blind_factor_type blinding_factor; blind_factor_type blinding_factor;
vector<blind_output> outputs; vector<blind_output> outputs;
account_id_type fee_payer()const { return account_id_type{}; } account_id_type fee_payer()const { return from; }
//account_id_type fee_payer()const { return from; }
//void validate()const; //void validate()const;
//share_type calculate_fee(const fee_parameters_type& )const; //share_type calculate_fee(const fee_parameters_type& )const;
}; };
@ -181,9 +179,7 @@ struct transfer_from_blind_operation : public base_operation
blind_factor_type blinding_factor; blind_factor_type blinding_factor;
vector<blind_input> inputs; vector<blind_input> inputs;
account_id_type fee_payer()const { return account_id_type{}; } account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
//account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
//void validate()const; //void validate()const;
//void get_required_authorities( vector<authority>& a )const //void get_required_authorities( vector<authority>& a )const
//{ //{
@ -246,10 +242,8 @@ struct blind_transfer_operation : public base_operation
vector<blind_input> inputs; vector<blind_input> inputs;
vector<blind_output> outputs; vector<blind_output> outputs;
account_id_type fee_payer()const { return account_id_type{}; }
/** graphene TEMP account */ /** graphene TEMP account */
//account_id_type fee_payer()const; account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
//void validate()const; //void validate()const;
//share_type calculate_fee( const fee_parameters_type& k )const; //share_type calculate_fee( const fee_parameters_type& k )const;
//void get_required_authorities( vector<authority>& a )const //void get_required_authorities( vector<authority>& a )const

View file

@ -18,7 +18,7 @@ namespace graphene
// Buyer purchasing lottery tickets // Buyer purchasing lottery tickets
account_id_type buyer; account_id_type buyer;
// count of tickets to buy // count of tickets to buy
uint64_t tickets_to_buy; share_type tickets_to_buy;
// amount that can spent // amount that can spent
asset amount; asset amount;

View file

@ -1,17 +1,23 @@
#pragma once #pragma once
#include <graphene/chain/protocol/base.hpp> #include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_sidechain_info.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
struct son_wallet_recreate_operation : public base_operation struct son_wallet_recreate_operation : public base_operation
{ {
struct fee_parameters_type { uint64_t fee = 0; }; struct fee_parameters_type { uint64_t fee = 0; };
struct ext
{
optional<flat_map<sidechain_type, vector<son_sidechain_info> > > sidechain_sons;
};
asset fee; asset fee;
account_id_type payer; account_id_type payer;
vector<son_info> sons; vector<son_info> sons;
extension< ext > extensions;
account_id_type fee_payer()const { return payer; } account_id_type fee_payer()const { return payer; }
share_type calculate_fee(const fee_parameters_type& k)const { return 0; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
@ -35,6 +41,7 @@ namespace graphene { namespace chain {
} } // namespace graphene::chain } } // namespace graphene::chain
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) FC_REFLECT(graphene::chain::son_wallet_recreate_operation::ext, (sidechain_sons))
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons)(extensions) )
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )

View file

@ -395,6 +395,13 @@ namespace graphene { namespace chain {
bool is_valid_muse( const std::string& base58str ); bool is_valid_muse( const std::string& base58str );
}; };
class pubkey_comparator {
public:
inline bool operator()(const public_key_type& a, const public_key_type& b) const {
return a.key_data < b.key_data;
}
};
struct extended_public_key_type struct extended_public_key_type
{ {
struct binary_key struct binary_key

View file

@ -59,7 +59,9 @@ struct vote_id_type
committee, committee,
witness, witness,
worker, worker,
son, son_bitcoin,
son_hive,
son_ethereum,
VOTE_TYPE_COUNT VOTE_TYPE_COUNT
}; };
@ -144,7 +146,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> ) FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(son_ethereum)(VOTE_TYPE_COUNT) )
FC_REFLECT( graphene::chain::vote_id_type, (content) ) FC_REFLECT( graphene::chain::vote_id_type, (content) )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type )

View file

@ -31,11 +31,20 @@ namespace graphene { namespace chain {
time_point_sec expires; time_point_sec expires;
sidechain_address_object() : sidechain_address_object() :
sidechain(sidechain_type::bitcoin), sidechain(sidechain_type::bitcoin), //! FIXME - bitcoin ???
deposit_public_key(""), deposit_public_key(""),
deposit_address(""), deposit_address(""),
withdraw_public_key(""), withdraw_public_key(""),
withdraw_address("") {} withdraw_address("") {}
inline string get_deposit_address() const {
if(sidechain_type::ethereum != sidechain)
return deposit_address;
auto deposit_address_lower = deposit_address;
std::transform(deposit_address_lower.begin(), deposit_address_lower.end(), deposit_address_lower.begin(), ::tolower);
return deposit_address_lower;
}
}; };
struct by_account; struct by_account;
@ -76,7 +85,7 @@ namespace graphene { namespace chain {
ordered_non_unique< tag<by_sidechain_and_deposit_address_and_expires>, ordered_non_unique< tag<by_sidechain_and_deposit_address_and_expires>,
composite_key<sidechain_address_object, composite_key<sidechain_address_object,
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>, member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>,
member<sidechain_address_object, string, &sidechain_address_object::deposit_address>, const_mem_fun<sidechain_address_object, string, &sidechain_address_object::get_deposit_address>,
member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires> member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires>
> >
> >

View file

@ -1,6 +1,11 @@
#pragma once #pragma once
#include <set>
#include <graphene/chain/hardfork.hpp>
#include <fc/reflect/reflect.hpp> #include <fc/reflect/reflect.hpp>
#include <fc/time.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -13,7 +18,23 @@ enum class sidechain_type {
hive hive
}; };
} } static const std::set<sidechain_type> all_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
inline std::set<sidechain_type> active_sidechain_types(const fc::time_point_sec block_time) {
std::set<sidechain_type> active_sidechain_types{};
if (block_time >= HARDFORK_SON_TIME)
active_sidechain_types.insert(sidechain_type::bitcoin);
if (block_time >= HARDFORK_SON_FOR_HIVE_TIME)
active_sidechain_types.insert(sidechain_type::hive);
if (block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME)
active_sidechain_types.insert(sidechain_type::ethereum);
return active_sidechain_types;
}
} // namespace chain
} // namespace graphene
FC_REFLECT_ENUM(graphene::chain::sidechain_type, FC_REFLECT_ENUM(graphene::chain::sidechain_type,
(unknown) (unknown)

View file

@ -2,7 +2,7 @@
#include <boost/multi_index/composite_key.hpp> #include <boost/multi_index/composite_key.hpp>
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
using namespace graphene::db; using namespace graphene::db;
@ -30,7 +30,7 @@ namespace graphene { namespace chain {
sidechain_type sidechain = sidechain_type::unknown; sidechain_type sidechain = sidechain_type::unknown;
object_id_type object_id; object_id_type object_id;
std::string transaction; std::string transaction;
std::vector<son_info> signers; std::vector<son_sidechain_info> signers;
std::vector<std::pair<son_id_type, std::string>> signatures; std::vector<std::pair<son_id_type, std::string>> signatures;
std::string sidechain_transaction; std::string sidechain_transaction;

View file

@ -3,7 +3,6 @@
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
using namespace graphene::db;
/** /**
* @class son_info * @class son_info
@ -40,8 +39,4 @@ namespace graphene { namespace chain {
} } } }
FC_REFLECT( graphene::chain::son_info, FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) (sidechain_public_keys) )
(son_id)
(weight)
(signing_key)
(sidechain_public_keys) )

View file

@ -35,15 +35,15 @@ namespace graphene { namespace chain {
// Transactions signed since the last son payouts // Transactions signed since the last son payouts
flat_map<sidechain_type, uint64_t> txs_signed; flat_map<sidechain_type, uint64_t> txs_signed;
// Total Voted Active time i.e. duration selected as part of voted active SONs // Total Voted Active time i.e. duration selected as part of voted active SONs
uint64_t total_voted_time = 0; flat_map<sidechain_type, uint64_t> total_voted_time;
// Total Downtime barring the current down time in seconds, used for stats to present to user // Total Downtime barring the current down time in seconds, used for stats to present to user
uint64_t total_downtime = 0; flat_map<sidechain_type, uint64_t> total_downtime;
// Current Interval Downtime since last maintenance // Current Interval Downtime since last maintenance
uint64_t current_interval_downtime = 0; flat_map<sidechain_type, uint64_t> current_interval_downtime;
// Down timestamp, if son status is in_maintenance use this // Down timestamp, if son status is in_maintenance use this
fc::time_point_sec last_down_timestamp; flat_map<sidechain_type, fc::time_point_sec> last_down_timestamp;
// Last Active heartbeat timestamp // Last Active heartbeat timestamp
fc::time_point_sec last_active_timestamp; flat_map<sidechain_type, fc::time_point_sec> last_active_timestamp;
// Deregistered Timestamp // Deregistered Timestamp
fc::time_point_sec deregistered_timestamp; fc::time_point_sec deregistered_timestamp;
// Total sidechain transactions reported by SON network while SON was active // Total sidechain transactions reported by SON network while SON was active
@ -64,23 +64,48 @@ namespace graphene { namespace chain {
static const uint8_t type_id = son_object_type; static const uint8_t type_id = son_object_type;
account_id_type son_account; account_id_type son_account;
vote_id_type vote_id; flat_map<sidechain_type, vote_id_type> sidechain_vote_ids;
uint64_t total_votes = 0; flat_map<sidechain_type, uint64_t> total_votes = []()
{
flat_map<sidechain_type, uint64_t> total_votes;
for(const auto& active_sidechain_type : all_sidechain_types)
{
total_votes[active_sidechain_type] = 0;
}
return total_votes;
}();
string url; string url;
vesting_balance_id_type deposit; vesting_balance_id_type deposit;
public_key_type signing_key; public_key_type signing_key;
vesting_balance_id_type pay_vb; vesting_balance_id_type pay_vb;
son_statistics_id_type statistics; son_statistics_id_type statistics;
son_status status = son_status::inactive; flat_map<sidechain_type, son_status> statuses = []()
{
flat_map<sidechain_type, son_status> statuses;
for(const auto& active_sidechain_type : all_sidechain_types)
{
statuses[active_sidechain_type] = son_status::inactive;
}
return statuses;
}();
flat_map<sidechain_type, string> sidechain_public_keys; flat_map<sidechain_type, string> sidechain_public_keys;
void pay_son_fee(share_type pay, database& db); void pay_son_fee(share_type pay, database& db);
bool has_valid_config()const; bool has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const;
bool has_valid_config(time_point_sec head_block_time)const;
inline optional<vote_id_type> get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.contains(sidechain) ? sidechain_vote_ids.at(sidechain) : optional<vote_id_type>{}; }
inline optional<vote_id_type> get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
inline optional<vote_id_type> get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
inline optional<vote_id_type> get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
private:
bool has_valid_config(sidechain_type sidechain) const;
}; };
struct by_account; struct by_account;
struct by_vote_id; struct by_vote_id_bitcoin;
struct by_vote_id_hive;
struct by_vote_id_ethereum;
using son_multi_index_type = multi_index_container< using son_multi_index_type = multi_index_container<
son_object, son_object,
indexed_by< indexed_by<
@ -90,8 +115,14 @@ namespace graphene { namespace chain {
ordered_unique< tag<by_account>, ordered_unique< tag<by_account>,
member<son_object, account_id_type, &son_object::son_account> member<son_object, account_id_type, &son_object::son_account>
>, >,
ordered_unique< tag<by_vote_id>, ordered_non_unique< tag<by_vote_id_bitcoin>,
member<son_object, vote_id_type, &son_object::vote_id> const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_bitcoin_vote_id>
>,
ordered_non_unique< tag<by_vote_id_hive>,
const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_hive_vote_id>
>,
ordered_non_unique< tag<by_vote_id_ethereum>,
const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_ethereum_vote_id>
> >
> >
>; >;
@ -117,14 +148,14 @@ FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintena
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
(son_account) (son_account)
(vote_id) (sidechain_vote_ids)
(total_votes) (total_votes)
(url) (url)
(deposit) (deposit)
(signing_key) (signing_key)
(pay_vb) (pay_vb)
(statistics) (statistics)
(status) (statuses)
(sidechain_public_keys) (sidechain_public_keys)
) )

View file

@ -0,0 +1,31 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain {
/**
* @class son_sidechain_info
* @brief tracks information about a SON info required to re/create primary wallet
* @ingroup object
*/
struct son_sidechain_info {
son_id_type son_id;
weight_type weight = 0;
public_key_type signing_key;
string public_key;
bool operator==(const son_sidechain_info& rhs) const {
bool son_sets_equal =
(son_id == rhs.son_id) &&
(weight == rhs.weight) &&
(signing_key == rhs.signing_key) &&
(public_key == rhs.public_key);
return son_sets_equal;
}
};
} }
FC_REFLECT( graphene::chain::son_sidechain_info, (son_id) (weight) (signing_key) (public_key) )

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
time_point_sec expires; time_point_sec expires;
flat_map<sidechain_type, string> addresses; flat_map<sidechain_type, string> addresses;
vector<son_info> sons; flat_map<sidechain_type, vector<son_sidechain_info> > sons;
}; };
struct by_valid_from; struct by_valid_from;

View file

@ -23,7 +23,7 @@ namespace graphene { namespace chain {
optional<voters_info_object> voters_for_witness; optional<voters_info_object> voters_for_witness;
optional<vector<voters_info_object> > voters_for_workers; optional<vector<voters_info_object> > voters_for_workers;
optional<vector<voters_info_object> > voters_against_workers; optional<vector<voters_info_object> > voters_against_workers;
optional<voters_info_object> voters_for_son; optional<flat_map<sidechain_type, voters_info_object> > voters_for_son;
}; };
} } // graphene::chain } } // graphene::chain

View file

@ -23,7 +23,7 @@ namespace graphene { namespace chain {
optional< vector< votes_info_object > > votes_for_witnesses; optional< vector< votes_info_object > > votes_for_witnesses;
optional< vector< votes_info_object > > votes_for_workers; optional< vector< votes_info_object > > votes_for_workers;
optional< vector< votes_info_object > > votes_against_workers; optional< vector< votes_info_object > > votes_against_workers;
optional< vector< votes_info_object > > votes_for_sons; optional< flat_map<sidechain_type, vector< votes_info_object > > > votes_for_sons;
}; };
} } // graphene::chain } } // graphene::chain

View file

@ -162,8 +162,12 @@ class generic_witness_scheduler
_schedule.pop_front(); _schedule.pop_front();
auto it = _lame_duck.find( result ); auto it = _lame_duck.find( result );
if( it != _lame_duck.end() ) if( it != _lame_duck.end() ) {
set< WitnessID > removal_set;
removal_set.insert(*it);
remove_all( removal_set );
_lame_duck.erase(it); _lame_duck.erase(it);
}
if( debug ) check_invariant(); if( debug ) check_invariant();
return result; return result;
} }
@ -389,7 +393,7 @@ class generic_witness_scheduler
// scheduled // scheduled
std::deque < WitnessID > _schedule; std::deque < WitnessID > _schedule;
// in _schedule, but not to be replaced // in _schedule, but must be removed
set< WitnessID > _lame_duck; set< WitnessID > _lame_duck;
}; };

View file

@ -30,7 +30,7 @@ namespace graphene
auto lottery_options = lottery_md_obj.lottery_data->lottery_options; auto lottery_options = lottery_md_obj.lottery_data->lottery_options;
FC_ASSERT(lottery_options.ticket_price.asset_id == op.amount.asset_id); FC_ASSERT(lottery_options.ticket_price.asset_id == op.amount.asset_id);
FC_ASSERT((double)op.amount.amount.value / lottery_options.ticket_price.amount.value == (double)op.tickets_to_buy); FC_ASSERT(op.tickets_to_buy * lottery_options.ticket_price.amount.value == op.amount.amount.value);
return void_result(); return void_result();
} }
FC_CAPTURE_AND_RETHROW((op)) FC_CAPTURE_AND_RETHROW((op))

View file

@ -174,22 +174,37 @@ void account_options::validate() const
{ {
auto needed_witnesses = num_witness; auto needed_witnesses = num_witness;
auto needed_committee = num_committee; auto needed_committee = num_committee;
auto needed_sons = num_son();
for( vote_id_type id : votes ) for( vote_id_type id : votes )
if( id.type() == vote_id_type::witness && needed_witnesses ) if( id.type() == vote_id_type::witness && needed_witnesses )
--needed_witnesses; --needed_witnesses;
else if ( id.type() == vote_id_type::committee && needed_committee ) else if ( id.type() == vote_id_type::committee && needed_committee )
--needed_committee; --needed_committee;
else if ( id.type() == vote_id_type::son && needed_sons )
--needed_sons;
FC_ASSERT( needed_witnesses == 0, FC_ASSERT( needed_witnesses == 0,
"May not specify fewer witnesses than the number voted for."); "May not specify fewer witnesses than the number voted for.");
FC_ASSERT( needed_committee == 0, FC_ASSERT( needed_committee == 0,
"May not specify fewer committee members than the number voted for."); "May not specify fewer committee members than the number voted for.");
FC_ASSERT( needed_sons == 0,
"May not specify fewer SONs than the number voted for."); if ( extensions.value.num_son.valid() )
{
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
for( vote_id_type id : votes )
if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
--needed_sons[sidechain_type::bitcoin];
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
--needed_sons[sidechain_type::hive];
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
--needed_sons[sidechain_type::ethereum];
FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0,
"May not specify fewer Bitcoin SONs than the number voted for.");
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
"May not specify fewer Hive SONs than the number voted for.");
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
"May not specify fewer Ethereum SONs than the number voted for.");
}
} }
void affiliate_reward_distribution::validate() const void affiliate_reward_distribution::validate() const

View file

@ -22,12 +22,15 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address
const auto &sidechain_addresses_idx = db().get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>(); const auto &sidechain_addresses_idx = db().get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum())); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum()));
if (addr_itr != sidechain_addresses_idx.end()) if (addr_itr != sidechain_addresses_idx.end()) {
{ if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) {
db().remove(*addr_itr);
} else {
db().modify(*addr_itr, [&](sidechain_address_object &sao) { db().modify(*addr_itr, [&](sidechain_address_object &sao) {
sao.expires = db().head_block_time(); sao.expires = db().head_block_time();
}); });
} }
}
const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){ const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){
obj.sidechain_address_account = op.sidechain_address_account; obj.sidechain_address_account = op.sidechain_address_account;
@ -47,7 +50,7 @@ void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_addr
{ try { { try {
const auto& sidx = db().get_index_type<son_index>().indices().get<by_account>(); const auto& sidx = db().get_index_type<son_index>().indices().get<by_account>();
const auto& son_obj = sidx.find(op.payer); const auto& son_obj = sidx.find(op.payer);
FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" ); FC_ASSERT( son_obj != sidx.end() && db().is_son_active(op.sidechain, son_obj->id), "Non active SON trying to update deposit address object" );
const auto& sdpke_idx = db().get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_public_key_and_expires>(); const auto& sdpke_idx = db().get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_public_key_and_expires>();
FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid"); FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid");
FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key"); FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key");
@ -106,10 +109,14 @@ void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
auto sidechain_address = idx.find(op.sidechain_address_id); auto sidechain_address = idx.find(op.sidechain_address_id);
if (sidechain_address != idx.end()) { if (sidechain_address != idx.end()) {
if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) {
db().remove(*sidechain_address);
} else {
db().modify(*sidechain_address, [&](sidechain_address_object &sao) { db().modify(*sidechain_address, [&](sidechain_address_object &sao) {
sao.expires = db().head_block_time(); sao.expires = db().head_block_time();
}); });
} }
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -28,15 +28,26 @@ void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_
object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op)
{ try { { try {
const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) { const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) {
sto.timestamp = db().head_block_time(); sto.timestamp = db().head_block_time();
sto.sidechain = op.sidechain; sto.sidechain = op.sidechain;
sto.object_id = op.object_id; sto.object_id = op.object_id;
sto.transaction = op.transaction; sto.transaction = op.transaction;
sto.signers = op.signers; std::vector<son_sidechain_info> signers;
std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) { for(const auto& signer : op.signers){
son_sidechain_info ssi;
ssi.son_id = signer.son_id;
ssi.weight = signer.weight;
ssi.signing_key = signer.signing_key;
ssi.public_key = signer.sidechain_public_keys.at(op.sidechain);
signers.emplace_back(std::move(ssi));
}
sto.signers = std::move(signers);
std::transform(sto.signers.begin(), sto.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_sidechain_info &si) {
return std::make_pair(si.son_id, std::string()); return std::make_pair(si.son_id, std::string());
}); });
for (const auto &si : op.signers) { for (const auto &si : sto.signers) {
sto.total_weight = sto.total_weight + si.weight; sto.total_weight = sto.total_weight + si.weight;
} }
sto.sidechain_transaction = ""; sto.sidechain_transaction = "";

View file

@ -39,13 +39,28 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
object_id_type create_son_evaluator::do_apply(const son_create_operation& op) object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
{ try { { try {
vote_id_type vote_id; vote_id_type vote_id;
flat_map<sidechain_type, vote_id_type> vote_ids;
const auto now = db().head_block_time();
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
db().modify(db().get_global_properties(), [&vote_id](global_property_object &p) { db().modify(db().get_global_properties(), [&vote_id](global_property_object &p) {
vote_id = get_next_vote_id(p, vote_id_type::son); vote_id = get_next_vote_id(p, vote_id_type::son_bitcoin);
}); });
}
else {
db().modify(db().get_global_properties(), [&vote_ids](global_property_object &p) {
vote_ids[sidechain_type::bitcoin] = get_next_vote_id(p, vote_id_type::son_bitcoin);
vote_ids[sidechain_type::hive] = get_next_vote_id(p, vote_id_type::son_hive);
vote_ids[sidechain_type::ethereum] = get_next_vote_id(p, vote_id_type::son_ethereum);
});
}
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ) { const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ) {
obj.son_account = op.owner_account; obj.son_account = op.owner_account;
obj.vote_id = vote_id; if( now < HARDFORK_SON_FOR_ETHEREUM_TIME )
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id;
else
obj.sidechain_vote_ids = vote_ids;
obj.url = op.url; obj.url = op.url;
obj.deposit = op.deposit; obj.deposit = op.deposit;
obj.signing_key = op.signing_key; obj.signing_key = op.signing_key;
@ -94,7 +109,8 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key; if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key;
if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys; if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys;
if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb; if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb;
if(so.status == son_status::deregistered) so.status = son_status::inactive; for(auto& status : so.statuses)
if(status.second == son_status::deregistered) status.second = son_status::inactive;
}); });
} }
return op.son_id; return op.son_id;
@ -127,7 +143,8 @@ void_result deregister_son_evaluator::do_apply(const son_deregister_operation& o
}); });
db().modify(*son, [&op](son_object &so) { db().modify(*son, [&op](son_object &so) {
so.status = son_status::deregistered; for(auto& status : so.statuses)
status.second = son_status::deregistered;
}); });
auto stats_obj = ss_idx.find(son->statistics); auto stats_obj = ss_idx.find(son->statistics);
@ -144,18 +161,28 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
{ try { { try {
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
auto itr = idx.find(op.son_id); const auto itr = idx.find(op.son_id);
FC_ASSERT( itr != idx.end() ); FC_ASSERT( itr != idx.end() );
FC_ASSERT(itr->son_account == op.owner_account); FC_ASSERT(itr->son_account == op.owner_account);
auto stats = itr->statistics( db() ); auto stats = itr->statistics( db() );
// Inactive SONs need not send heartbeats // Inactive SONs need not send heartbeats
FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); bool status_need_to_send_heartbeats = false;
for(const auto& status : itr->statuses)
{
if( (status.second == son_status::active) || (status.second == son_status::in_maintenance) || (status.second == son_status::request_maintenance) )
status_need_to_send_heartbeats = true;
}
FC_ASSERT(status_need_to_send_heartbeats, "Inactive SONs need not send heartbeats");
// Account for network delays // Account for network delays
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
// Account for server ntp sync difference // Account for server ntp sync difference
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); if(stats.last_active_timestamp.contains(active_sidechain_type))
FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type));
if(stats.last_down_timestamp.contains(active_sidechain_type))
FC_ASSERT(op.ts > stats.last_down_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} is invalid can't be <= last down timestamp", ("sidechain", active_sidechain_type));
}
FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold");
FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold"); FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold");
return void_result(); return void_result();
@ -164,46 +191,50 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op) object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op)
{ try { { try {
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
auto itr = idx.find(op.son_id); const auto itr = idx.find(op.son_id);
if(itr != idx.end()) if(itr != idx.end())
{ {
const global_property_object& gpo = db().get_global_properties(); const global_property_object& gpo = db().get_global_properties();
for(const auto& active_sidechain_sons : gpo.active_sons) {
const auto& sidechain = active_sidechain_sons.first;
const auto& active_sons = active_sidechain_sons.second;
vector<son_id_type> active_son_ids; vector<son_id_type> active_son_ids;
active_son_ids.reserve(gpo.active_sons.size()); active_son_ids.reserve(active_sons.size());
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), std::transform(active_sons.cbegin(), active_sons.cend(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info& swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); const auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id);
bool is_son_active = true; bool is_son_active = true;
if (it_son == active_son_ids.end()) { if (it_son == active_son_ids.end()) {
is_son_active = false; is_son_active = false;
} }
if(itr->status == son_status::in_maintenance) { if (itr->statuses.at(sidechain) == son_status::in_maintenance) {
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
{ sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - (sso.last_down_timestamp.contains(sidechain) ? sso.last_down_timestamp.at(sidechain).sec_since_epoch() : op.ts.sec_since_epoch());
sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); sso.last_active_timestamp[sidechain] = op.ts;
sso.last_active_timestamp = op.ts;
}); });
db().modify(*itr, [&is_son_active](son_object &so) { db().modify(*itr, [&is_son_active, &sidechain](son_object &so) {
if (is_son_active) { if (is_son_active) {
so.status = son_status::active; so.statuses[sidechain] = son_status::active;
} else { } else {
so.status = son_status::inactive; so.statuses[sidechain] = son_status::inactive;
} }
}); });
} else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { } else if ((itr->statuses.at(sidechain) == son_status::active) || (itr->statuses.at(sidechain) == son_status::request_maintenance)) {
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
{ sso.last_active_timestamp[sidechain] = op.ts;
sso.last_active_timestamp = op.ts;
}); });
} }
} }
}
return op.son_id; return op.son_id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -213,30 +244,42 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
FC_ASSERT( idx.find(op.son_id) != idx.end() ); FC_ASSERT( idx.find(op.son_id) != idx.end() );
auto itr = idx.find(op.son_id); const auto itr = idx.find(op.son_id);
auto stats = itr->statistics( db() ); const auto stats = itr->statistics( db() );
FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); bool status_need_to_report_down = false;
FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); for(const auto& status : itr->statuses)
{
if( (status.second == son_status::active) || (status.second == son_status::request_maintenance) )
status_need_to_report_down = true;
}
FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
if(stats.last_active_timestamp.contains(active_sidechain_type))
FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type));
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op) object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op)
{ try { { try {
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
auto itr = idx.find(op.son_id); const auto itr = idx.find(op.son_id);
if(itr != idx.end()) if(itr != idx.end())
{ {
if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { for( const auto& status : itr->statuses ) {
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) const auto& sidechain = status.first;
{
sso.last_down_timestamp = op.down_ts; if ((status.second == son_status::active) || (status.second == son_status::request_maintenance)) {
db().modify(*itr, [&sidechain](son_object &so) {
so.statuses[sidechain] = son_status::in_maintenance;
}); });
db().modify(*itr, [&op](son_object &so) { db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
so.status = son_status::in_maintenance; sso.last_down_timestamp[sidechain] = op.down_ts;
}); });
} }
} }
}
return op.son_id; return op.son_id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -249,9 +292,19 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati
FC_ASSERT( itr != idx.end() ); FC_ASSERT( itr != idx.end() );
// Inactive SONs can't go to maintenance, toggle between active and request_maintenance states // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states
if(op.request_type == son_maintenance_request_type::request_maintenance) { if(op.request_type == son_maintenance_request_type::request_maintenance) {
FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance"); bool status_active = false;
for(const auto& status : itr->statuses) {
if( (status.second == son_status::active) )
status_active = true;
}
FC_ASSERT(status_active, "Inactive SONs can't request for maintenance");
} else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request"); bool status_request_maintenance = false;
for(const auto& status : itr->statuses) {
if( (status.second == son_status::request_maintenance) )
status_request_maintenance = true;
}
FC_ASSERT(status_request_maintenance, "Only maintenance requested SONs can cancel the request");
} else { } else {
FC_ASSERT(false, "Invalid maintenance operation"); FC_ASSERT(false, "Invalid maintenance operation");
} }
@ -264,16 +317,34 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati
auto itr = idx.find(op.son_id); auto itr = idx.find(op.son_id);
if(itr != idx.end()) if(itr != idx.end())
{ {
if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) { bool status_active = false;
for(const auto& status : itr->statuses) {
if( (status.second == son_status::active) )
status_active = true;
}
if(status_active && op.request_type == son_maintenance_request_type::request_maintenance) {
db().modify(*itr, [](son_object &so) { db().modify(*itr, [](son_object &so) {
so.status = son_status::request_maintenance; for(auto& status : so.statuses) {
status.second = son_status::request_maintenance;
}
}); });
} else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { }
else
{
bool status_request_maintenance = false;
for(const auto& status : itr->statuses) {
if( (status.second == son_status::request_maintenance) )
status_request_maintenance = true;
}
if(status_request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
db().modify(*itr, [](son_object &so) { db().modify(*itr, [](son_object &so) {
so.status = son_status::active; for(auto& status : so.statuses) {
status.second = son_status::active;
}
}); });
} }
} }
}
return op.son_id; return op.son_id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -6,20 +6,22 @@ namespace graphene { namespace chain {
db.adjust_balance(son_account, pay); db.adjust_balance(son_account, pay);
} }
bool son_object::has_valid_config()const { bool son_object::has_valid_config(sidechain_type sidechain) const {
return ((std::string(signing_key).length() > 0) && return (sidechain_public_keys.find( sidechain ) != sidechain_public_keys.end()) &&
(sidechain_public_keys.size() > 0) && (sidechain_public_keys.at(sidechain).length() > 0);
(sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) &&
(sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0));
} }
bool son_object::has_valid_config(time_point_sec head_block_time)const { bool son_object::has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const {
bool retval = has_valid_config(); bool retval = (std::string(signing_key).length() > 0) && (sidechain_public_keys.size() > 0);
if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME) { if (head_block_time < HARDFORK_SON_FOR_HIVE_TIME) {
retval = retval && retval = retval && has_valid_config(sidechain_type::bitcoin);
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) && }
(sidechain_public_keys.at(sidechain_type::hive).length() > 0); if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME && head_block_time < HARDFORK_SON_FOR_ETHEREUM_TIME) {
retval = retval && has_valid_config(sidechain_type::bitcoin) && has_valid_config(sidechain_type::hive);
}
else if (head_block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
retval = retval && has_valid_config(sidechain);
} }
return retval; return retval;

View file

@ -23,9 +23,9 @@ void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_de
const auto &swdo_idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>(); const auto &swdo_idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>();
const auto swdo = swdo_idx.find(op.sidechain_uid); const auto swdo = swdo_idx.find(op.sidechain_uid);
if (swdo == swdo_idx.end()) { if (swdo == swdo_idx.end()) {
auto &gpo = db().get_global_properties(); const auto &gpo = db().get_global_properties();
bool expected = false; bool expected = false;
for (auto &si : gpo.active_sons) { for (auto &si : gpo.active_sons.at(op.sidechain)) {
if (op.son_id == si.son_id) { if (op.son_id == si.son_id) {
expected = true; expected = true;
break; break;
@ -78,8 +78,8 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de
swdo.peerplays_to = op.peerplays_to; swdo.peerplays_to = op.peerplays_to;
swdo.peerplays_asset = op.peerplays_asset; swdo.peerplays_asset = op.peerplays_asset;
auto &gpo = db().get_global_properties(); const auto &gpo = db().get_global_properties();
for (auto &si : gpo.active_sons) { for (auto &si : gpo.active_sons.at(op.sidechain)) {
swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id); auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
@ -142,11 +142,11 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
const auto& itr = idx.find(op.son_wallet_deposit_id); const auto& itr = idx.find(op.son_wallet_deposit_id);
FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); FC_ASSERT(itr != idx.end(), "Son wallet deposit not found");
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); FC_ASSERT(!itr->processed, "Son wallet deposit is already processed");
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -7,7 +7,8 @@ namespace graphene { namespace chain {
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
@ -16,12 +17,36 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate
{ {
// Compare current wallet SONs and to-be lists of active sons // Compare current wallet SONs and to-be lists of active sons
auto cur_wallet_sons = (*itr).sons; auto cur_wallet_sons = (*itr).sons;
auto new_wallet_sons = op.sons; flat_map<sidechain_type, vector<son_sidechain_info> > new_wallet_sons;
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
for(const auto& son : op.sons){
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
son_sidechain_info ssi;
ssi.son_id = son.son_id;
ssi.weight = son.weight;
ssi.signing_key = son.signing_key;
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
new_wallet_sons[active_sidechain_type].emplace_back(std::move(ssi));
}
}
}
else{
FC_ASSERT(op.extensions.value.sidechain_sons.valid(), "Sons is not valid");
new_wallet_sons = *op.extensions.value.sidechain_sons;
}
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
if (son_sets_equal) {
for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) {
const auto& sidechain = cur_wallet_sidechain_sons.first;
const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second;
son_sets_equal = son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_wallet_sons.at(sidechain).size());
if (son_sets_equal) { if (son_sets_equal) {
for (size_t i = 0; i < cur_wallet_sons.size(); i++) { for (size_t i = 0; i < cur_wallet_sons.size(); i++) {
son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i); son_sets_equal = son_sets_equal && _cur_wallet_sidechain_sons.at(i) == new_wallet_sons.at(sidechain).at(i);
}
}
} }
} }
@ -43,9 +68,26 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate
} }
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){ const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
obj.valid_from = db().head_block_time(); const auto now = db().head_block_time();
obj.valid_from = now;
obj.expires = time_point_sec::maximum(); obj.expires = time_point_sec::maximum();
obj.sons = op.sons; if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
flat_map<sidechain_type, vector<son_sidechain_info> > sons;
for(const auto& son : op.sons){
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
son_sidechain_info ssi;
ssi.son_id = son.son_id;
ssi.weight = son.weight;
ssi.signing_key = son.signing_key;
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
sons[active_sidechain_type].emplace_back(std::move(ssi));
}
}
obj.sons = std::move(sons);
}
else{
obj.sons = *op.extensions.value.sidechain_sons;
}
}); });
return new_son_wallet_object.id; return new_son_wallet_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -55,8 +97,19 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
const son_wallet_id_type son_wallet_id = [&]{
if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME)
{
const auto ast = active_sidechain_types(db().head_block_time());
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
return son_wallet_id_type{ id };
}
return op.son_wallet_id;
}();
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); FC_ASSERT( idx.find(son_wallet_id) != idx.end() );
//auto itr = idx.find(op.son_wallet_id); //auto itr = idx.find(op.son_wallet_id);
//FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
// itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set"); // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
@ -65,8 +118,19 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
{ try { { try {
const son_wallet_id_type son_wallet_id = [&]{
if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME)
{
const auto ast = active_sidechain_types(db().head_block_time());
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
return son_wallet_id_type{ id };
}
return op.son_wallet_id;
}();
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
auto itr = idx.find(op.son_wallet_id); auto itr = idx.find(son_wallet_id);
if (itr != idx.end()) if (itr != idx.end())
{ {
if (itr->addresses.find(op.sidechain) == itr->addresses.end()) { if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {

View file

@ -10,12 +10,13 @@ namespace graphene { namespace chain {
void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op)
{ try { { try {
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
const auto so = son_idx.find(op.son_id); const auto so = son_idx.find(op.son_id);
FC_ASSERT(so != son_idx.end(), "SON not found"); FC_ASSERT(so != son_idx.end(), "SON not found");
FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner"); FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner");
FC_ASSERT(!(op.sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>(); const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists"); FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists");
@ -23,15 +24,23 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w
const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>(); const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
const auto swwo = swwo_idx.find(op.peerplays_uid); const auto swwo = swwo_idx.find(op.peerplays_uid);
if (swwo == swwo_idx.end()) { if (swwo == swwo_idx.end()) {
auto &gpo = db().get_global_properties(); const sidechain_type sidechain = [&op]{
if(op.sidechain == sidechain_type::peerplays){
return op.withdraw_sidechain;
}
else
return op.sidechain;
}();
const auto &gpo = db().get_global_properties();
bool expected = false; bool expected = false;
for (auto &si : gpo.active_sons) { for (auto &si : gpo.active_sons.at(sidechain)) {
if (op.son_id == si.son_id) { if (op.son_id == si.son_id) {
expected = true; expected = true;
break; break;
} }
} }
FC_ASSERT(expected, "Only active SON can create deposit"); FC_ASSERT(expected, "Only active SON can create withdraw");
} else { } else {
bool exactly_the_same = true; bool exactly_the_same = true;
exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain); exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain);
@ -76,8 +85,16 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_currency = op.withdraw_currency;
swwo.withdraw_amount = op.withdraw_amount; swwo.withdraw_amount = op.withdraw_amount;
auto &gpo = db().get_global_properties(); const sidechain_type sidechain = [&op]{
for (auto &si : gpo.active_sons) { if(op.sidechain == sidechain_type::peerplays){
return op.withdraw_sidechain;
}
else
return op.sidechain;
}();
const auto &gpo = db().get_global_properties();
for (auto &si : gpo.active_sons.at(sidechain)) {
swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id); auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
@ -138,13 +155,17 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op)
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto& itr = idx.find(op.son_wallet_withdraw_id); const auto& itr = idx.find(op.son_wallet_withdraw_id);
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
FC_ASSERT(!(itr->sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
if(itr->sidechain != sidechain_type::peerplays) {
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
}
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -29,6 +29,7 @@
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <map> #include <map>
#include <mutex>
namespace graphene { namespace db { namespace graphene { namespace db {
@ -144,6 +145,7 @@ namespace graphene { namespace db {
fc::path get_data_dir()const { return _data_dir; } fc::path get_data_dir()const { return _data_dir; }
/** public for testing purposes only... should be private in practice. */ /** public for testing purposes only... should be private in practice. */
mutable std::mutex _undo_db_mutex;
undo_database _undo_db; undo_database _undo_db;
protected: protected:
template<typename IndexType> template<typename IndexType>

View file

@ -47,4 +47,3 @@ namespace graphene { namespace net {
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type; const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
} } // graphene::net } } // graphene::net

View file

@ -23,6 +23,8 @@
*/ */
#pragma once #pragma once
#include <stddef.h>
#define GRAPHENE_NET_PROTOCOL_VERSION 106 #define GRAPHENE_NET_PROTOCOL_VERSION 106
/** /**
@ -110,3 +112,6 @@
#define GRAPHENE_NET_MAX_NESTED_OBJECTS (250) #define GRAPHENE_NET_MAX_NESTED_OBJECTS (250)
#define MAXIMUM_PEERDB_SIZE 1000 #define MAXIMUM_PEERDB_SIZE 1000
constexpr size_t MAX_BLOCKS_TO_HANDLE_AT_ONCE = 200;
constexpr size_t MAX_SYNC_BLOCKS_TO_PREFETCH = 10 * MAX_BLOCKS_TO_HANDLE_AT_ONCE;

View file

@ -61,7 +61,7 @@ namespace graphene { namespace net {
class node_delegate class node_delegate
{ {
public: public:
virtual ~node_delegate(){} virtual ~node_delegate() = default;
/** /**
* If delegate has the item, the network has no need to fetch it. * If delegate has the item, the network has no need to fetch it.
@ -71,7 +71,9 @@ namespace graphene { namespace net {
/** /**
* @brief Called when a new block comes in from the network * @brief Called when a new block comes in from the network
* *
* @param blk_msg the message which contains the block
* @param sync_mode true if the message was fetched through the sync process, false during normal operation * @param sync_mode true if the message was fetched through the sync process, false during normal operation
* @param contained_transaction_msg_ids container for the transactions to write back into
* @returns true if this message caused the blockchain to switch forks, false if it did not * @returns true if this message caused the blockchain to switch forks, false if it did not
* *
* @throws exception if error validating the item, otherwise the item is * @throws exception if error validating the item, otherwise the item is
@ -195,7 +197,7 @@ namespace graphene { namespace net {
{ {
public: public:
node(const std::string& user_agent); node(const std::string& user_agent);
~node(); virtual ~node();
void close(); void close();
@ -213,11 +215,34 @@ namespace graphene { namespace net {
*/ */
void add_node( const fc::ip::endpoint& ep ); void add_node( const fc::ip::endpoint& ep );
/*****
* @brief add a list of nodes to seed the p2p network
* @param seeds a vector of url strings
*/
void add_seed_nodes( std::vector<std::string> seeds );
/****
* @brief add a node to seed the p2p network
* @param in the url as a string
*/
void add_seed_node( const std::string& in);
/** /**
* Attempt to connect to the specified endpoint immediately. * Attempt to connect to the specified endpoint immediately.
*/ */
virtual void connect_to_endpoint( const fc::ip::endpoint& ep ); virtual void connect_to_endpoint( const fc::ip::endpoint& ep );
/**
* @brief Helper to convert a string to a collection of endpoints
*
* This converts a string (i.e. "bitshares.eu:665535" to a collection of endpoints.
* NOTE: Throws an exception if not in correct format or was unable to resolve URL.
*
* @param in the incoming string
* @returns a vector of endpoints
*/
static std::vector<fc::ip::endpoint> resolve_string_to_ip_endpoints( const std::string& in );
/** /**
* Specifies the network interface and port upon which incoming * Specifies the network interface and port upon which incoming
* connections should be accepted. * connections should be accepted.

View file

@ -62,6 +62,7 @@ namespace graphene { namespace net
class peer_connection_delegate class peer_connection_delegate
{ {
public: public:
virtual ~peer_connection_delegate() = default;
virtual void on_message(peer_connection* originating_peer, virtual void on_message(peer_connection* originating_peer,
const message& received_message) = 0; const message& received_message) = 0;
virtual void on_connection_closed(peer_connection* originating_peer) = 0; virtual void on_connection_closed(peer_connection* originating_peer) = 0;
@ -125,7 +126,7 @@ namespace graphene { namespace net
* it is sitting on the queue * it is sitting on the queue
*/ */
virtual size_t get_size_in_queue() = 0; virtual size_t get_size_in_queue() = 0;
virtual ~queued_message() {} virtual ~queued_message() = default;
}; };
/* when you queue up a 'real_queued_message', a full copy of the message is /* when you queue up a 'real_queued_message', a full copy of the message is

View file

@ -97,7 +97,7 @@ namespace graphene { namespace net {
{ {
public: public:
peer_database(); peer_database();
~peer_database(); virtual ~peer_database();
void open(const fc::path& databaseFilename); void open(const fc::path& databaseFilename);
void close(); void close();

View file

@ -72,6 +72,7 @@
#include <fc/io/raw_fwd.hpp> #include <fc/io/raw_fwd.hpp>
#include <fc/network/rate_limiting.hpp> #include <fc/network/rate_limiting.hpp>
#include <fc/network/ip.hpp> #include <fc/network/ip.hpp>
#include <fc/network/resolve.hpp>
#include <graphene/net/node.hpp> #include <graphene/net/node.hpp>
#include <graphene/net/peer_database.hpp> #include <graphene/net/peer_database.hpp>
@ -127,6 +128,124 @@ namespace graphene { namespace net {
namespace detail namespace detail
{ {
namespace bmi = boost::multi_index; namespace bmi = boost::multi_index;
/*******
* A class to wrap std::unordered_set for multithreading
*/
template <class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key> >
class concurrent_unordered_set : private std::unordered_set<Key, Hash, Pred>
{
private:
mutable fc::mutex mux;
public:
/// Iterations require a lock. This exposes the mutex. Use with care (i.e. lock_guard)
fc::mutex& get_mutex()const { return mux; }
/// Insertion
/// @{
std::pair< typename std::unordered_set<Key, Hash, Pred>::iterator, bool> emplace( Key key)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::emplace( key );
}
std::pair< typename std::unordered_set<Key, Hash, Pred>::iterator, bool> insert (const Key& val)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::insert( val );
}
/// @}
/// Size
/// @{
size_t size() const
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::size();
}
bool empty() const noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::empty();
}
/// @}
/// Removal
/// @{
void clear() noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
std::unordered_set<Key, Hash, Pred>::clear();
}
typename std::unordered_set<Key, Hash, Pred>::iterator erase(
typename std::unordered_set<Key, Hash, Pred>::const_iterator itr)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::erase( itr);
}
size_t erase( const Key& key)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::erase( key );
}
/// @}
/// Swap
/// @{
void swap( typename std::unordered_set<Key, Hash, Pred>& other ) noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
std::unordered_set<Key, Hash, Pred>::swap( other );
}
/// @}
/// Iteration
/// @{
typename std::unordered_set<Key, Hash, Pred>::iterator begin() noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::begin();
}
typename std::unordered_set<Key, Hash, Pred>::const_iterator begin() const noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::begin();
}
typename std::unordered_set<Key, Hash, Pred>::local_iterator begin(size_t n)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::begin(n);
}
typename std::unordered_set<Key, Hash, Pred>::const_local_iterator begin(size_t n) const
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::begin(n);
}
typename std::unordered_set<Key, Hash, Pred>::iterator end() noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::end();
}
typename std::unordered_set<Key, Hash, Pred>::const_iterator end() const noexcept
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::end();
}
typename std::unordered_set<Key, Hash, Pred>::local_iterator end(size_t n)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::end(n);
}
typename std::unordered_set<Key, Hash, Pred>::const_local_iterator end(size_t n) const
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::end(n);
}
/// @}
/// Search
typename std::unordered_set<Key, Hash, Pred>::const_iterator find(Key key)
{
fc::scoped_lock<fc::mutex> lock(mux);
return std::unordered_set<Key, Hash, Pred>::find(key);
}
};
class blockchain_tied_message_cache class blockchain_tied_message_cache
{ {
private: private:
@ -482,7 +601,7 @@ namespace graphene { namespace net { namespace detail {
// @{ // @{
fc::promise<void>::ptr _retrigger_advertise_inventory_loop_promise; fc::promise<void>::ptr _retrigger_advertise_inventory_loop_promise;
fc::future<void> _advertise_inventory_loop_done; fc::future<void> _advertise_inventory_loop_done;
std::unordered_set<item_id> _new_inventory; /// list of items we have received but not yet advertised to our peers concurrent_unordered_set<item_id> _new_inventory; /// list of items we have received but not yet advertised to our peers
// @} // @}
fc::future<void> _terminate_inactive_connections_loop_done; fc::future<void> _terminate_inactive_connections_loop_done;
@ -518,13 +637,13 @@ namespace graphene { namespace net { namespace detail {
/** Stores all connections which have not yet finished key exchange or are still sending initial handshaking messages /** Stores all connections which have not yet finished key exchange or are still sending initial handshaking messages
* back and forth (not yet ready to initiate syncing) */ * back and forth (not yet ready to initiate syncing) */
std::unordered_set<peer_connection_ptr> _handshaking_connections; concurrent_unordered_set<peer_connection_ptr> _handshaking_connections;
/** stores fully established connections we're either syncing with or in normal operation with */ /** stores fully established connections we're either syncing with or in normal operation with */
std::unordered_set<peer_connection_ptr> _active_connections; concurrent_unordered_set<peer_connection_ptr> _active_connections;
/** stores connections we've closed (sent closing message, not actually closed), but are still waiting for the remote end to close before we delete them */ /** stores connections we've closed (sent closing message, not actually closed), but are still waiting for the remote end to close before we delete them */
std::unordered_set<peer_connection_ptr> _closing_connections; concurrent_unordered_set<peer_connection_ptr> _closing_connections;
/** stores connections we've closed, but are still waiting for the OS to notify us that the socket is really closed */ /** stores connections we've closed, but are still waiting for the OS to notify us that the socket is really closed */
std::unordered_set<peer_connection_ptr> _terminating_connections; concurrent_unordered_set<peer_connection_ptr> _terminating_connections;
boost::circular_buffer<item_hash_t> _most_recent_blocks_accepted; // the /n/ most recent blocks we've accepted (currently tuned to the max number of connections) boost::circular_buffer<item_hash_t> _most_recent_blocks_accepted; // the /n/ most recent blocks we've accepted (currently tuned to the max number of connections)
@ -555,6 +674,10 @@ namespace graphene { namespace net { namespace detail {
fc::future<void> _bandwidth_monitor_loop_done; fc::future<void> _bandwidth_monitor_loop_done;
fc::future<void> _dump_node_status_task_done; fc::future<void> _dump_node_status_task_done;
/// Used by the task that checks whether addresses of seed nodes have been updated
/// @{
boost::container::flat_set<std::string> _seed_nodes;
fc::future<void> _update_seed_nodes_loop_done;
/* We have two alternate paths through the schedule_peer_for_deletion code -- one that /* We have two alternate paths through the schedule_peer_for_deletion code -- one that
* uses a mutex to prevent one fiber from adding items to the queue while another is deleting * uses a mutex to prevent one fiber from adding items to the queue while another is deleting
@ -728,6 +851,11 @@ namespace graphene { namespace net { namespace detail {
void listen_to_p2p_network(); void listen_to_p2p_network();
void connect_to_p2p_network(); void connect_to_p2p_network();
void add_node( const fc::ip::endpoint& ep ); void add_node( const fc::ip::endpoint& ep );
void add_seed_node( const std::string& in);
void add_seed_nodes( std::vector<std::string> seeds );
void resolve_seed_node_and_add( const std::string& seed_string );
void update_seed_nodes_task();
void schedule_next_update_seed_nodes_task();
void initiate_connect_to(const peer_connection_ptr& peer); void initiate_connect_to(const peer_connection_ptr& peer);
void connect_to_endpoint(const fc::ip::endpoint& ep); void connect_to_endpoint(const fc::ip::endpoint& ep);
void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available); void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available);
@ -844,6 +972,8 @@ namespace graphene { namespace net { namespace detail {
ilog( "cleaning up node" ); ilog( "cleaning up node" );
_node_is_shutting_down.store(true); _node_is_shutting_down.store(true);
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& active_peer : _active_connections) for (const peer_connection_ptr& active_peer : _active_connections)
{ {
fc::optional<fc::ip::endpoint> inbound_endpoint = active_peer->get_endpoint_for_connecting(); fc::optional<fc::ip::endpoint> inbound_endpoint = active_peer->get_endpoint_for_connecting();
@ -857,6 +987,7 @@ namespace graphene { namespace net { namespace detail {
} }
} }
} }
}
try try
{ {
@ -1051,6 +1182,7 @@ namespace graphene { namespace net { namespace detail {
std::set<item_hash_t> sync_items_to_request; std::set<item_hash_t> sync_items_to_request;
// for each idle peer that we're syncing with // for each idle peer that we're syncing with
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
{ {
if( peer->we_need_sync_items_from_peer && if( peer->we_need_sync_items_from_peer &&
@ -1109,6 +1241,7 @@ namespace graphene { namespace net { namespace detail {
bool node_impl::is_item_in_any_peers_inventory(const item_id& item) const bool node_impl::is_item_in_any_peers_inventory(const item_id& item) const
{ {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
{ {
if (peer->inventory_peer_advertised_to_us.find(item) != peer->inventory_peer_advertised_to_us.end() ) if (peer->inventory_peer_advertised_to_us.find(item) != peer->inventory_peer_advertised_to_us.end() )
@ -1148,9 +1281,13 @@ namespace graphene { namespace net { namespace detail {
fetch_messages_to_send_set items_by_peer; fetch_messages_to_send_set items_by_peer;
// initialize the fetch_messages_to_send with an empty set of items for all idle peers // initialize the fetch_messages_to_send with an empty set of items for all idle peers
for (const peer_connection_ptr& peer : _active_connections) {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) {
if (peer->idle()) if (peer->idle())
items_by_peer.insert(peer_and_items_to_fetch(peer)); items_by_peer.insert(peer_and_items_to_fetch(peer));
}
}
// now loop over all items we want to fetch // now loop over all items we want to fetch
for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();) for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();)
@ -1252,13 +1389,15 @@ namespace graphene { namespace net { namespace detail {
dlog("beginning an iteration of advertise inventory"); dlog("beginning an iteration of advertise inventory");
// swap inventory into local variable, clearing the node's copy // swap inventory into local variable, clearing the node's copy
std::unordered_set<item_id> inventory_to_advertise; std::unordered_set<item_id> inventory_to_advertise;
inventory_to_advertise.swap(_new_inventory); _new_inventory.swap(inventory_to_advertise);
// process all inventory to advertise and construct the inventory messages we'll send // process all inventory to advertise and construct the inventory messages we'll send
// first, then send them all in a batch (to avoid any fiber interruption points while // first, then send them all in a batch (to avoid any fiber interruption points while
// we're computing the messages) // we're computing the messages)
std::list<std::pair<peer_connection_ptr, item_ids_inventory_message> > inventory_messages_to_send; std::list<std::pair<peer_connection_ptr, item_ids_inventory_message> > inventory_messages_to_send;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
// only advertise to peers who are in sync with us // only advertise to peers who are in sync with us
@ -1303,6 +1442,7 @@ namespace graphene { namespace net { namespace detail {
} }
peer->clear_old_inventory(); peer->clear_old_inventory();
} }
}
for (auto iter = inventory_messages_to_send.begin(); iter != inventory_messages_to_send.end(); ++iter) for (auto iter = inventory_messages_to_send.begin(); iter != inventory_messages_to_send.end(); ++iter)
iter->first->send_message(iter->second); iter->first->send_message(iter->second);
@ -1350,7 +1490,10 @@ namespace graphene { namespace net { namespace detail {
uint32_t handshaking_timeout = _peer_inactivity_timeout; uint32_t handshaking_timeout = _peer_inactivity_timeout;
fc::time_point handshaking_disconnect_threshold = fc::time_point::now() - fc::seconds(handshaking_timeout); fc::time_point handshaking_disconnect_threshold = fc::time_point::now() - fc::seconds(handshaking_timeout);
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for( const peer_connection_ptr handshaking_peer : _handshaking_connections ) for( const peer_connection_ptr handshaking_peer : _handshaking_connections )
{
if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold && if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold &&
handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold && handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold &&
handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold ) handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold )
@ -1369,6 +1512,8 @@ namespace graphene { namespace net { namespace detail {
("received", handshaking_peer->get_total_bytes_received()))); ("received", handshaking_peer->get_total_bytes_received())));
peers_to_disconnect_forcibly.push_back( handshaking_peer ); peers_to_disconnect_forcibly.push_back( handshaking_peer );
} }
}
}
// timeout for any active peers is two block intervals // timeout for any active peers is two block intervals
uint32_t active_disconnect_timeout = 10 * _recent_block_interval_in_seconds; uint32_t active_disconnect_timeout = 10 * _recent_block_interval_in_seconds;
@ -1388,6 +1533,8 @@ namespace graphene { namespace net { namespace detail {
fc::time_point active_disconnect_threshold = fc::time_point::now() - fc::seconds(active_disconnect_timeout); fc::time_point active_disconnect_threshold = fc::time_point::now() - fc::seconds(active_disconnect_timeout);
fc::time_point active_send_keepalive_threshold = fc::time_point::now() - fc::seconds(active_send_keepalive_timeout); fc::time_point active_send_keepalive_threshold = fc::time_point::now() - fc::seconds(active_send_keepalive_timeout);
fc::time_point active_ignored_request_threshold = fc::time_point::now() - active_ignored_request_timeout; fc::time_point active_ignored_request_threshold = fc::time_point::now() - active_ignored_request_timeout;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& active_peer : _active_connections ) for( const peer_connection_ptr& active_peer : _active_connections )
{ {
if( active_peer->connection_initiation_time < active_disconnect_threshold && if( active_peer->connection_initiation_time < active_disconnect_threshold &&
@ -1455,27 +1602,34 @@ namespace graphene { namespace net { namespace detail {
} }
} }
} }
}
fc::time_point closing_disconnect_threshold = fc::time_point::now() - fc::seconds(GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT); fc::time_point closing_disconnect_threshold = fc::time_point::now() - fc::seconds(GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT);
for( const peer_connection_ptr& closing_peer : _closing_connections )
if( closing_peer->connection_closed_time < closing_disconnect_threshold )
{ {
fc::scoped_lock<fc::mutex> lock(_closing_connections.get_mutex());
for( const peer_connection_ptr& closing_peer : _closing_connections ) {
if (closing_peer->connection_closed_time < closing_disconnect_threshold) {
// we asked this peer to close their connectoin to us at least GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT // we asked this peer to close their connectoin to us at least GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT
// seconds ago, but they haven't done it yet. Terminate the connection now // seconds ago, but they haven't done it yet. Terminate the connection now
wlog("Forcibly disconnecting peer ${peer} who failed to close their connection in a timely manner", wlog("Forcibly disconnecting peer ${peer} who failed to close their connection in a timely manner",
("peer", closing_peer->get_remote_endpoint())); ("peer", closing_peer->get_remote_endpoint()));
peers_to_disconnect_forcibly.push_back(closing_peer); peers_to_disconnect_forcibly.push_back(closing_peer);
} }
}
}
uint32_t failed_terminate_timeout_seconds = 120; uint32_t failed_terminate_timeout_seconds = 120;
fc::time_point failed_terminate_threshold = fc::time_point::now() - fc::seconds(failed_terminate_timeout_seconds); fc::time_point failed_terminate_threshold = fc::time_point::now() - fc::seconds(failed_terminate_timeout_seconds);
for (const peer_connection_ptr& peer : _terminating_connections )
if (peer->get_connection_terminated_time() != fc::time_point::min() &&
peer->get_connection_terminated_time() < failed_terminate_threshold)
{ {
fc::scoped_lock<fc::mutex> lock(_terminating_connections.get_mutex());
for (const peer_connection_ptr& peer : _terminating_connections ) {
if (peer->get_connection_terminated_time() != fc::time_point::min() &&
peer->get_connection_terminated_time() < failed_terminate_threshold) {
wlog("Terminating connection with peer ${peer}, closing the connection didn't work", ("peer", peer->get_remote_endpoint())); wlog("Terminating connection with peer ${peer}, closing the connection didn't work", ("peer", peer->get_remote_endpoint()));
peers_to_terminate.push_back(peer); peers_to_terminate.push_back(peer);
} }
}
}
// That's the end of the sorting step; now all peers that require further processing are now in one of the // That's the end of the sorting step; now all peers that require further processing are now in one of the
// lists peers_to_disconnect_gently, peers_to_disconnect_forcibly, peers_to_send_keep_alive, or peers_to_terminate // lists peers_to_disconnect_gently, peers_to_disconnect_forcibly, peers_to_send_keep_alive, or peers_to_terminate
@ -1483,12 +1637,15 @@ namespace graphene { namespace net { namespace detail {
// if we've decided to delete any peers, do it now; in its current implementation this doesn't yield, // if we've decided to delete any peers, do it now; in its current implementation this doesn't yield,
// and once we start yielding, we may find that we've moved that peer to another list (closed or active) // and once we start yielding, we may find that we've moved that peer to another list (closed or active)
// and that triggers assertions, maybe even errors // and that triggers assertions, maybe even errors
{
fc::scoped_lock<fc::mutex> lock(_terminating_connections.get_mutex());
for (const peer_connection_ptr& peer : peers_to_terminate ) for (const peer_connection_ptr& peer : peers_to_terminate )
{ {
assert(_terminating_connections.find(peer) != _terminating_connections.end()); assert(_terminating_connections.find(peer) != _terminating_connections.end());
_terminating_connections.erase(peer); _terminating_connections.erase(peer);
schedule_peer_for_deletion(peer); schedule_peer_for_deletion(peer);
} }
}
peers_to_terminate.clear(); peers_to_terminate.clear();
// if we're going to abruptly disconnect anyone, do it here // if we're going to abruptly disconnect anyone, do it here
@ -1506,6 +1663,7 @@ namespace graphene { namespace net { namespace detail {
// disconnect reason, so it may yield) // disconnect reason, so it may yield)
for( const peer_connection_ptr& peer : peers_to_disconnect_gently ) for( const peer_connection_ptr& peer : peers_to_disconnect_gently )
{ {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
fc::exception detailed_error( FC_LOG_MESSAGE(warn, "Disconnecting due to inactivity", fc::exception detailed_error( FC_LOG_MESSAGE(warn, "Disconnecting due to inactivity",
( "last_message_received_seconds_ago", (peer->get_last_message_received_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() ) ( "last_message_received_seconds_ago", (peer->get_last_message_received_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() )
( "last_message_sent_seconds_ago", (peer->get_last_message_sent_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() ) ( "last_message_sent_seconds_ago", (peer->get_last_message_sent_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() )
@ -1529,6 +1687,7 @@ namespace graphene { namespace net { namespace detail {
{ {
VERIFY_CORRECT_THREAD(); VERIFY_CORRECT_THREAD();
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
std::list<peer_connection_ptr> original_active_peers(_active_connections.begin(), _active_connections.end()); std::list<peer_connection_ptr> original_active_peers(_active_connections.begin(), _active_connections.end());
for( const peer_connection_ptr& active_peer : original_active_peers ) for( const peer_connection_ptr& active_peer : original_active_peers )
{ {
@ -1700,12 +1859,19 @@ namespace graphene { namespace net { namespace detail {
peer_connection_ptr node_impl::get_peer_by_node_id(const node_id_t& node_id) peer_connection_ptr node_impl::get_peer_by_node_id(const node_id_t& node_id)
{ {
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& active_peer : _active_connections) for (const peer_connection_ptr& active_peer : _active_connections)
if (node_id == active_peer->node_id) if (node_id == active_peer->node_id)
return active_peer; return active_peer;
}
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for (const peer_connection_ptr& handshaking_peer : _handshaking_connections) for (const peer_connection_ptr& handshaking_peer : _handshaking_connections)
if (node_id == handshaking_peer->node_id) if (node_id == handshaking_peer->node_id)
return handshaking_peer; return handshaking_peer;
}
return peer_connection_ptr(); return peer_connection_ptr();
} }
@ -1717,18 +1883,25 @@ namespace graphene { namespace net { namespace detail {
dlog("is_already_connected_to_id returning true because the peer is us"); dlog("is_already_connected_to_id returning true because the peer is us");
return true; return true;
} }
for (const peer_connection_ptr active_peer : _active_connections)
if (node_id == active_peer->node_id)
{ {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr active_peer : _active_connections) {
if (node_id == active_peer->node_id) {
dlog("is_already_connected_to_id returning true because the peer is already in our active list"); dlog("is_already_connected_to_id returning true because the peer is already in our active list");
return true; return true;
} }
for (const peer_connection_ptr handshaking_peer : _handshaking_connections) }
if (node_id == handshaking_peer->node_id) }
{ {
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for (const peer_connection_ptr handshaking_peer : _handshaking_connections) {
if (node_id == handshaking_peer->node_id) {
dlog("is_already_connected_to_id returning true because the peer is already in our handshaking list"); dlog("is_already_connected_to_id returning true because the peer is already in our handshaking list");
return true; return true;
} }
}
}
return false; return false;
} }
@ -1760,6 +1933,8 @@ namespace graphene { namespace net { namespace detail {
("max", _maximum_number_of_connections)); ("max", _maximum_number_of_connections));
dlog(" my id is ${id}", ("id", _node_id)); dlog(" my id is ${id}", ("id", _node_id));
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& active_connection : _active_connections) for (const peer_connection_ptr& active_connection : _active_connections)
{ {
dlog(" active: ${endpoint} with ${id} [${direction}]", dlog(" active: ${endpoint} with ${id} [${direction}]",
@ -1767,6 +1942,9 @@ namespace graphene { namespace net { namespace detail {
("id", active_connection->node_id) ("id", active_connection->node_id)
("direction", active_connection->direction)); ("direction", active_connection->direction));
} }
}
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for (const peer_connection_ptr& handshaking_connection : _handshaking_connections) for (const peer_connection_ptr& handshaking_connection : _handshaking_connections)
{ {
dlog(" handshaking: ${endpoint} with ${id} [${direction}]", dlog(" handshaking: ${endpoint} with ${id} [${direction}]",
@ -1775,6 +1953,7 @@ namespace graphene { namespace net { namespace detail {
("direction", handshaking_connection->direction)); ("direction", handshaking_connection->direction));
} }
} }
}
void node_impl::on_message( peer_connection* originating_peer, const message& received_message ) void node_impl::on_message( peer_connection* originating_peer, const message& received_message )
{ {
@ -2219,6 +2398,7 @@ namespace graphene { namespace net { namespace detail {
if (!_peer_advertising_disabled) if (!_peer_advertising_disabled)
{ {
reply.addresses.reserve(_active_connections.size()); reply.addresses.reserve(_active_connections.size());
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& active_peer : _active_connections) for (const peer_connection_ptr& active_peer : _active_connections)
{ {
fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*active_peer->get_remote_endpoint()); fc::optional<potential_peer_record> updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*active_peer->get_remote_endpoint());
@ -2404,12 +2584,15 @@ namespace graphene { namespace net { namespace detail {
{ {
VERIFY_CORRECT_THREAD(); VERIFY_CORRECT_THREAD();
uint32_t max_number_of_unfetched_items = 0; uint32_t max_number_of_unfetched_items = 0;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
{ {
uint32_t this_peer_number_of_unfetched_items = (uint32_t)peer->ids_of_items_to_get.size() + peer->number_of_unfetched_item_ids; uint32_t this_peer_number_of_unfetched_items = (uint32_t)peer->ids_of_items_to_get.size() + peer->number_of_unfetched_item_ids;
max_number_of_unfetched_items = std::max(max_number_of_unfetched_items, max_number_of_unfetched_items = std::max(max_number_of_unfetched_items,
this_peer_number_of_unfetched_items); this_peer_number_of_unfetched_items);
} }
}
return max_number_of_unfetched_items; return max_number_of_unfetched_items;
} }
@ -2623,17 +2806,19 @@ namespace graphene { namespace net { namespace detail {
originating_peer->ids_of_items_to_get.empty()) originating_peer->ids_of_items_to_get.empty())
{ {
bool is_first_item_for_other_peer = false; bool is_first_item_for_other_peer = false;
for (const peer_connection_ptr& peer : _active_connections) {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) {
if (peer != originating_peer->shared_from_this() && if (peer != originating_peer->shared_from_this() &&
!peer->ids_of_items_to_get.empty() && !peer->ids_of_items_to_get.empty() &&
peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front()) peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front()) {
{
dlog("The item ${newitem} is the first item for peer ${peer}", dlog("The item ${newitem} is the first item for peer ${peer}",
("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front()) ("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front())("peer", peer->get_remote_endpoint()));
("peer", peer->get_remote_endpoint()));
is_first_item_for_other_peer = true; is_first_item_for_other_peer = true;
break; break;
} }
}
}
dlog("is_first_item_for_other_peer: ${is_first}. item_hashes_received.size() = ${size}", dlog("is_first_item_for_other_peer: ${is_first}. item_hashes_received.size() = ${size}",
("is_first", is_first_item_for_other_peer)("size", item_hashes_received.size())); ("is_first", is_first_item_for_other_peer)("size", item_hashes_received.size()));
if (!is_first_item_for_other_peer) if (!is_first_item_for_other_peer)
@ -2923,6 +3108,8 @@ namespace graphene { namespace net { namespace detail {
item_id advertised_item_id(item_ids_inventory_message_received.item_type, item_hash); item_id advertised_item_id(item_ids_inventory_message_received.item_type, item_hash);
bool we_advertised_this_item_to_a_peer = false; bool we_advertised_this_item_to_a_peer = false;
bool we_requested_this_item_from_a_peer = false; bool we_requested_this_item_from_a_peer = false;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr peer : _active_connections) for (const peer_connection_ptr peer : _active_connections)
{ {
if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end()) if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end())
@ -2933,6 +3120,7 @@ namespace graphene { namespace net { namespace detail {
if (peer->items_requested_from_peer.find(advertised_item_id) != peer->items_requested_from_peer.end()) if (peer->items_requested_from_peer.find(advertised_item_id) != peer->items_requested_from_peer.end())
we_requested_this_item_from_a_peer = true; we_requested_this_item_from_a_peer = true;
} }
}
// if we have already advertised it to a peer, we must have it, no need to do anything else // if we have already advertised it to a peer, we must have it, no need to do anything else
if (!we_advertised_this_item_to_a_peer) if (!we_advertised_this_item_to_a_peer)
@ -3162,6 +3350,7 @@ namespace graphene { namespace net { namespace detail {
}; };
bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num()); bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num());
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -3244,6 +3433,7 @@ namespace graphene { namespace net { namespace detail {
else else
{ {
// invalid message received // invalid message received
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -3346,6 +3536,8 @@ namespace graphene { namespace net { namespace detail {
// find out if this block is the next block on the active chain or one of the forks // find out if this block is the next block on the active chain or one of the forks
bool potential_first_block = false; bool potential_first_block = false;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -3357,6 +3549,7 @@ namespace graphene { namespace net { namespace detail {
peer->ids_of_items_being_processed.insert(received_block_iter->block_id); peer->ids_of_items_being_processed.insert(received_block_iter->block_id);
} }
} }
}
// if it is, process it, remove it from all sync peers lists // if it is, process it, remove it from all sync peers lists
if (potential_first_block) if (potential_first_block)
@ -3382,6 +3575,7 @@ namespace graphene { namespace net { namespace detail {
{ {
dlog("Already received and accepted this block (presumably through normal inventory mechanism), treating it as accepted"); dlog("Already received and accepted this block (presumably through normal inventory mechanism), treating it as accepted");
std::vector< peer_connection_ptr > peers_needing_next_batch; std::vector< peer_connection_ptr > peers_needing_next_batch;
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
auto items_being_processed_iter = peer->ids_of_items_being_processed.find(received_block_iter->block_id); auto items_being_processed_iter = peer->ids_of_items_being_processed.find(received_block_iter->block_id);
@ -3517,6 +3711,8 @@ namespace graphene { namespace net { namespace detail {
fc::time_point_sec block_time = block_message_to_process.block.timestamp; fc::time_point_sec block_time = block_message_to_process.block.timestamp;
bool disconnect_this_peer = false; bool disconnect_this_peer = false;
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -3533,10 +3729,14 @@ namespace graphene { namespace net { namespace detail {
} }
peer->clear_old_inventory(); peer->clear_old_inventory();
} }
}
message_propagation_data propagation_data{message_receive_time, message_validated_time, originating_peer->node_id}; message_propagation_data propagation_data{message_receive_time, message_validated_time, originating_peer->node_id};
broadcast( block_message_to_process, propagation_data ); broadcast( block_message_to_process, propagation_data );
_message_cache.block_accepted(); _message_cache.block_accepted();
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
if (is_hard_fork_block(block_number) ) if (is_hard_fork_block(block_number) )
@ -3568,6 +3768,7 @@ namespace graphene { namespace net { namespace detail {
#endif #endif
} }
} }
}
if(rejecting_block_due_hf) if(rejecting_block_due_hf)
{ {
@ -3604,10 +3805,12 @@ namespace graphene { namespace net { namespace detail {
disconnect_reason = "You offered me a block that I have deemed to be invalid"; disconnect_reason = "You offered me a block that I have deemed to be invalid";
peers_to_disconnect.insert( originating_peer->shared_from_this() ); peers_to_disconnect.insert( originating_peer->shared_from_this() );
for (const peer_connection_ptr& peer : _active_connections) fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) {
if (!peer->ids_of_items_to_get.empty() && peer->ids_of_items_to_get.front() == block_message_to_process.block_id) if (!peer->ids_of_items_to_get.empty() && peer->ids_of_items_to_get.front() == block_message_to_process.block_id)
peers_to_disconnect.insert(peer); peers_to_disconnect.insert(peer);
} }
}
if (restart_sync_exception) if (restart_sync_exception)
{ {
@ -3727,6 +3930,8 @@ namespace graphene { namespace net { namespace detail {
void node_impl::forward_firewall_check_to_next_available_peer(firewall_check_state_data* firewall_check_state) void node_impl::forward_firewall_check_to_next_available_peer(firewall_check_state_data* firewall_check_state)
{ {
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
if (firewall_check_state->expected_node_id != peer->node_id && // it's not the node who is asking us to test if (firewall_check_state->expected_node_id != peer->node_id && // it's not the node who is asking us to test
@ -3746,6 +3951,8 @@ namespace graphene { namespace net { namespace detail {
return; return;
} }
} }
}
wlog("Unable to forward firewall check for node ${to_check} to any other peers, returning 'unable'", wlog("Unable to forward firewall check for node ${to_check} to any other peers, returning 'unable'",
("to_check", firewall_check_state->endpoint_to_test)); ("to_check", firewall_check_state->endpoint_to_test));
@ -3918,6 +4125,8 @@ namespace graphene { namespace net { namespace detail {
} }
fc::time_point now = fc::time_point::now(); fc::time_point now = fc::time_point::now();
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -3953,6 +4162,8 @@ namespace graphene { namespace net { namespace detail {
data_for_this_peer.user_data = user_data; data_for_this_peer.user_data = user_data;
reply.current_connections.emplace_back(data_for_this_peer); reply.current_connections.emplace_back(data_for_this_peer);
} }
}
originating_peer->send_message(reply); originating_peer->send_message(reply);
} }
@ -4037,6 +4248,7 @@ namespace graphene { namespace net { namespace detail {
void node_impl::start_synchronizing() void node_impl::start_synchronizing()
{ {
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
start_synchronizing_with_peer( peer ); start_synchronizing_with_peer( peer );
} }
@ -4243,9 +4455,19 @@ namespace graphene { namespace net { namespace detail {
// the read loop before it gets an EOF). // the read loop before it gets an EOF).
// operate off copies of the lists in case they change during iteration // operate off copies of the lists in case they change during iteration
std::list<peer_connection_ptr> all_peers; std::list<peer_connection_ptr> all_peers;
boost::push_back(all_peers, _active_connections); auto p_back = [&all_peers](const peer_connection_ptr& conn) { all_peers.push_back(conn); };
boost::push_back(all_peers, _handshaking_connections); {
boost::push_back(all_peers, _closing_connections); fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
std::for_each(_active_connections.begin(), _active_connections.end(), p_back);
}
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
std::for_each(_handshaking_connections.begin(), _handshaking_connections.end(), p_back);
}
{
fc::scoped_lock<fc::mutex> lock(_closing_connections.get_mutex());
std::for_each(_closing_connections.begin(), _closing_connections.end(), p_back);
}
for (const peer_connection_ptr& peer : all_peers) for (const peer_connection_ptr& peer : all_peers)
{ {
@ -4511,9 +4733,7 @@ namespace graphene { namespace net { namespace detail {
// whether the peer is firewalled, we want to disconnect now. // whether the peer is firewalled, we want to disconnect now.
_handshaking_connections.erase(new_peer); _handshaking_connections.erase(new_peer);
_terminating_connections.erase(new_peer); _terminating_connections.erase(new_peer);
assert(_active_connections.find(new_peer) == _active_connections.end());
_active_connections.erase(new_peer); _active_connections.erase(new_peer);
assert(_closing_connections.find(new_peer) == _closing_connections.end());
_closing_connections.erase(new_peer); _closing_connections.erase(new_peer);
display_current_connections(); display_current_connections();
@ -4757,6 +4977,68 @@ namespace graphene { namespace net { namespace detail {
_potential_peer_db.update_entry(updated_peer_record); _potential_peer_db.update_entry(updated_peer_record);
trigger_p2p_network_connect_loop(); trigger_p2p_network_connect_loop();
} }
void node_impl::add_seed_node(const std::string& endpoint_string)
{
VERIFY_CORRECT_THREAD();
_seed_nodes.insert( endpoint_string );
resolve_seed_node_and_add( endpoint_string );
}
void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string)
{
VERIFY_CORRECT_THREAD();
std::vector<fc::ip::endpoint> endpoints;
ilog("Resolving seed node ${endpoint}", ("endpoint", endpoint_string));
try
{
endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string);
}
catch(...)
{
wlog( "Unable to resolve endpoint during attempt to add seed node ${ep}", ("ep", endpoint_string) );
}
for (const fc::ip::endpoint& endpoint : endpoints)
{
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
add_node(endpoint);
}
}
void node_impl::update_seed_nodes_task()
{
VERIFY_CORRECT_THREAD();
try
{
dlog("Starting an iteration of update_seed_nodes loop.");
for( const std::string& endpoint_string : _seed_nodes )
{
resolve_seed_node_and_add( endpoint_string );
}
dlog("Done an iteration of update_seed_nodes loop.");
}
catch (const fc::canceled_exception&)
{
ilog( "update_seed_nodes_task canceled" );
throw;
}
FC_CAPTURE_AND_LOG( (_seed_nodes) )
schedule_next_update_seed_nodes_task();
}
void node_impl::schedule_next_update_seed_nodes_task()
{
VERIFY_CORRECT_THREAD();
if( _node_is_shutting_down )
return;
if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() )
return;
_update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); },
fc::time_point::now() + fc::hours(3),
"update_seed_nodes_loop" );
}
void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer) void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer)
{ {
@ -4795,18 +5077,25 @@ namespace graphene { namespace net { namespace detail {
peer_connection_ptr node_impl::get_connection_to_endpoint( const fc::ip::endpoint& remote_endpoint ) peer_connection_ptr node_impl::get_connection_to_endpoint( const fc::ip::endpoint& remote_endpoint )
{ {
VERIFY_CORRECT_THREAD(); VERIFY_CORRECT_THREAD();
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& active_peer : _active_connections ) for( const peer_connection_ptr& active_peer : _active_connections )
{ {
fc::optional<fc::ip::endpoint> endpoint_for_this_peer( active_peer->get_remote_endpoint() ); fc::optional<fc::ip::endpoint> endpoint_for_this_peer( active_peer->get_remote_endpoint() );
if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint )
return active_peer; return active_peer;
} }
}
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for( const peer_connection_ptr& handshaking_peer : _handshaking_connections ) for( const peer_connection_ptr& handshaking_peer : _handshaking_connections )
{ {
fc::optional<fc::ip::endpoint> endpoint_for_this_peer( handshaking_peer->get_remote_endpoint() ); fc::optional<fc::ip::endpoint> endpoint_for_this_peer( handshaking_peer->get_remote_endpoint() );
if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint )
return handshaking_peer; return handshaking_peer;
} }
}
return peer_connection_ptr(); return peer_connection_ptr();
} }
@ -4850,6 +5139,8 @@ namespace graphene { namespace net { namespace detail {
ilog( " number of peers: ${active} active, ${handshaking}, ${closing} closing. attempting to maintain ${desired} - ${maximum} peers", ilog( " number of peers: ${active} active, ${handshaking}, ${closing} closing. attempting to maintain ${desired} - ${maximum} peers",
( "active", _active_connections.size() )("handshaking", _handshaking_connections.size() )("closing",_closing_connections.size() ) ( "active", _active_connections.size() )("handshaking", _handshaking_connections.size() )("closing",_closing_connections.size() )
( "desired", _desired_number_of_connections )("maximum", _maximum_number_of_connections ) ); ( "desired", _desired_number_of_connections )("maximum", _maximum_number_of_connections ) );
{
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
{ {
ilog( " active peer ${endpoint} peer_is_in_sync_with_us:${in_sync_with_us} we_are_in_sync_with_peer:${in_sync_with_them}", ilog( " active peer ${endpoint} peer_is_in_sync_with_us:${in_sync_with_us} we_are_in_sync_with_peer:${in_sync_with_them}",
@ -4861,11 +5152,15 @@ namespace graphene { namespace net { namespace detail {
ilog( " we are not fetching sync blocks from the above peer (inhibit_fetching_sync_blocks == true)" ); ilog( " we are not fetching sync blocks from the above peer (inhibit_fetching_sync_blocks == true)" );
} }
}
{
fc::scoped_lock<fc::mutex> lock(_handshaking_connections.get_mutex());
for( const peer_connection_ptr& peer : _handshaking_connections ) for( const peer_connection_ptr& peer : _handshaking_connections )
{ {
ilog( " handshaking peer ${endpoint} in state ours(${our_state}) theirs(${their_state})", ilog( " handshaking peer ${endpoint} in state ours(${our_state}) theirs(${their_state})",
( "endpoint", peer->get_remote_endpoint() )("our_state", peer->our_state )("their_state", peer->their_state ) ); ( "endpoint", peer->get_remote_endpoint() )("our_state", peer->our_state )("their_state", peer->their_state ) );
} }
}
ilog( "--------- MEMORY USAGE ------------" ); ilog( "--------- MEMORY USAGE ------------" );
ilog( "node._active_sync_requests size: ${size}", ("size", _active_sync_requests.size() ) ); ilog( "node._active_sync_requests size: ${size}", ("size", _active_sync_requests.size() ) );
@ -4874,6 +5169,7 @@ namespace graphene { namespace net { namespace detail {
ilog( "node._items_to_fetch size: ${size}", ("size", _items_to_fetch.size() ) ); ilog( "node._items_to_fetch size: ${size}", ("size", _items_to_fetch.size() ) );
ilog( "node._new_inventory size: ${size}", ("size", _new_inventory.size() ) ); ilog( "node._new_inventory size: ${size}", ("size", _new_inventory.size() ) );
ilog( "node._message_cache size: ${size}", ("size", _message_cache.size() ) ); ilog( "node._message_cache size: ${size}", ("size", _message_cache.size() ) );
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for( const peer_connection_ptr& peer : _active_connections ) for( const peer_connection_ptr& peer : _active_connections )
{ {
ilog( " peer ${endpoint}", ("endpoint", peer->get_remote_endpoint() ) ); ilog( " peer ${endpoint}", ("endpoint", peer->get_remote_endpoint() ) );
@ -4971,6 +5267,7 @@ namespace graphene { namespace net { namespace detail {
{ {
VERIFY_CORRECT_THREAD(); VERIFY_CORRECT_THREAD();
std::vector<peer_status> statuses; std::vector<peer_status> statuses;
fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr& peer : _active_connections) for (const peer_connection_ptr& peer : _active_connections)
{ {
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
@ -5161,10 +5458,13 @@ namespace graphene { namespace net { namespace detail {
_allowed_peers.clear(); _allowed_peers.clear();
_allowed_peers.insert(allowed_peers.begin(), allowed_peers.end()); _allowed_peers.insert(allowed_peers.begin(), allowed_peers.end());
std::list<peer_connection_ptr> peers_to_disconnect; std::list<peer_connection_ptr> peers_to_disconnect;
if (!_allowed_peers.empty()) if (!_allowed_peers.empty()) {
for (const peer_connection_ptr& peer : _active_connections) fc::scoped_lock<fc::mutex> lock(_active_connections.get_mutex());
for (const peer_connection_ptr &peer : _active_connections) {
if (_allowed_peers.find(peer->node_id) == _allowed_peers.end()) if (_allowed_peers.find(peer->node_id) == _allowed_peers.end())
peers_to_disconnect.push_back(peer); peers_to_disconnect.push_back(peer);
}
}
for (const peer_connection_ptr& peer : peers_to_disconnect) for (const peer_connection_ptr& peer : peers_to_disconnect)
disconnect_from_peer(peer.get(), "My allowed_peers list has changed, and you're no longer allowed. Bye."); disconnect_from_peer(peer.get(), "My allowed_peers list has changed, and you're no longer allowed. Bye.");
#endif // ENABLE_P2P_DEBUGGING_API #endif // ENABLE_P2P_DEBUGGING_API
@ -5296,6 +5596,11 @@ namespace graphene { namespace net { namespace detail {
INVOKE_IN_IMPL(add_node, ep); INVOKE_IN_IMPL(add_node, ep);
} }
void node::add_seed_node(const std::string& in)
{
INVOKE_IN_IMPL(add_seed_node, in);
}
void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint ) void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint )
{ {
INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint); INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint);
@ -5677,5 +5982,45 @@ namespace graphene { namespace net { namespace detail {
#undef INVOKE_AND_COLLECT_STATISTICS #undef INVOKE_AND_COLLECT_STATISTICS
} // end namespace detail } // end namespace detail
std::vector<fc::ip::endpoint> node::resolve_string_to_ip_endpoints(const std::string& in)
{
try
{
std::string::size_type colon_pos = in.find(':');
if (colon_pos == std::string::npos)
FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"",
("endpoint_string", in));
std::string port_string = in.substr(colon_pos + 1);
try
{
uint16_t port = boost::lexical_cast<uint16_t>(port_string);
std::string hostname = in.substr(0, colon_pos);
std::vector<fc::ip::endpoint> endpoints = fc::resolve(hostname, port);
if (endpoints.empty())
FC_THROW_EXCEPTION( fc::unknown_host_exception,
"The host name can not be resolved: ${hostname}",
("hostname", hostname) );
return endpoints;
}
catch (const boost::bad_lexical_cast&)
{
FC_THROW("Bad port: ${port}", ("port", port_string));
}
}
FC_CAPTURE_AND_RETHROW((in))
}
void node::add_seed_nodes(std::vector<std::string> seeds)
{
for(const std::string& endpoint_string : seeds )
{
try {
add_seed_node(endpoint_string);
} catch( const fc::exception& e ) {
wlog( "caught exception ${e} while adding seed node ${endpoint}",
("e", e.to_detail_string())("endpoint", endpoint_string) );
}
}
}
} } // end namespace graphene::net } } // end namespace graphene::net

View file

@ -50,7 +50,8 @@ namespace graphene { namespace net {
indexed_by<ordered_non_unique<tag<last_seen_time_index>, indexed_by<ordered_non_unique<tag<last_seen_time_index>,
member<potential_peer_record, member<potential_peer_record,
fc::time_point_sec, fc::time_point_sec,
&potential_peer_record::last_seen_time> >, &potential_peer_record::last_seen_time>,
std::greater<fc::time_point_sec> >,
hashed_unique<tag<endpoint_index>, hashed_unique<tag<endpoint_index>,
member<potential_peer_record, member<potential_peer_record,
fc::ip::endpoint, fc::ip::endpoint,

View file

@ -85,6 +85,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
vector<optional< operation_history_object > >& hist = db.get_applied_operations(); vector<optional< operation_history_object > >& hist = db.get_applied_operations();
bool is_first = true; bool is_first = true;
auto skip_oho_id = [&is_first,&db,this]() { auto skip_oho_id = [&is_first,&db,this]() {
const std::lock_guard<std::mutex> undo_db_lock{db._undo_db_mutex};
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
{ {
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) ); db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );

View file

@ -16,3 +16,4 @@ install( TARGETS
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib
) )
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/debug_witness" )

View file

@ -34,7 +34,9 @@ class debug_api_impl
}; };
debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app ) debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app )
{} {
// Nothing else to do
}
void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count ) void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count )

View file

@ -38,7 +38,10 @@ using std::vector;
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
debug_witness_plugin::~debug_witness_plugin() {} debug_witness_plugin::~debug_witness_plugin()
{
cleanup();
}
void debug_witness_plugin::plugin_set_program_options( void debug_witness_plugin::plugin_set_program_options(
boost::program_options::options_description& command_line_options, boost::program_options::options_description& command_line_options,
@ -62,7 +65,7 @@ void debug_witness_plugin::plugin_initialize(const boost::program_options::varia
ilog("debug_witness plugin: plugin_initialize() begin"); ilog("debug_witness plugin: plugin_initialize() begin");
_options = &options; _options = &options;
if( options.count("debug-private-key") ) if( options.count("debug-private-key") > 0 )
{ {
const std::vector<std::string> key_id_to_wif_pair_strings = options["debug-private-key"].as<std::vector<std::string>>(); const std::vector<std::string> key_id_to_wif_pair_strings = options["debug-private-key"].as<std::vector<std::string>>();
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
@ -100,7 +103,6 @@ void debug_witness_plugin::plugin_startup()
_changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_changed_objects(ids, impacted_accounts); }); _changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_changed_objects(ids, impacted_accounts); });
_removed_objects_conn = db.removed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*>& objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); }); _removed_objects_conn = db.removed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*>& objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); });
return;
} }
void debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts ) void debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts )
@ -155,11 +157,15 @@ void debug_witness_plugin::flush_json_object_stream()
} }
void debug_witness_plugin::plugin_shutdown() void debug_witness_plugin::plugin_shutdown()
{
cleanup();
}
void debug_witness_plugin::cleanup()
{ {
if( _json_object_stream ) if( _json_object_stream )
{ {
_json_object_stream->close(); _json_object_stream->close();
_json_object_stream.reset(); _json_object_stream.reset();
} }
return;
} }

View file

@ -34,23 +34,25 @@ namespace graphene { namespace debug_witness_plugin {
class debug_witness_plugin : public graphene::app::plugin { class debug_witness_plugin : public graphene::app::plugin {
public: public:
~debug_witness_plugin(); using graphene::app::plugin::plugin;
~debug_witness_plugin() override;
std::string plugin_name()const override; std::string plugin_name()const override;
virtual void plugin_set_program_options( void plugin_set_program_options(
boost::program_options::options_description &command_line_options, boost::program_options::options_description &command_line_options,
boost::program_options::options_description &config_file_options boost::program_options::options_description &config_file_options
) override; ) override;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; void plugin_initialize( const boost::program_options::variables_map& options ) override;
virtual void plugin_startup() override; void plugin_startup() override;
virtual void plugin_shutdown() override; void plugin_shutdown() override;
void set_json_object_stream( const std::string& filename ); void set_json_object_stream( const std::string& filename );
void flush_json_object_stream(); void flush_json_object_stream();
private: private:
void cleanup();
void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts ); void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
void on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts ); void on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
@ -58,7 +60,7 @@ private:
boost::program_options::variables_map _options; boost::program_options::variables_map _options;
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys; std::map<chain::public_key_type, fc::ecc::private_key, chain::pubkey_comparator> _private_keys;
std::shared_ptr< std::ofstream > _json_object_stream; std::shared_ptr< std::ofstream > _json_object_stream;
boost::signals2::scoped_connection _applied_block_conn; boost::signals2::scoped_connection _applied_block_conn;

View file

@ -63,8 +63,24 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c
void delayed_node_plugin::connect() void delayed_node_plugin::connect()
{ {
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); fc::http::websocket_connection_ptr con;
try
{
con = my->client.connect(my->remote_endpoint);
}
catch( const fc::exception& e )
{
wlog("Error while connecting: ${e}", ("e", e.to_detail_string()));
connection_failed();
return;
}
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(
con, GRAPHENE_NET_MAX_NESTED_OBJECTS );
my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0); my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0);
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
{
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
} );
my->client_connection_closed = my->client_connection->closed.connect([this] { my->client_connection_closed = my->client_connection->closed.connect([this] {
connection_failed(); connection_failed();
}); });
@ -73,7 +89,9 @@ void delayed_node_plugin::connect()
void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options) void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{ {
FC_ASSERT(options.count("trusted-node") > 0); FC_ASSERT(options.count("trusted-node") > 0);
ilog("delayed_node_plugin: plugin_initialize() begin");
my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>(); my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>();
ilog("delayed_node_plugin: plugin_initialize() end");
} }
void delayed_node_plugin::sync_with_trusted_node() void delayed_node_plugin::sync_with_trusted_node()
@ -100,8 +118,11 @@ void delayed_node_plugin::sync_with_trusted_node()
while( remote_dpo.last_irreversible_block_num > db.head_block_num() ) while( remote_dpo.last_irreversible_block_num > db.head_block_num() )
{ {
fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 ); fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 );
// TODO: during sync, decouple requesting blocks from preprocessing + applying them
FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have.");
ilog("Pushing block #${n}", ("n", block->block_num())); ilog("Pushing block #${n}", ("n", block->block_num()));
// timur: failed to merge from bitshares, API n/a in peerplays
// db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait();
db.push_block(*block); db.push_block(*block);
synced_blocks++; synced_blocks++;
} }
@ -136,24 +157,12 @@ void delayed_node_plugin::plugin_startup()
mainloop(); mainloop();
}); });
try
{
connect(); connect();
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
{
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
} );
return;
}
catch (const fc::exception& e)
{
elog("Error during connection: ${e}", ("e", e.to_detail_string()));
}
fc::async([this]{connection_failed();});
} }
void delayed_node_plugin::connection_failed() void delayed_node_plugin::connection_failed()
{ {
my->last_received_remote_head = my->last_processed_remote_head;
elog("Connection to trusted node failed; retrying in 5 seconds..."); elog("Connection to trusted node failed; retrying in 5 seconds...");
fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5)); fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));
} }

View file

@ -127,6 +127,7 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b
const vector<optional< operation_history_object > >& hist = db.get_applied_operations(); const vector<optional< operation_history_object > >& hist = db.get_applied_operations();
bool is_first = true; bool is_first = true;
auto skip_oho_id = [&is_first,&db,this]() { auto skip_oho_id = [&is_first,&db,this]() {
const std::lock_guard<std::mutex> undo_db_lock{db._undo_db_mutex};
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
{ {
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) ); db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );

6
libraries/plugins/peerplays_sidechain/CMakeLists.txt Executable file → Normal file
View file

@ -3,7 +3,7 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp")
add_library( peerplays_sidechain add_library( peerplays_sidechain
peerplays_sidechain_plugin.cpp peerplays_sidechain_plugin.cpp
sidechain_api.cpp sidechain_api.cpp
sidechain_net_manager.cpp sidechain_net_handler_factory.cpp
sidechain_net_handler.cpp sidechain_net_handler.cpp
sidechain_net_handler_bitcoin.cpp sidechain_net_handler_bitcoin.cpp
sidechain_net_handler_ethereum.cpp sidechain_net_handler_ethereum.cpp
@ -16,6 +16,8 @@ add_library( peerplays_sidechain
bitcoin/segwit_addr.cpp bitcoin/segwit_addr.cpp
bitcoin/utils.cpp bitcoin/utils.cpp
bitcoin/sign_bitcoin_transaction.cpp bitcoin/sign_bitcoin_transaction.cpp
bitcoin/libbitcoin_client.cpp
bitcoin/estimate_fee_external.cpp
common/rpc_client.cpp common/rpc_client.cpp
common/utils.cpp common/utils.cpp
ethereum/encoders.cpp ethereum/encoders.cpp
@ -42,7 +44,7 @@ endif()
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq ) target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin sha3 zmq bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer )
target_include_directories( peerplays_sidechain target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -242,12 +242,12 @@ bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) {
} }
btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network ntype) { network ntype, payment_type type) {
network_type = ntype; network_type = ntype;
this->type = type;
create_redeem_script(keys_data); create_redeem_script(keys_data);
create_witness_script(); create_witness_script();
create_segwit_address(); create_segwit_address();
type = payment_type::P2WSH;
} }
void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) { void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() {
script_builder builder; script_builder builder;
builder << op::_0; builder << op::_0;
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
witness_script_ = builder; witness_script_ = builder;
} }
void btc_weighted_multisig_address::create_segwit_address() { void btc_weighted_multisig_address::create_segwit_address() {
std::string hrp; std::string hrp;
address_types byte_version;
switch (network_type) { switch (network_type) {
case (network::mainnet): case (network::mainnet):
hrp = "bc"; hrp = "bc";
byte_version = address_types::MAINNET_SCRIPT;
break; break;
case (network::testnet): case (network::testnet):
hrp = "tb"; hrp = "tb";
byte_version = address_types::TESTNET_SCRIPT;
break; break;
case (network::regtest): case (network::regtest):
hrp = "bcrt"; hrp = "bcrt";
byte_version = address_types::TESTNET_SCRIPT;
break; break;
} }
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size()); std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data); address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
wlog("Unsupported payment type of address");
}
} }
btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data,
@ -353,12 +370,12 @@ void btc_one_or_m_of_n_multisig_address::create_segwit_address() {
btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data,
const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
bitcoin_address::network ntype) { bitcoin_address::network ntype, payment_type type) {
network_type = ntype; network_type = ntype;
this->type = type;
create_redeem_script(user_key_data, keys_data); create_redeem_script(user_key_data, keys_data);
create_witness_script(); create_witness_script();
create_segwit_address(); create_segwit_address();
type = payment_type::P2WSH;
} }
void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) { void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() {
void btc_one_or_weighted_multisig_address::create_segwit_address() { void btc_one_or_weighted_multisig_address::create_segwit_address() {
std::string hrp; std::string hrp;
address_types byte_version;
switch (network_type) { switch (network_type) {
case (network::mainnet): case (network::mainnet):
byte_version = address_types::MAINNET_SCRIPT;
hrp = "bc"; hrp = "bc";
break; break;
case (network::testnet): case (network::testnet):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "tb"; hrp = "tb";
break; break;
case (network::regtest): case (network::regtest):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "bcrt"; hrp = "bcrt";
break; break;
} }
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size()); std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data); address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version test net
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
elog("Unsupported payment type of address");
}
} }
btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) : btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) :

View file

@ -0,0 +1,157 @@
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fc/log/logger.hpp>
#include <iostream>
namespace graphene {
namespace peerplays_sidechain {
static size_t writeFunction(void *ptr, size_t size, size_t nmemb, std::string *data) {
data->append((char *)ptr, size * nmemb);
return size * nmemb;
}
estimate_fee_external::estimate_fee_external() {
curl = curl_easy_init();
}
estimate_fee_external::~estimate_fee_external() {
curl_easy_cleanup(curl);
}
std::vector<std::pair<std::string, uint64_t>> estimate_fee_external::get_fee_external(uint16_t target_block) {
std::vector<std::pair<std::string, uint64_t>> estimate_fee_external_collection;
this->target_block = target_block;
for (auto &url_fee_parser : url_get_fee_parsers) {
response = get_response(url_fee_parser.first);
uint64_t fee = url_fee_parser.second();
std::string url_str = url_fee_parser.first;
if (fee != 0) {
estimate_fee_external_collection.emplace_back(std::make_pair(url_fee_parser.first, fee));
}
}
return estimate_fee_external_collection;
}
std::string estimate_fee_external::get_response(std::string url) {
std::string response;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.42.0");
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
}
return response;
}
uint64_t estimate_fee_external::parse_and_get_fee_1() {
//"https://www.bitgo.com/api/v2/btc/tx/fee"
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("feeByBlockTarget")) {
const auto &block_num = tx_child.first.data();
const auto &fee = tx_child.second.data();
founded_fee = std::stoi(fee);
if (std::stoi(block_num) >= target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_2() {
// https://bitcoiner.live/api/fees/estimates/latest
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("estimates")) {
const auto &time_str = tx_child.first.data();
auto time = std::stoi(time_str);
auto block_num = time / 30;
if (tx_child.second.count("sat_per_vbyte")) {
auto founded_fee_str = tx_child.second.get_child("sat_per_vbyte").data();
founded_fee = std::stoi(founded_fee_str) * 1000;
}
if (block_num >= target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_3() {
// https://api.blockchain.info/mempool/fees
if (response.empty()) {
return 0;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
if (response_pt.get_child("limits").count("min") && response_pt.get_child("limits").count("max")) {
auto limits_min_str = response_pt.get_child("limits.min").data();
auto limits_max_str = response_pt.get_child("limits.max").data();
auto limits_min = std::stoi(limits_min_str);
auto limits_max = std::stoi(limits_max_str);
auto priority_max = (limits_max - (limits_min - 1)) / 2;
if (response_pt.count("regular") && response_pt.count("priority")) {
auto regular_str = response_pt.get_child("regular").data();
auto priority_str = response_pt.get_child("priority").data();
auto regular = std::stoi(regular_str);
auto priority = std::stoi(priority_str);
if (target_block >= priority_max) {
return regular * 1000;
} else {
return priority * 1000;
}
}
}
return 0;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,227 @@
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
#include <system_error>
#include <bitcoin/explorer/config/transaction.hpp>
#include <bitcoin/system/config/hash256.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/log/logger.hpp>
namespace graphene { namespace peerplays_sidechain {
libbitcoin_client::libbitcoin_client(std::string url) :
obelisk_client(LIBBITCOIN_SERVER_TIMEOUT, LIBBITCOIN_SERVER_RETRIES) {
std::string reg_expr = "^((?P<Protocol>https|http|tcp):\\/\\/)?(?P<Host>[a-zA-Z0-9\\-\\.]+)(:(?P<Port>\\d{1,5}))?(?P<Target>\\/.+)?";
boost::xpressive::sregex sr = boost::xpressive::sregex::compile(reg_expr);
boost::xpressive::smatch sm;
if (boost::xpressive::regex_search(url, sm, sr)) {
protocol = sm["Protocol"];
if (protocol.empty()) {
protocol = "tcp";
}
host = sm["Host"];
if (host.empty()) {
host + "localhost";
}
port = sm["Port"];
if (port.empty()) {
port = "9091";
}
}
uint16_t port_num = std::stoi(port);
std::string final_url = protocol + "://" + host;
libbitcoin::config::endpoint address(final_url, port_num);
libbitcoin::client::connection_type connection;
connection.retries = LIBBITCOIN_SERVER_RETRIES;
connection.server = address;
if (!obelisk_client.connect(connection)) {
elog("Can't connect libbitcoin for url: ${url}", ("url", final_url));
}
is_connected = true;
}
std::string libbitcoin_client::send_transaction(std::string tx) {
std::string res;
auto error_handler = [&](const std::error_code &ec) {
elog("error on sending bitcoin transaction ${error_code}", ("error_code", ec.message()));
};
auto result_handler = [&](libbitcoin::code result_code) {
ilog("result code on sending transaction ${result_code}", ("result_code", result_code.message()));
res = std::to_string(result_code.value());
};
libbitcoin::explorer::config::transaction transaction(tx);
libbitcoin::chain::transaction trx;
// This validates the tx, submits it to local tx pool, and notifies peers.
obelisk_client.transaction_pool_broadcast(error_handler, result_handler, transaction);
obelisk_client.wait();
return res;
}
libbitcoin::chain::output::list libbitcoin_client::get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions) {
libbitcoin::chain::output::list outs;
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetch_trx_by_hash: ${hash} ${error_code}", ("hash", tx_id)("error_code", ec.message()));
};
auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) {
tx_hash = libbitcoin::config::hash256(tx_handler.hash(false)).to_string();
// TODO try to find this value (confirmitions)
confirmitions = 1;
outs = tx_handler.outputs();
};
libbitcoin::hash_digest hash = libbitcoin::config::hash256(tx_id);
// obelisk_client.blockchain_fetch_transaction (error_handler, transaction_handler,hash);
obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, hash);
obelisk_client.wait();
return outs;
}
std::vector<list_unspent_replay> libbitcoin_client::listunspent(std::string address, double amount) {
std::vector<list_unspent_replay> result;
auto error_handler = [&](const std::error_code &ec) {
elog("error on list_unspent ${error_code}", ("error_code", ec.message()));
};
auto replay_handler = [&](const libbitcoin::chain::points_value &points) {
for (auto &point : points.points) {
list_unspent_replay output;
output.hash = libbitcoin::config::hash256(point.hash()).to_string();
output.value = point.value();
output.index = point.index();
result.emplace_back(output);
}
};
libbitcoin::wallet::payment_address payment_address(address);
uint64_t satoshi = 100000000 * amount;
obelisk_client.blockchain_fetch_unspent_outputs(error_handler,
replay_handler, payment_address, satoshi, libbitcoin::wallet::select_outputs::algorithm::individual);
obelisk_client.wait();
return result;
}
bool libbitcoin_client::get_is_test_net() {
bool result = false;
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetching genesis block ${error_code}", ("error_code", ec.message()));
};
auto block_header_handler = [&](const libbitcoin::chain::header &block_header) {
std::string hash_str = libbitcoin::config::hash256(block_header.hash()).to_string();
if (hash_str == GENESIS_TESTNET_HASH || hash_str == GENESIS_REGTEST_HASH) {
result = true;
}
};
obelisk_client.blockchain_fetch_block_header(error_handler, block_header_handler, 0);
obelisk_client.wait();
return result;
}
uint64_t libbitcoin_client::get_fee_from_trx(libbitcoin::chain::transaction trx) {
bool general_fee_est_error = false;
if (trx.is_coinbase()) {
return 0;
}
const auto total_output_value = trx.total_output_value();
// get the inputs and from inputs previous outputs
std::map<libbitcoin::hash_digest, std::vector<uint32_t>> prev_out_trxs;
for (auto &ins : trx.inputs()) {
const auto &prev_out = ins.previous_output();
prev_out_trxs[prev_out.hash()].emplace_back(prev_out.index());
}
// fetch the trx to get total input value
uint64_t total_input_value = 0;
auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) {
std::vector<uint32_t> indexes = prev_out_trxs[tx_handler.hash()];
for (auto &index : indexes) {
total_input_value += tx_handler.outputs()[index].value();
}
};
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetching trx ${error_code}", ("error_code", ec.message()));
general_fee_est_error = true;
};
for (const auto &iter : prev_out_trxs) {
if (general_fee_est_error) {
break;
}
obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, iter.first);
obelisk_client.wait();
}
if (total_input_value >= total_output_value) {
return total_input_value - total_output_value;
} else {
// something is really wrong if this happens,so we are going to mark as an error
elog("On fee estimation something is wrong in total inputs and total outputs for trx hash: ${hash}",
("hash", libbitcoin::config::hash256(trx.hash()).to_string()));
return 0;
}
}
uint64_t libbitcoin_client::get_average_fee_from_trxs(std::vector<libbitcoin::chain::transaction> trx_list) {
std::vector<uint64_t> fee_per_trxs;
for (auto &trx : trx_list) {
uint64_t fee = get_fee_from_trx(trx);
if (fee > 0) {
fee_per_trxs.emplace_back(fee);
}
}
uint64_t average_estimated_fee = 0;
if (fee_per_trxs.size()) {
for (const auto &fee : fee_per_trxs) {
average_estimated_fee += fee;
}
average_estimated_fee /= fee_per_trxs.size();
}
return average_estimated_fee;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -18,10 +18,40 @@
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : struct rpc_reply {
url(_url), uint16_t status;
user(_user), std::string body;
password(_password), };
class rpc_connection {
public:
rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls);
std::string send_post_request(std::string method, std::string params, bool show_log);
std::string get_url() const;
protected:
rpc_credentials credentials;
bool debug_rpc_calls;
std::string protocol;
std::string host;
std::string port;
std::string target;
std::string authorization;
uint32_t request_id;
private:
rpc_reply send_post_request(std::string body, bool show_log);
boost::beast::net::io_context ioc;
boost::beast::net::ip::tcp::resolver resolver;
boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> results;
};
rpc_connection::rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls) :
credentials(_credentials),
debug_rpc_calls(_debug_rpc_calls), debug_rpc_calls(_debug_rpc_calls),
request_id(0), request_id(0),
resolver(ioc) { resolver(ioc) {
@ -31,7 +61,7 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor
boost::xpressive::smatch sm; boost::xpressive::smatch sm;
if (boost::xpressive::regex_search(url, sm, sr)) { if (boost::xpressive::regex_search(credentials.url, sm, sr)) {
protocol = sm["Protocol"]; protocol = sm["Protocol"];
if (protocol.empty()) { if (protocol.empty()) {
protocol = "http"; protocol = "http";
@ -52,16 +82,26 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor
target = "/"; target = "/";
} }
authorization = "Basic " + base64_encode(user + ":" + password); authorization = "Basic " + base64_encode(credentials.user + ":" + credentials.password);
results = resolver.resolve(host, port); results = resolver.resolve(host, port);
} else { } else {
elog("Invalid URL: ${url}", ("url", url)); elog("Invalid URL: ${url}", ("url", credentials.url));
} }
} }
std::string rpc_connection::get_url() const {
return credentials.url;
}
std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) { std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) {
if (reply_str.empty()) {
wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__));
return "";
}
try {
std::stringstream ss(reply_str); std::stringstream ss(reply_str);
boost::property_tree::ptree json; boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json); boost::property_tree::read_json(ss, json);
@ -85,11 +125,20 @@ std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, s
} }
} }
} }
} catch (const boost::property_tree::json_parser::json_parser_error &e) {
wlog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what()));
}
return ""; return "";
} }
std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::string value_path) { std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::string value_path) {
if (reply_str.empty()) {
wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__));
return "";
}
try {
std::stringstream ss(reply_str); std::stringstream ss(reply_str);
boost::property_tree::ptree json; boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json); boost::property_tree::read_json(ss, json);
@ -103,9 +152,14 @@ std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::st
} }
return json.get<std::string>("result"); return json.get<std::string>("result");
} catch (const boost::property_tree::json_parser::json_parser_error &e) {
wlog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what()));
} }
std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { return "";
}
std::string rpc_connection::send_post_request(std::string method, std::string params, bool show_log) {
std::stringstream body; std::stringstream body;
request_id = request_id + 1; request_id = request_id + 1;
@ -118,6 +172,7 @@ std::string rpc_client::send_post_request(std::string method, std::string params
body << " }"; body << " }";
try {
const auto reply = send_post_request(body.str(), show_log); const auto reply = send_post_request(body.str(), show_log);
if (reply.body.empty()) { if (reply.body.empty()) {
@ -136,11 +191,14 @@ std::string rpc_client::send_post_request(std::string method, std::string params
if (reply.status == 200) { if (reply.status == 200) {
return ss.str(); return ss.str();
} }
} catch (const boost::system::system_error &e) {
elog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what()));
}
return ""; return "";
} }
rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { rpc_reply rpc_connection::send_post_request(std::string body, bool show_log) {
// These object is used as a context for ssl connection // These object is used as a context for ssl connection
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client); boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client);
@ -215,7 +273,7 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) {
reply.body = rbody; reply.body = rbody;
if (show_log) { if (show_log) {
ilog("### Request URL: ${url}", ("url", url)); ilog("### Request URL: ${url}", ("url", credentials.url));
ilog("### Request: ${body}", ("body", body)); ilog("### Request: ${body}", ("body", body));
ilog("### Response: ${rbody}", ("rbody", rbody)); ilog("### Response: ${rbody}", ("rbody", rbody));
} }
@ -223,4 +281,113 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) {
return reply; return reply;
} }
rpc_client::rpc_client(sidechain_type _sidechain, const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) :
sidechain(_sidechain),
debug_rpc_calls(_debug_rpc_calls),
simulate_connection_reselection(_simulate_connection_reselection) {
FC_ASSERT(_credentials.size());
for (size_t i = 0; i < _credentials.size(); i++)
connections.push_back(new rpc_connection(_credentials[i], _debug_rpc_calls));
n_active_conn = 0;
if (connections.size() > 1)
schedule_connection_selection();
}
void rpc_client::schedule_connection_selection() {
fc::time_point now = fc::time_point::now();
static const int64_t time_to_next_conn_selection = 10 * 1000 * 1000; // 10 sec
fc::time_point next_wakeup = now + fc::microseconds(time_to_next_conn_selection);
connection_selection_task = fc::schedule([this] {
select_connection();
},
next_wakeup, "SON RPC connection selection");
}
void rpc_client::select_connection() {
FC_ASSERT(connections.size() > 1);
const std::lock_guard<std::mutex> lock(conn_mutex);
static const int t_limit = 5 * 1000 * 1000, // 5 sec
quality_diff_threshold = 10 * 1000; // 10 ms
int best_n = -1;
int best_quality = -1;
std::vector<uint64_t> head_block_numbers;
head_block_numbers.resize(connections.size());
std::vector<int> qualities;
qualities.resize(connections.size());
for (size_t n = 0; n < connections.size(); n++) {
rpc_connection &conn = *connections[n];
int quality = 0;
head_block_numbers[n] = std::numeric_limits<uint64_t>::max();
// ping n'th node
if (debug_rpc_calls)
ilog("### Ping ${sidechain} node #${n}, ${url}", ("sidechain", fc::reflector<sidechain_type>::to_string(sidechain))("n", n)("url", conn.get_url()));
fc::time_point t_sent = fc::time_point::now();
uint64_t head_block_number = ping(conn);
fc::time_point t_received = fc::time_point::now();
int t = (t_received - t_sent).count();
// evaluate n'th node reply quality and switch to it if it's better
if (head_block_number != std::numeric_limits<uint64_t>::max()) {
if (simulate_connection_reselection)
t += rand() % 10;
FC_ASSERT(t != -1);
head_block_numbers[n] = head_block_number;
if (t < t_limit)
quality = t_limit - t; // the less time, the higher quality
// look for the best quality
if (quality > best_quality) {
best_n = n;
best_quality = quality;
}
}
qualities[n] = quality;
}
FC_ASSERT(best_n != -1 && best_quality != -1);
if (best_n != n_active_conn) { // if the best client is not the current one, ...
uint64_t active_head_block_number = head_block_numbers[n_active_conn];
if ((active_head_block_number == std::numeric_limits<uint64_t>::max() // ...and the current one has no known head block...
|| head_block_numbers[best_n] >= active_head_block_number) // ...or the best client's head is more recent than the current, ...
&& best_quality > qualities[n_active_conn] + quality_diff_threshold) { // ...and the new client's quality exceeds current more than by threshold
n_active_conn = best_n; // ...then select new one
if (debug_rpc_calls)
ilog("### Reselected ${sidechain} node to #${n}, ${url}", ("sidechain", fc::reflector<sidechain_type>::to_string(sidechain))("n", n_active_conn)("url", connections[n_active_conn]->get_url()));
}
}
schedule_connection_selection();
}
rpc_connection &rpc_client::get_active_connection() const {
return *connections[n_active_conn];
}
std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) {
const std::lock_guard<std::mutex> lock(conn_mutex);
return send_post_request(get_active_connection(), method, params, show_log);
}
std::string rpc_client::send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log) {
return conn.send_post_request(method, params, show_log);
}
rpc_client::~rpc_client() {
try {
if (connection_selection_task.valid())
connection_selection_task.cancel_and_wait(__FUNCTION__);
} catch (fc::canceled_exception &) {
// Expected exception. Move along.
} catch (fc::exception &e) {
edump((e.to_detail_string()));
}
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -1,8 +1,8 @@
#include <graphene/peerplays_sidechain/common/utils.hpp> #include <graphene/peerplays_sidechain/common/utils.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp> #include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp> #include <boost/archive/iterators/binary_from_base64.hpp>
//#include <boost/archive/iterators/ostream_iterator.hpp>
#include <boost/archive/iterators/transform_width.hpp> #include <boost/archive/iterators/transform_width.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
@ -48,4 +48,17 @@ std::string object_id_to_string(graphene::chain::object_id_type id) {
return object_id; return object_id;
} }
graphene::chain::object_id_type string_to_object_id(const std::string &id) {
std::vector<std::string> strs;
boost::split(strs, id, boost::is_any_of("."));
if (strs.size() != 3) {
elog("Wrong object_id format: ${id}", ("id", id));
return graphene::chain::object_id_type{};
}
auto s = boost::lexical_cast<int>(strs.at(0));
auto t = boost::lexical_cast<int>(strs.at(1));
return graphene::chain::object_id_type{(uint8_t)s, (uint8_t)t, boost::lexical_cast<uint64_t>(strs.at(2))};
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -5,10 +5,52 @@
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
//! rlp_decoder //! base_decoder
boost::multiprecision::uint256_t base_decoder::decode_uint256(const std::string &value) {
boost::multiprecision::uint256_t result = 0;
namespace boost::multiprecision::uint256_t power(1);
{ uint8_t digit;
int pos = value.size() - 1;
while (pos >= 0) {
digit = 0;
if ('0' <= value[pos] && value[pos] <= '9') {
digit = value[pos] - '0';
} else if ('a' <= value[pos] && value[pos] <= 'z') {
digit = value[pos] - 'a' + 10;
}
result += digit * power;
pos--;
power *= 16;
}
return result;
}
std::string base_decoder::decode_address(const std::string &value) {
return value.substr(24, 40);
}
//! deposit_erc20_decoder
const std::string deposit_erc20_decoder::function_signature = "97feb926"; //! depositERC20(address,uint256)
fc::optional<deposit_erc20_transaction> deposit_erc20_decoder::decode(const std::string &input) {
const auto input_without_0x = remove_0x(input);
if (function_signature != input_without_0x.substr(0, 8)) {
return fc::optional<deposit_erc20_transaction>{};
}
if (input_without_0x.size() != 136) {
return fc::optional<deposit_erc20_transaction>{};
}
deposit_erc20_transaction erc_20;
erc_20.token = add_0x(base_decoder::decode_address(input_without_0x.substr(8, 64)));
erc_20.amount = base_decoder::decode_uint256(input_without_0x.substr(72, 64));
return erc_20;
}
//! rlp_decoder
namespace {
const signed char p_util_hexdigit[256] = const signed char p_util_hexdigit[256] =
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@ -28,21 +70,18 @@ const signed char p_util_hexdigit[256] =
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
} }
std::vector<std::string> rlp_decoder::decode(const std::string& str) std::vector<std::string> rlp_decoder::decode(const std::string &str) {
{
size_t consumed = 0; size_t consumed = 0;
const auto raw_vec = parse_hex(str); const auto raw_vec = parse_hex(str);
const std::vector<std::string> rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed); const std::vector<std::string> rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed);
std::vector<std::string> result_array; std::vector<std::string> result_array;
for(const auto& rlp : decode_rlp(raw_vec.data(), raw_vec.size(), consumed)) for (const auto &rlp : decode_rlp(raw_vec.data(), raw_vec.size(), consumed)) {
{
result_array.emplace_back(bytes2hex(rlp)); result_array.emplace_back(bytes2hex(rlp));
} }
return result_array; return result_array;
} }
std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t& consumed) std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t &consumed) {
{
std::vector<std::string> rlp_result; std::vector<std::string> rlp_result;
consumed = 0; consumed = 0;
@ -51,14 +90,12 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
const size_t prefixlen = 1; const size_t prefixlen = 1;
unsigned char ch = *raw; unsigned char ch = *raw;
if (len < 1) if (len < 1) {
{
return rlp_result; return rlp_result;
} }
// Case 1: [prefix is 1-byte data buffer] // Case 1: [prefix is 1-byte data buffer]
if (ch <= 0x7f) if (ch <= 0x7f) {
{
const unsigned char *tok_start = raw; const unsigned char *tok_start = raw;
const unsigned char *tok_end = tok_start + prefixlen; const unsigned char *tok_end = tok_start + prefixlen;
FC_ASSERT(tok_end <= end); FC_ASSERT(tok_end <= end);
@ -70,8 +107,7 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
consumed = buf.size(); consumed = buf.size();
} }
// Case 2: [prefix, including buffer length][data] // Case 2: [prefix, including buffer length][data]
else if ((ch >= 0x80) && (ch <= 0xb7)) else if ((ch >= 0x80) && (ch <= 0xb7)) {
{
const size_t blen = ch - 0x80; const size_t blen = ch - 0x80;
const size_t expected = prefixlen + blen; const size_t expected = prefixlen + blen;
@ -93,8 +129,7 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
consumed = expected; consumed = expected;
} }
// Case 3: [prefix][buffer length][data] // Case 3: [prefix][buffer length][data]
else if ((ch >= 0xb8) && (ch <= 0xbf)) else if ((ch >= 0xb8) && (ch <= 0xbf)) {
{
const size_t uintlen = ch - 0xb7; const size_t uintlen = ch - 0xb7;
size_t expected = prefixlen + uintlen; size_t expected = prefixlen + uintlen;
@ -124,8 +159,7 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
consumed = expected; consumed = expected;
} }
// Case 4: [prefix][list] // Case 4: [prefix][list]
else if ((ch >= 0xc0) && (ch <= 0xf7)) else if ((ch >= 0xc0) && (ch <= 0xf7)) {
{
const size_t payloadlen = ch - 0xc0; const size_t payloadlen = ch - 0xc0;
const size_t expected = prefixlen + payloadlen; const size_t expected = prefixlen + payloadlen;
@ -136,8 +170,7 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
consumed = expected; consumed = expected;
} }
// Case 5: [prefix][list length][list] // Case 5: [prefix][list length][list]
else else {
{
FC_ASSERT((ch >= 0xf8) && (ch <= 0xff)); FC_ASSERT((ch >= 0xf8) && (ch <= 0xff));
const size_t uintlen = ch - 0xf7; const size_t uintlen = ch - 0xf7;
@ -169,8 +202,7 @@ std::vector<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_
return rlp_result; return rlp_result;
} }
std::vector<std::string> rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen) std::vector<std::string> rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen) {
{
std::vector<std::string> rlp_result; std::vector<std::string> rlp_result;
const size_t prefixlen = 1; const size_t prefixlen = 1;
@ -183,8 +215,7 @@ std::vector<std::string> rlp_decoder::decode_array(const unsigned char *raw, siz
const unsigned char *list_ent = raw + prefixlen + uintlen; const unsigned char *list_ent = raw + prefixlen + uintlen;
// recursively read until payloadlen bytes parsed, or error // recursively read until payloadlen bytes parsed, or error
while (child_len > 0) while (child_len > 0) {
{
size_t child_consumed = 0; size_t child_consumed = 0;
const auto val = decode_rlp(list_ent, child_len, child_consumed); const auto val = decode_rlp(list_ent, child_len, child_consumed);
@ -197,8 +228,7 @@ std::vector<std::string> rlp_decoder::decode_array(const unsigned char *raw, siz
return rlp_result; return rlp_result;
} }
uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len) uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len) {
{
if (len == 0) if (len == 0)
return 0; return 0;
else if (len == 1) else if (len == 1)
@ -207,17 +237,14 @@ uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len)
return (raw[len - 1]) + (to_int(raw, len - 1) * 256); return (raw[len - 1]) + (to_int(raw, len - 1) * 256);
} }
std::vector<unsigned char> rlp_decoder::parse_hex(const std::string& str) std::vector<unsigned char> rlp_decoder::parse_hex(const std::string &str) {
{
return parse_hex(str.c_str()); return parse_hex(str.c_str());
} }
std::vector<unsigned char> rlp_decoder::parse_hex(const char* psz) std::vector<unsigned char> rlp_decoder::parse_hex(const char *psz) {
{
// convert hex dump to vector // convert hex dump to vector
std::vector<unsigned char> vch; std::vector<unsigned char> vch;
while (true) while (true) {
{
while (isspace(*psz)) while (isspace(*psz))
psz++; psz++;
signed char c = hex_digit(*psz++); signed char c = hex_digit(*psz++);
@ -233,8 +260,7 @@ std::vector<unsigned char> rlp_decoder::parse_hex(const char* psz)
return vch; return vch;
} }
signed char rlp_decoder::hex_digit(char c) signed char rlp_decoder::hex_digit(char c) {
{
return p_util_hexdigit[(unsigned char)c]; return p_util_hexdigit[(unsigned char)c];
} }

View file

@ -1,6 +1,5 @@
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp> #include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <stdlib.h>
#include <boost/algorithm/hex.hpp> #include <boost/algorithm/hex.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -9,33 +8,31 @@
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
//! base_encoder //! base_encoder
std::string base_encoder::encode_uint256(boost::multiprecision::uint256_t value) std::string base_encoder::encode_uint256(boost::multiprecision::uint256_t value) {
{
return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str();
} }
std::string base_encoder::encode_address(const std::string& value) std::string base_encoder::encode_address(const std::string &value) {
{
return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str();
} }
std::string base_encoder::encode_string(const std::string& value) std::string base_encoder::encode_string(const std::string &value) {
{
std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str(); std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str();
data += boost::algorithm::hex(value) + std::string( (64 - value.size() * 2 % 64), '0' ); data += boost::algorithm::hex(value);
if (value.size() % 32 != 0) {
data += std::string((64 - value.size() * 2 % 64), '0');
}
return data; return data;
} }
//! update_owners_encoder //! update_owners_encoder
std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>>& owners_weights, const std::string& object_id) const const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string)
{ std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) {
std::string data = "0x" + function_signature; std::string data = add_0x(function_signature);
data += base_encoder::encode_uint256(64); data += base_encoder::encode_uint256(64);
data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32);
data += base_encoder::encode_uint256(owners_weights.size()); data += base_encoder::encode_uint256(owners_weights.size());
for(const auto& owner : owners_weights) for (const auto &owner : owners_weights) {
{
data += base_encoder::encode_address(owner.first); data += base_encoder::encode_address(owner.first);
data += base_encoder::encode_uint256(owner.second); data += base_encoder::encode_uint256(owner.second);
} }
@ -45,9 +42,9 @@ std::string update_owners_encoder::encode(const std::vector<std::pair<std::strin
} }
//! withdrawal_encoder //! withdrawal_encoder
std::string withdrawal_encoder::encode(const std::string& to, boost::multiprecision::uint256_t amount, const std::string& object_id) const const std::string withdrawal_encoder::function_signature = "e088747b"; //! withdraw(address,uint256,string)
{ std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
std::string data = "0x" + function_signature; std::string data = add_0x(function_signature);
data += base_encoder::encode_address(to); data += base_encoder::encode_address(to);
data += base_encoder::encode_uint256(amount); data += base_encoder::encode_uint256(amount);
data += base_encoder::encode_uint256(32 * 3); data += base_encoder::encode_uint256(32 * 3);
@ -56,23 +53,81 @@ std::string withdrawal_encoder::encode(const std::string& to, boost::multiprecis
return data; return data;
} }
//! withdrawal_erc20_encoder
const std::string withdrawal_erc20_encoder::function_signature = "483c0467"; //! withdrawERC20(address,address,uint256,string)
std::string withdrawal_erc20_encoder::encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
std::string data = add_0x(function_signature);
data += base_encoder::encode_address(token);
data += base_encoder::encode_address(to);
data += base_encoder::encode_uint256(amount);
data += base_encoder::encode_uint256(32 * 4);
data += base_encoder::encode_string(object_id);
return data;
}
//! signature_encoder
const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[])
const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[])
const std::string withdrawal_erc20_function_signature = "d2bf2866"; //! withdrawERC20((bytes,(uint8,bytes32,bytes32))[])
signature_encoder::signature_encoder(const std::string &function_hash) :
function_signature{function_hash} {
}
std::string signature_encoder::get_function_signature_from_transaction(const std::string &transaction) {
const std::string tr = remove_0x(transaction);
if (tr.substr(0, 8) == update_owners_encoder::function_signature)
return update_owners_function_signature;
if (tr.substr(0, 8) == withdrawal_encoder::function_signature)
return withdrawal_function_signature;
if (tr.substr(0, 8) == withdrawal_erc20_encoder::function_signature)
return withdrawal_erc20_function_signature;
return "";
}
std::string signature_encoder::encode(const std::vector<encoded_sign_transaction> &transactions) const {
std::string data = add_0x(function_signature);
data += base_encoder::encode_uint256(32);
data += base_encoder::encode_uint256(transactions.size());
size_t offset = (transactions.size()) * 32;
for (const auto &transaction : transactions) {
data += base_encoder::encode_uint256(offset);
const auto transaction_data = remove_0x(transaction.data);
offset += 5 * 32 + transaction_data.size() / 2;
if (transaction_data.size() / 2 % 32 != 0) {
offset += 32 - transaction_data.size() / 2 % 32;
}
}
for (const auto &transaction : transactions) {
data += base_encoder::encode_uint256(4 * 32);
data += base_encoder::encode_address(transaction.sign.v);
data += base_encoder::encode_address(transaction.sign.r);
data += base_encoder::encode_address(transaction.sign.s);
const auto transaction_data = remove_0x(transaction.data);
data += base_encoder::encode_uint256(transaction_data.size() / 2);
data += transaction_data;
if (transaction_data.size() % 64 != 0) {
data += std::string((64 - transaction_data.size() % 64), '0');
}
}
return data;
}
//! rlp_encoder //! rlp_encoder
std::string rlp_encoder::encode(const std::string& s) std::string rlp_encoder::encode(const std::string &s) {
{
return encode_rlp(hex2bytes(s)); return encode_rlp(hex2bytes(s));
} }
std::string rlp_encoder::encode_length(int len, int offset) std::string rlp_encoder::encode_length(int len, int offset) {
{ if (len < 56) {
if(len<56)
{
std::string temp; std::string temp;
temp = (char)(len + offset); temp = (char)(len + offset);
return temp; return temp;
} } else {
else
{
const std::string hexLength = to_hex(len); const std::string hexLength = to_hex(len);
const int lLength = hexLength.size() / 2; const int lLength = hexLength.size() / 2;
const std::string fByte = to_hex(offset + 55 + lLength); const std::string fByte = to_hex(offset + 55 + lLength);
@ -80,24 +135,22 @@ std::string rlp_encoder::encode_length(int len, int offset)
} }
} }
std::string rlp_encoder::hex2bytes(const std::string& s) std::string rlp_encoder::hex2bytes(const std::string &s) {
{
std::string dest; std::string dest;
dest.resize(s.size()/2); const auto s_final = s.size() % 2 == 0 ? s : "0" + s;
hex2bin(s.c_str(), &dest[0]); dest.resize(s_final.size() / 2);
hex2bin(s_final.c_str(), &dest[0]);
return dest; return dest;
} }
std::string rlp_encoder::encode_rlp(const std::string& s) std::string rlp_encoder::encode_rlp(const std::string &s) {
{
if (s.size() == 1 && (unsigned char)s[0] < 128) if (s.size() == 1 && (unsigned char)s[0] < 128)
return s; return s;
else else
return encode_length(s.size(), 128) + s; return encode_length(s.size(), 128) + s;
} }
int rlp_encoder::char2int(char input) int rlp_encoder::char2int(char input) {
{
if (input >= '0' && input <= '9') if (input >= '0' && input <= '9')
return input - '0'; return input - '0';
if (input >= 'A' && input <= 'F') if (input >= 'A' && input <= 'F')
@ -108,10 +161,8 @@ int rlp_encoder::char2int(char input)
return -1; return -1;
} }
void rlp_encoder::hex2bin(const char* src, char* target) void rlp_encoder::hex2bin(const char *src, char *target) {
{ while (*src && src[1]) {
while(*src && src[1])
{
*(target++) = char2int(*src) * 16 + char2int(src[1]); *(target++) = char2int(*src) * 16 + char2int(src[1]);
src += 2; src += 2;
} }

View file

@ -1,6 +1,7 @@
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp> #include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
#include <boost/algorithm/hex.hpp> #include <boost/algorithm/hex.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
@ -10,28 +11,70 @@
#include <fc/crypto/elliptic.hpp> #include <fc/crypto/elliptic.hpp>
#include <fc/crypto/hex.hpp> #include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp> #include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp> #include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp> #include <graphene/peerplays_sidechain/ethereum/utils.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
const secp256k1_context *eth_context() { const secp256k1_context *eth_context() {
static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
return ctx; return ctx;
} }
bytes keccak_hash(const std::string &data) {
bytes hash;
hash.resize(32);
const auto transaction_string = boost::algorithm::unhex(remove_0x(data));
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
return hash;
}
signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key) {
const bytes priv_key = parse_hex(private_key);
int recid = 0;
secp256k1_ecdsa_recoverable_signature sig;
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), NULL, NULL));
fc::ecc::compact_signature result;
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
unsigned int v = recid + from_hex<unsigned int>(chain_id) * 2 + 35;
bytes r;
for (int i = 1; i < 33; i++)
r.emplace_back((char)result.at(i));
bytes s;
for (int i = 33; i < 65; i++)
s.emplace_back((char)result.at(i));
signature eth_sig;
eth_sig.v = to_hex(v);
eth_sig.r = fc::to_hex((char *)&r[0], r.size());
eth_sig.s = fc::to_hex((char *)&s[0], s.size());
return eth_sig;
}
//! base_transaction
base_transaction::base_transaction(const std::string &raw_tx) {
}
//! transaction //! transaction
const transaction& transaction::sign(const std::string& private_key) const transaction::transaction(const std::string &raw_tx) :
{ base_transaction{raw_tx} {
deserialize(raw_tx);
}
const transaction &transaction::sign(const std::string &private_key) const {
return *this; return *this;
} }
std::string transaction::serialize() const std::string transaction::serialize() const {
{
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
pt.put("from", from); pt.put("from", from);
pt.put("to", to); pt.put("to", to);
@ -42,8 +85,7 @@ std::string transaction::serialize() const
return ss.str(); return ss.str();
} }
void transaction::deserialize(const std::string& raw_tx) void transaction::deserialize(const std::string &raw_tx) {
{
std::stringstream ss_tx(raw_tx); std::stringstream ss_tx(raw_tx);
boost::property_tree::ptree tx_json; boost::property_tree::ptree tx_json;
boost::property_tree::read_json(ss_tx, tx_json); boost::property_tree::read_json(ss_tx, tx_json);
@ -58,8 +100,16 @@ void transaction::deserialize(const std::string& raw_tx)
//! raw_transaction //! raw_transaction
signed_transaction raw_transaction::sign(const std::string& private_key) const raw_transaction::raw_transaction(const std::string &raw_tx) :
{ base_transaction{raw_tx} {
deserialize(raw_tx);
}
bytes raw_transaction::hash() const {
return keccak_hash(serialize());
}
signed_transaction raw_transaction::sign(const std::string &private_key) const {
//! Prepare signed transaction //! Prepare signed transaction
signed_transaction tr; signed_transaction tr;
tr.nonce = nonce; tr.nonce = nonce;
@ -69,99 +119,125 @@ signed_transaction raw_transaction::sign(const std::string& private_key) const
tr.value = value; tr.value = value;
tr.data = data; tr.data = data;
//! Calculate keccak hash of transaction const auto sig = sign_hash(hash(), chain_id, private_key);
bytes hash; tr.v = sig.v;
hash.resize(32); tr.r = sig.r;
const auto transaction_string = boost::algorithm::unhex( serialize() ); tr.s = sig.s;
keccak_256((const unsigned char *) transaction_string.data(), transaction_string.size(), (unsigned char *) hash.data());
const bytes priv_key = parse_hex(private_key);
int recid = 0;
secp256k1_ecdsa_recoverable_signature sig;
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), NULL, NULL));
fc::ecc::compact_signature result;
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
bytes v = bytes{char(recid + from_hex<int>(chain_id) * 2 + 35)};
bytes r;
for(int i = 1; i < 33; i++)
r.emplace_back((char) result.at(i));
bytes s;
for(int i = 33; i < 65; i++)
s.emplace_back((char) result.at(i));
tr.v = fc::to_hex((char *)&v[0], v.size());
tr.r = fc::to_hex((char *)&r[0], r.size());
tr.s = fc::to_hex((char *)&s[0], s.size());
return tr; return tr;
} }
std::string raw_transaction::serialize() const std::string raw_transaction::serialize() const {
{ const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
rlp_encoder encoder; rlp_encoder::encode(remove_0x(gas_price)) +
const std::string serialized = encoder.encode(remove_0x(nonce)) + rlp_encoder::encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(gas_price)) + rlp_encoder::encode(remove_0x(to)) +
encoder.encode(remove_0x(gas_limit)) + rlp_encoder::encode(remove_0x(value)) +
encoder.encode(remove_0x(to)) + rlp_encoder::encode(remove_0x(data)) +
encoder.encode(remove_0x(value)) + rlp_encoder::encode(remove_0x(chain_id)) +
encoder.encode(remove_0x(data)) + rlp_encoder::encode("") +
encoder.encode(remove_0x(chain_id)) + rlp_encoder::encode("");
encoder.encode("") +
encoder.encode("");
return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized));
} }
void raw_transaction::deserialize(const std::string& raw_tx) void raw_transaction::deserialize(const std::string &raw_tx) {
{ const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
rlp_decoder decoder;
const auto rlp_array = decoder.decode(raw_tx);
FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format"); FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0)); nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
boost::algorithm::to_lower(nonce);
gas_price = add_0x(rlp_array.at(1)); gas_price = add_0x(rlp_array.at(1));
boost::algorithm::to_lower(gas_price);
gas_limit = add_0x(rlp_array.at(2)); gas_limit = add_0x(rlp_array.at(2));
boost::algorithm::to_lower(gas_limit);
to = add_0x(rlp_array.at(3)); to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4)); boost::algorithm::to_lower(to);
data = add_0x(rlp_array.at(5)); value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
boost::algorithm::to_lower(value);
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
boost::algorithm::to_lower(data);
chain_id = add_0x(rlp_array.at(6)); chain_id = add_0x(rlp_array.at(6));
boost::algorithm::to_lower(chain_id);
} }
//! signed_transaction //! signed_transaction
std::string signed_transaction::serialize() const signed_transaction::signed_transaction(const std::string &raw_tx) :
{ base_transaction{raw_tx} {
rlp_encoder encoder; deserialize(raw_tx);
const std::string serialized = encoder.encode(remove_0x(nonce)) +
encoder.encode(remove_0x(gas_price)) +
encoder.encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(to)) +
encoder.encode(remove_0x(value)) +
encoder.encode(remove_0x(data)) +
encoder.encode(remove_0x(v)) +
encoder.encode(remove_0x(r)) +
encoder.encode(remove_0x(s));
return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized );
} }
void signed_transaction::deserialize(const std::string& raw_tx) std::string signed_transaction::recover(const std::string &chain_id) const {
{ fc::ecc::compact_signature input64;
rlp_decoder decoder; fc::from_hex(r, (char *)&input64.at(1), 32);
const auto rlp_array = decoder.decode(raw_tx); const int recid = from_hex<unsigned int>(v) - from_hex<unsigned int>(chain_id) * 2 - 35;
fc::from_hex(std::to_string(recid), (char *)&input64.at(0), 1);
fc::from_hex(s, (char *)&input64.at(33), 32);
secp256k1_ecdsa_recoverable_signature sig;
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_parse_compact(eth_context(), &sig, (const unsigned char *)&input64.data[1], recid));
raw_transaction tr;
tr.nonce = nonce;
tr.gas_price = gas_price;
tr.gas_limit = gas_limit;
tr.to = to;
tr.value = value;
tr.data = data;
tr.chain_id = chain_id;
secp256k1_pubkey rawPubkey;
FC_ASSERT(secp256k1_ecdsa_recover(eth_context(), &rawPubkey, &sig, (const unsigned char *)tr.hash().data()));
std::array<uint8_t, 65> pubkey;
size_t biglen = 65;
FC_ASSERT(secp256k1_ec_pubkey_serialize(eth_context(), pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED));
const std::string out = std::string(pubkey.begin(), pubkey.end()).substr(1);
bytes hash;
hash.resize(32);
keccak_256((const unsigned char *)out.data(), out.size(), (unsigned char *)hash.data());
return add_0x(fc::to_hex((char *)&hash[0], hash.size()).substr(24));
}
std::string signed_transaction::serialize() const {
const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
rlp_encoder::encode(remove_0x(gas_price)) +
rlp_encoder::encode(remove_0x(gas_limit)) +
rlp_encoder::encode(remove_0x(to)) +
rlp_encoder::encode(remove_0x(value)) +
rlp_encoder::encode(remove_0x(data)) +
rlp_encoder::encode(remove_0x(v)) +
rlp_encoder::encode(remove_leading_00(remove_0x(r))) +
rlp_encoder::encode(remove_leading_00(remove_0x(s)));
return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized));
}
void signed_transaction::deserialize(const std::string &raw_tx) {
const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format"); FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0)); nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
boost::algorithm::to_lower(nonce);
gas_price = add_0x(rlp_array.at(1)); gas_price = add_0x(rlp_array.at(1));
boost::algorithm::to_lower(gas_price);
gas_limit = add_0x(rlp_array.at(2)); gas_limit = add_0x(rlp_array.at(2));
boost::algorithm::to_lower(gas_limit);
to = add_0x(rlp_array.at(3)); to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4)); boost::algorithm::to_lower(to);
data = add_0x(rlp_array.at(5)); value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
boost::algorithm::to_lower(value);
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
boost::algorithm::to_lower(data);
v = add_0x(rlp_array.at(6)); v = add_0x(rlp_array.at(6));
r = add_0x(rlp_array.at(7)); boost::algorithm::to_lower(v);
s = add_0x(rlp_array.at(8)); r = add_0x(add_leading_00(rlp_array.at(7)));
boost::algorithm::to_lower(r);
s = add_0x(add_leading_00(rlp_array.at(8)));
boost::algorithm::to_lower(s);
} }
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -1,5 +1,36 @@
#include <graphene/peerplays_sidechain/ethereum/types.hpp> #include <graphene/peerplays_sidechain/ethereum/types.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
signature::signature(const std::string &sign) {
deserialize(sign);
}
std::string signature::serialize() const {
boost::property_tree::ptree pt;
pt.put("v", v);
pt.put("r", r);
pt.put("s", s);
std::stringstream ss;
boost::property_tree::json_parser::write_json(ss, pt);
return ss.str();
}
void signature::deserialize(const std::string &raw_tx) {
std::stringstream ss_tx(raw_tx);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(ss_tx, tx_json);
if (tx_json.count("v"))
v = tx_json.get<std::string>("v");
if (tx_json.count("r"))
r = tx_json.get<std::string>("r");
if (tx_json.count("s"))
s = tx_json.get<std::string>("s");
}
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -10,8 +10,7 @@ bytes parse_hex(const std::string &str) {
return vec; return vec;
} }
std::string bytes2hex(const std::string& s) std::string bytes2hex(const std::string &s) {
{
std::string dest; std::string dest;
for (const auto &i : s) for (const auto &i : s)
dest += uchar2Hex((unsigned char)i); dest += uchar2Hex((unsigned char)i);
@ -19,14 +18,12 @@ std::string bytes2hex(const std::string& s)
return dest; return dest;
} }
std::string uchar2Hex(unsigned char n) std::string uchar2Hex(unsigned char n) {
{
std::string dest; std::string dest;
dest.resize(2); dest.resize(2);
sprintf(&dest[0], "%X", n); sprintf(&dest[0], "%X", n);
if(n < (unsigned char)16) if (n < (unsigned char)16) {
{
dest[1] = dest[0]; dest[1] = dest[0];
dest[0] = '0'; dest[0] = '0';
} }
@ -34,8 +31,7 @@ std::string uchar2Hex(unsigned char n)
return dest; return dest;
} }
std::string add_0x(const std::string& s) std::string add_0x(const std::string &s) {
{
if (s.size() > 1) { if (s.size() > 1) {
if (s.substr(0, 2) == "0x") if (s.substr(0, 2) == "0x")
return s; return s;
@ -44,8 +40,7 @@ std::string add_0x(const std::string& s)
return "0x" + s; return "0x" + s;
} }
std::string remove_0x(const std::string& s) std::string remove_0x(const std::string &s) {
{
if (s.size() > 1) { if (s.size() > 1) {
if (s.substr(0, 2) == "0x") if (s.substr(0, 2) == "0x")
return s.substr(2); return s.substr(2);
@ -54,4 +49,24 @@ std::string remove_0x(const std::string& s)
return s; return s;
} }
std::string add_leading_00(const std::string &s) {
std::string result = s;
while (result.size() < 64) {
result = "00" + result;
}
return result;
}
std::string remove_leading_00(const std::string &s) {
std::string result = s;
while (result.size() > 1 && result.substr(0, 2) == "00") {
result = result.substr(2);
}
return result;
}
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -8,6 +8,14 @@ using namespace graphene::chain;
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15 const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum script_op {
OP_0 = 0x00,
OP_PUSH = 0x20,
OP_SIZE_34 = 0x22
};
class bitcoin_address { class bitcoin_address {
@ -96,9 +104,6 @@ private:
void create_address(); void create_address();
public: public:
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum { OP_0 = 0x00, enum { OP_0 = 0x00,
OP_EQUAL = 0x87, OP_EQUAL = 0x87,
OP_HASH160 = 0xa9, OP_HASH160 = 0xa9,
@ -145,7 +150,7 @@ public:
btc_weighted_multisig_address() = default; btc_weighted_multisig_address() = default;
btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest); network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const { bytes get_redeem_script() const {
return redeem_script_; return redeem_script_;
@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address {
public: public:
btc_one_or_weighted_multisig_address() = default; btc_one_or_weighted_multisig_address() = default;
btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest); network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const { bytes get_redeem_script() const {
return redeem_script_; return redeem_script_;
} }

View file

@ -0,0 +1,39 @@
#pragma once
#include <curl/curl.h>
#include <cstdint>
#include <functional>
#include <map>
#include <string>
#include <vector>
typedef std::function<uint64_t()> get_fee_func_type;
namespace graphene { namespace peerplays_sidechain {
class estimate_fee_external {
public:
estimate_fee_external();
~estimate_fee_external();
std::vector<std::pair<std::string, uint64_t>> get_fee_external(uint16_t target_block);
private:
std::string get_response(std::string url);
// Here add your custom parser for external url. Take care of incremental name
// and populate the list of url_parsers bellow paired with the function
uint64_t parse_and_get_fee_1();
uint64_t parse_and_get_fee_2();
uint64_t parse_and_get_fee_3();
const std::map<std::string, get_fee_func_type> url_get_fee_parsers{
{"https://www.bitgo.com/api/v2/btc/tx/fee", std::bind(&estimate_fee_external::parse_and_get_fee_1, this)},
{"https://bitcoiner.live/api/fees/estimates/latest", std::bind(&estimate_fee_external::parse_and_get_fee_2, this)},
{"https://api.blockchain.info/mempool/fees", std::bind(&estimate_fee_external::parse_and_get_fee_3, this)}};
std::string response;
uint16_t target_block;
CURL *curl{nullptr};
};
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,53 @@
#pragma once
#include <bitcoin/client/obelisk_client.hpp>
#include <bitcoin/client/socket_stream.hpp>
#include <bitcoin/system/chain/block.hpp>
#include <boost/signals2.hpp>
#include <mutex>
#define LIBBITCOIN_SERVER_TIMEOUT (10)
#define LIBBITCOIN_SERVER_RETRIES (100)
#define DEAFULT_LIBBITCOIN_TRX_FEE (20000)
#define MAX_TRXS_IN_MEMORY_POOL (30000)
#define MIN_TRXS_IN_BUCKET (100)
#define GENESIS_MAINNET_HASH "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
#define GENESIS_TESTNET_HASH "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
#define GENESIS_REGTEST_HASH "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
namespace graphene { namespace peerplays_sidechain {
typedef std::function<void(const libbitcoin::chain::block &)>
block_update_handler;
struct list_unspent_replay {
std::string hash;
uint64_t value;
uint32_t index;
};
class libbitcoin_client {
public:
libbitcoin_client(std::string url);
std::string send_transaction(const std::string tx);
libbitcoin::chain::output::list get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions);
std::vector<list_unspent_replay> listunspent(std::string address, double amount);
uint64_t get_average_fee_from_trxs(std::vector<libbitcoin::chain::transaction> trx_list);
uint64_t get_fee_from_trx(libbitcoin::chain::transaction trx);
bool get_is_test_net();
private:
libbitcoin::client::obelisk_client obelisk_client;
libbitcoin::protocol::zmq::identifier id;
std::string protocol;
std::string host;
std::string port;
std::string url;
bool is_connected = false;
};
}} // namespace graphene::peerplays_sidechain

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <fc/crypto/elliptic.hpp> #include <fc/crypto/elliptic.hpp>
#include <fc/crypto/hex.hpp> #include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/bitcoin/types.hpp> #include <graphene/peerplays_sidechain/bitcoin/types.hpp>

View file

@ -3,44 +3,52 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <fc/thread/future.hpp>
#include <fc/thread/thread.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp> #include <boost/beast/core.hpp>
#include <graphene/peerplays_sidechain/defs.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
struct rpc_reply { class rpc_connection;
uint16_t status;
std::string body; struct rpc_credentials {
std::string url;
std::string user;
std::string password;
}; };
class rpc_client { class rpc_client {
public: public:
rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); const sidechain_type sidechain;
rpc_client(sidechain_type _sidechain, const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection);
~rpc_client();
protected: protected:
std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); bool debug_rpc_calls;
std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); bool simulate_connection_reselection;
std::string send_post_request(std::string method, std::string params, bool show_log); std::string send_post_request(std::string method, std::string params, bool show_log);
std::string url; static std::string send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log);
std::string user;
std::string password;
bool debug_rpc_calls;
std::string protocol; static std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx);
std::string host; static std::string retrieve_value_from_reply(std::string reply_str, std::string value_path);
std::string port;
std::string target;
std::string authorization;
uint32_t request_id;
private: private:
rpc_reply send_post_request(std::string body, bool show_log); std::vector<rpc_connection *> connections;
int n_active_conn;
fc::future<void> connection_selection_task;
std::mutex conn_mutex;
boost::beast::net::io_context ioc; rpc_connection &get_active_connection() const;
boost::beast::net::ip::tcp::resolver resolver;
boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> results; void select_connection();
void schedule_connection_selection();
virtual uint64_t ping(rpc_connection &conn) const = 0;
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -8,5 +8,6 @@ std::string base64_encode(const std::string &s);
std::string base64_decode(const std::string &s); std::string base64_decode(const std::string &s);
std::string object_id_to_string(graphene::chain::object_id_type id); std::string object_id_to_string(graphene::chain::object_id_type id);
graphene::chain::object_id_type string_to_object_id(const std::string &id);
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -57,10 +57,16 @@ struct info_for_vin {
bool resend = false; bool resend = false;
}; };
enum class sidechain_event_type {
deposit,
withdrawal
};
struct sidechain_event_data { struct sidechain_event_data {
fc::time_point_sec timestamp; fc::time_point_sec timestamp;
uint32_t block_num; uint32_t block_num;
sidechain_type sidechain; sidechain_type sidechain;
sidechain_event_type type;
std::string sidechain_uid; std::string sidechain_uid;
std::string sidechain_transaction_id; std::string sidechain_transaction_id;
std::string sidechain_from; std::string sidechain_from;

View file

@ -1,14 +1,34 @@
#pragma once #pragma once
#include <boost/multiprecision/cpp_int.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <fc/optional.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
class base_decoder {
public:
static boost::multiprecision::uint256_t decode_uint256(const std::string &value);
static std::string decode_address(const std::string &value);
};
struct deposit_erc20_transaction {
std::string token;
boost::multiprecision::uint256_t amount;
};
class deposit_erc20_decoder {
public:
static const std::string function_signature;
static fc::optional<deposit_erc20_transaction> decode(const std::string &input);
};
class rlp_decoder { class rlp_decoder {
private: private:
enum RLP_constants enum RLP_constants {
{
RLP_maxUintLen = 8, RLP_maxUintLen = 8,
RLP_bufferLenStart = 0x80, RLP_bufferLenStart = 0x80,
RLP_listStart = 0xc0, RLP_listStart = 0xc0,

View file

@ -1,11 +1,18 @@
#pragma once #pragma once
#include <boost/multiprecision/cpp_int.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/multiprecision/cpp_int.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
struct encoded_sign_transaction {
std::string data;
signature sign;
};
class base_encoder { class base_encoder {
public: public:
static std::string encode_uint256(boost::multiprecision::uint256_t value); static std::string encode_uint256(boost::multiprecision::uint256_t value);
@ -15,16 +22,34 @@ public:
class update_owners_encoder { class update_owners_encoder {
public: public:
const std::string function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) static const std::string function_signature;
std::string encode(const std::vector<std::pair<std::string, uint16_t>>& owners_weights, const std::string& object_id) const; static std::string encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id);
}; };
class withdrawal_encoder { class withdrawal_encoder {
public: public:
const std::string function_signature = "e088747b"; //! withdraw(address,uint256,string) static const std::string function_signature;
std::string encode(const std::string& to, boost::multiprecision::uint256_t amount, const std::string& object_id) const; static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
};
class withdrawal_erc20_encoder {
public:
static const std::string function_signature;
static std::string encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
};
class signature_encoder {
public:
const std::string function_signature;
signature_encoder(const std::string &function_hash);
static std::string get_function_signature_from_transaction(const std::string &transaction);
std::string encode(const std::vector<encoded_sign_transaction> &transactions) const;
}; };
class rlp_encoder { class rlp_encoder {
@ -39,35 +64,4 @@ private:
static void hex2bin(const char *src, char *target); static void hex2bin(const char *src, char *target);
}; };
/*class ethereum_function_call_encoder {
public:
enum operation_t {
OPERATION_CALL,
OPERATION_DELEGATE_CALL
};
static constexpr const char *const default_prev_addr = "0000000000000000000000000000000000000001";
std::string encode_function_signature(const std::string &function_signature);
std::string encode_address(const std::string &addr);
std::string encode_uint256(const std::string &value);
std::string encode_uint8(uint8_t value);
std::string encode_bytes(const std::string &values);
};
class safe_transaction_encoder {
public:
static constexpr const char *const default_safe_tx_gas = "0";
static constexpr const char *const default_data_gas = "0";
static constexpr const char *const default_gas_price = "0";
static constexpr const char *const default_gas_token = "0000000000000000000000000000000000000000";
static constexpr const char *const default_refund_receiver = "0000000000000000000000000000000000000000";
std::string create_safe_address(const std::vector<std::string> &owner_addresses, uint32_t threshold);
std::string build_transaction(const std::string &safe_account_addr, const std::string &value, const std::string &data, uint8_t operation, const std::string &safeTxGas, const std::string &dataGas, const std::string &gasPrice, const std::string &gasToken, const std::string &refundReceiver);
private:
ethereum_function_call_encoder m_ethereum_function_call_encoder;
};*/
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -4,21 +4,31 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
class base_transaction bytes keccak_hash(const std::string &data);
{ signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key);
class base_transaction {
public:
base_transaction() = default;
base_transaction(const std::string &raw_tx);
virtual std::string serialize() const = 0; virtual std::string serialize() const = 0;
virtual void deserialize(const std::string &raw_tx) = 0; virtual void deserialize(const std::string &raw_tx) = 0;
}; };
class transaction : base_transaction class transaction : base_transaction {
{
public: public:
std::string from; std::string from;
std::string to; std::string to;
std::string data; std::string data;
transaction() = default;
transaction(const std::string &raw_tx);
const transaction &sign(const std::string &private_key) const; const transaction &sign(const std::string &private_key) const;
virtual std::string serialize() const override; virtual std::string serialize() const override;
@ -26,8 +36,7 @@ public:
}; };
class signed_transaction; class signed_transaction;
class raw_transaction : base_transaction class raw_transaction : base_transaction {
{
public: public:
std::string nonce; std::string nonce;
std::string gas_price; std::string gas_price;
@ -37,14 +46,17 @@ public:
std::string data; std::string data;
std::string chain_id; std::string chain_id;
raw_transaction() = default;
raw_transaction(const std::string &raw_tx);
bytes hash() const;
signed_transaction sign(const std::string &private_key) const; signed_transaction sign(const std::string &private_key) const;
virtual std::string serialize() const override; virtual std::string serialize() const override;
virtual void deserialize(const std::string &raw_tx) override; virtual void deserialize(const std::string &raw_tx) override;
}; };
class signed_transaction : base_transaction class signed_transaction : base_transaction {
{
public: public:
std::string nonce; std::string nonce;
std::string gas_price; std::string gas_price;
@ -56,94 +68,13 @@ public:
std::string r; std::string r;
std::string s; std::string s;
signed_transaction() = default;
signed_transaction(const std::string &raw_tx);
std::string recover(const std::string &chain_id) const;
virtual std::string serialize() const override; virtual std::string serialize() const override;
virtual void deserialize(const std::string &raw_tx) override; virtual void deserialize(const std::string &raw_tx) override;
}; };
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum
// Example 1
//{
// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c",
// "blockNumber": "0xe5827c",
// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12",
// "gas": "0x38822",
// "gasPrice": "0xce42cba69",
// "maxFeePerGas": "0xddb4d8d16",
// "maxPriorityFeePerGas": "0x3b9aca00",
// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc",
// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
// "nonce": "0x32",
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
// "transactionIndex": "0xb6",
// "value": "0x2514d9d7d7d8000",
// "type": "0x2",
// "accessList": [],
// "chainId": "0x1",
// "v": "0x1",
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
//}
//
//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
//
//{
// "nonce": 50,
// "gasPrice": {
// "_hex": "0x0ce42cba69"
// },
// "gasLimit": {
// "_hex": "0x038822"
// },
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
// "value": {
// "_hex": "0x02514d9d7d7d8000"
// },
// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
// "v": 1,
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
//}
// Example 2
//{
// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894",
// "blockNumber": "0xe58271",
// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26",
// "gas": "0x5208",
// "gasPrice": "0xe6f3b322e",
// "maxFeePerGas": "0x1322455fd3",
// "maxPriorityFeePerGas": "0x53724e00",
// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce",
// "input": "0x",
// "nonce": "0x37",
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
// "transactionIndex": "0x8a",
// "value": "0x4563918244f40000",
// "type": "0x2",
// "accessList": [],
// "chainId": "0x1",
// "v": "0x0",
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
//}
//
//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
//
//{
// "nonce": 55,
// "gasPrice": {
// "_hex": "0x0e6f3b322e"
// },
// "gasLimit": {
// "_hex": "0x5208"
// },
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
// "value": {
// "_hex": "0x4563918244f40000"
// },
// "data": "0x",
// "v": 0,
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
//}

View file

@ -9,4 +9,17 @@ typedef uint64_t network_id_type;
using bytes = std::vector<char>; using bytes = std::vector<char>;
class signature {
public:
std::string v;
std::string r;
std::string s;
signature() = default;
signature(const std::string &sign);
std::string serialize() const;
void deserialize(const std::string &sign);
};
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -14,20 +14,24 @@ std::string add_0x(const std::string& s);
std::string remove_0x(const std::string &s); std::string remove_0x(const std::string &s);
std::string add_leading_00(const std::string &s);
std::string remove_leading_00(const std::string &s);
template <typename T> template <typename T>
std::string to_hex( const T& val ) std::string to_hex(const T &val, bool add_front_zero = true) {
{
std::stringstream stream; std::stringstream stream;
stream << std::hex << val; stream << std::hex << val;
std::string result(stream.str()); std::string result(stream.str());
if (add_front_zero) {
if (result.size() % 2) if (result.size() % 2)
result = "0" + result; result = "0" + result;
}
return result; return result;
} }
template <typename T> template <typename T>
T from_hex( const std::string& s ) T from_hex(const std::string &s) {
{
T val; T val;
std::stringstream stream; std::stringstream stream;
stream << std::hex << s; stream << std::hex << s;

View file

@ -13,16 +13,18 @@ class peerplays_sidechain_plugin_impl;
} }
struct son_proposal_type { struct son_proposal_type {
son_proposal_type(int op, son_id_type son, object_id_type object) : son_proposal_type(int op, sidechain_type sid, son_id_type son, object_id_type object) :
op_type(op), op_type(op),
sidechain(sid),
son_id(son), son_id(son),
object_id(object) { object_id(object) {
} }
int op_type; int op_type;
sidechain_type sidechain;
son_id_type son_id; son_id_type son_id;
object_id_type object_id; object_id_type object_id;
bool operator<(const son_proposal_type &other) const { bool operator<(const son_proposal_type &other) const {
return std::tie(op_type, son_id, object_id) < std::tie(other.op_type, other.son_id, other.object_id); return std::tie(op_type, sidechain, son_id, object_id) < std::tie(other.op_type, other.sidechain, other.son_id, other.object_id);
} }
}; };
@ -42,16 +44,17 @@ public:
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my; std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
std::set<chain::son_id_type> &get_sons(); std::set<chain::son_id_type> &get_sons();
const son_id_type get_current_son_id(); const son_id_type get_current_son_id(sidechain_type sidechain);
const son_object get_current_son_object(); const son_object get_current_son_object(sidechain_type sidechain);
const son_object get_son_object(son_id_type son_id); const son_object get_son_object(son_id_type son_id);
bool is_active_son(son_id_type son_id); bool is_active_son(sidechain_type sidechain, son_id_type son_id);
bool is_son_deregistered(son_id_type son_id); bool is_son_deregistered(son_id_type son_id);
fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key); fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id); void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -26,9 +26,10 @@ public:
std::shared_ptr<detail::sidechain_api_impl> my; std::shared_ptr<detail::sidechain_api_impl> my;
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain
FC_API(graphene::peerplays_sidechain::sidechain_api, FC_API(graphene::peerplays_sidechain::sidechain_api,
(get_son_listener_log)) (get_son_listener_log)(estimate_withdrawal_transaction_fee))

View file

@ -16,14 +16,17 @@
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
class sidechain_net_handler { class sidechain_net_handler {
protected:
sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
public: public:
sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
virtual ~sidechain_net_handler(); virtual ~sidechain_net_handler();
sidechain_type get_sidechain(); sidechain_type get_sidechain() const;
std::vector<std::string> get_sidechain_deposit_addresses(); std::vector<std::string> get_sidechain_deposit_addresses() const;
std::vector<std::string> get_sidechain_withdraw_addresses(); std::vector<std::string> get_sidechain_withdraw_addresses() const;
std::string get_private_key(std::string public_key); std::vector<sidechain_transaction_object> get_sidechain_transaction_objects(sidechain_transaction_status status) const;
std::string get_private_key(std::string public_key) const;
bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional<chain::operation &> proposal_op = boost::none); bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional<chain::operation &> proposal_op = boost::none);
bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer); bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer);
@ -50,13 +53,15 @@ public:
void add_to_son_listener_log(std::string trx_id); void add_to_son_listener_log(std::string trx_id);
std::vector<std::string> get_son_listener_log(); std::vector<std::string> get_son_listener_log();
virtual optional<asset> estimate_withdrawal_transaction_fee() const = 0;
protected: protected:
const sidechain_type sidechain;
peerplays_sidechain_plugin &plugin; peerplays_sidechain_plugin &plugin;
graphene::chain::database &database; graphene::chain::database &database;
sidechain_type sidechain;
bool debug_rpc_calls; bool debug_rpc_calls;
bool use_bitcoind_client;
std::map<std::string, std::string> private_keys; std::map<std::string, std::string> private_keys;

View file

@ -1,18 +1,20 @@
#pragma once #pragma once
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <zmq_addon.hpp> #include <zmq_addon.hpp>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <mutex>
#include <fc/network/http/connection.hpp> #include <fc/network/http/connection.hpp>
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp> #include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
@ -23,7 +25,27 @@ public:
uint64_t amount_; uint64_t amount_;
}; };
class bitcoin_rpc_client : public rpc_client { class btc_txin {
public:
std::vector<std::string> tx_address;
uint64_t tx_amount;
uint64_t tx_vout;
};
class btc_tx {
public:
std::string tx_txid;
uint32_t tx_confirmations;
std::vector<btc_txin> tx_in_list;
};
class block_data {
public:
std::string block_hash;
libbitcoin::chain::block block;
};
class bitcoin_client_base {
public: public:
enum class multi_type { enum class multi_type {
script, script,
@ -41,14 +63,47 @@ public:
std::string label; std::string label;
}; };
public: virtual uint64_t estimatesmartfee(uint16_t conf_target = 1) = 0;
bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); virtual std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2) = 0;
virtual btc_tx getrawtransaction(const std::string &txid, const bool verbose = false) = 0;
virtual void getnetworkinfo() = 0;
virtual std::string getblockchaininfo() = 0;
virtual std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999) = 0;
virtual std::string sendrawtransaction(const std::string &tx_hex) = 0;
virtual void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true) {
;
};
virtual std::string loadwallet(const std::string &filename) {
return "";
};
virtual std::string walletlock() {
return "";
};
virtual bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60) {
return false;
};
std::string createwallet(const std::string &wallet_name); void import_trx_to_memory_pool(const libbitcoin::chain::transaction &trx) {
uint64_t estimatesmartfee(uint16_t conf_target = 128); std::unique_lock<std::mutex> lck(libbitcoin_event_mutex);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2); if (trx_memory_pool.size() < MAX_TRXS_IN_MEMORY_POOL) {
std::string getrawtransaction(const std::string &txid, const bool verbose = false); trx_memory_pool.emplace_back(trx);
std::string getnetworkinfo(); }
}
protected:
std::vector<libbitcoin::chain::transaction> trx_memory_pool;
std::mutex libbitcoin_event_mutex;
};
class bitcoin_rpc_client : public bitcoin_client_base, public rpc_client {
public:
public:
bitcoin_rpc_client(const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection);
uint64_t estimatesmartfee(uint16_t conf_target = 1);
std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2);
btc_tx getrawtransaction(const std::string &txid, const bool verbose = false);
void getnetworkinfo();
std::string getblockchaininfo(); std::string getblockchaininfo();
void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true); void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true);
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
@ -58,37 +113,88 @@ public:
std::string walletlock(); std::string walletlock();
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
virtual uint64_t ping(rpc_connection &conn) const override;
private: private:
std::string ip; std::string ip;
uint32_t rpc_port;
std::string user; std::string user;
std::string password; std::string password;
std::string wallet_name; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
uint32_t bitcoin_major_version;
};
class bitcoin_libbitcoin_client : public bitcoin_client_base, public libbitcoin_client {
public:
bitcoin_libbitcoin_client(std::string url);
uint64_t estimatesmartfee(uint16_t conf_target = 1);
std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2);
btc_tx getrawtransaction(const std::string &txid, const bool verbose = false);
void getnetworkinfo();
std::string getblockchaininfo();
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
std::string sendrawtransaction(const std::string &tx_hex);
private:
bool is_test_net = false;
std::unique_ptr<estimate_fee_external> estimate_fee_ext;
uint64_t current_internal_fee = DEAFULT_LIBBITCOIN_TRX_FEE;
}; };
// ============================================================================= // =============================================================================
class zmq_listener { class zmq_listener_base {
public: public:
zmq_listener(std::string _ip, uint32_t _zmq); virtual ~zmq_listener_base(){};
virtual ~zmq_listener(); zmq_listener_base(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0) {
ip = _ip;
block_zmq_port = _block_zmq_port;
trx_zmq_port = _trx_zmq_port;
stopped = false;
};
virtual void start() = 0;
boost::signals2::signal<void(const block_data &)> block_event_received;
boost::signals2::signal<void(const libbitcoin::chain::transaction &)> trx_event_received;
protected:
std::string ip;
uint32_t block_zmq_port;
uint32_t trx_zmq_port;
std::atomic_bool stopped;
std::thread block_thr;
std::thread trx_thr;
};
class zmq_listener : public zmq_listener_base {
public:
zmq_listener(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0);
virtual ~zmq_listener();
void start(); void start();
boost::signals2::signal<void(const std::string &)> event_received;
private: private:
void handle_zmq(); void handle_zmq();
std::vector<zmq::message_t> receive_multipart(); std::vector<zmq::message_t> receive_multipart();
std::string ip;
uint32_t zmq_port;
zmq::context_t ctx; zmq::context_t ctx;
zmq::socket_t socket; zmq::socket_t socket;
};
std::atomic_bool stopped; class zmq_listener_libbitcoin : public zmq_listener_base {
std::thread thr; public:
zmq_listener_libbitcoin(std::string _ip, uint32_t _block_zmq_port = 9093, uint32_t _trx_zmq_port = 9094);
virtual ~zmq_listener_libbitcoin();
void start();
private:
void handle_block();
void handle_trx();
libbitcoin::protocol::zmq::context block_context;
libbitcoin::protocol::zmq::socket block_socket;
libbitcoin::protocol::zmq::poller block_poller;
libbitcoin::protocol::zmq::context trx_context;
libbitcoin::protocol::zmq::socket trx_socket;
libbitcoin::protocol::zmq::poller trx_poller;
}; };
// ============================================================================= // =============================================================================
@ -106,18 +212,19 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string ip; std::vector<rpc_credentials> _rpc_credentials;
uint32_t zmq_port; std::string libbitcoin_server_ip;
uint32_t rpc_port; uint32_t libbitcoin_block_zmq_port;
std::string rpc_user; uint32_t libbitcoin_trx_zmq_port;
std::string rpc_password; uint32_t bitcoin_node_zmq_port;
std::string wallet_name; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
std::unique_ptr<bitcoin_rpc_client> bitcoin_client; std::unique_ptr<bitcoin_client_base> bitcoin_client;
std::unique_ptr<zmq_listener> listener; std::unique_ptr<zmq_listener_base> listener;
fc::future<void> on_changed_objects_task; fc::future<void> on_changed_objects_task;
@ -127,7 +234,7 @@ private:
std::mutex event_handler_mutex; std::mutex event_handler_mutex;
typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock; typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock;
std::string create_primary_wallet_address(const std::vector<son_info> &son_pubkeys); std::string create_primary_wallet_address(const std::vector<son_sidechain_info> &son_pubkeys);
std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address); std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address);
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
@ -137,9 +244,9 @@ private:
std::string sign_transaction(const sidechain_transaction_object &sto); std::string sign_transaction(const sidechain_transaction_object &sto);
std::string send_transaction(const sidechain_transaction_object &sto); std::string send_transaction(const sidechain_transaction_object &sto);
void handle_event(const std::string &event_data); void block_handle_event(const block_data &event_data);
void trx_handle_event(const libbitcoin::chain::transaction &event_data);
std::string get_redeemscript_for_userdeposit(const std::string &user_address); std::string get_redeemscript_for_userdeposit(const std::string &user_address);
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts); void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts); void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
}; };

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <boost/bimap.hpp>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp> #include <graphene/peerplays_sidechain/common/rpc_client.hpp>
@ -13,23 +14,30 @@ namespace graphene { namespace peerplays_sidechain {
class ethereum_rpc_client : public rpc_client { class ethereum_rpc_client : public rpc_client {
public: public:
ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); ethereum_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection);
std::string admin_node_info(); std::string eth_blockNumber();
std::string eth_get_block_by_number(std::string block_number, bool full_block); std::string eth_get_block_by_number(std::string block_number, bool full_block);
std::string eth_get_logs(std::string wallet_contract_address); std::string eth_get_logs(std::string wallet_contract_address);
std::string eth_chainId();
std::string net_version(); std::string net_version();
std::string eth_get_transaction_count(const std::string &params); std::string eth_get_transaction_count(const std::string &params);
std::string eth_gas_price(); std::string eth_gas_price();
std::string eth_estimateGas(const std::string &params);
std::string get_chain_id(); std::string get_chain_id();
std::string get_network_id(); std::string get_network_id();
std::string get_nonce(const std::string &address); std::string get_nonce(const std::string &address);
std::string get_gas_price(); std::string get_gas_price();
std::string get_gas_limit(); std::string get_gas_limit();
std::string get_estimate_gas(const std::string &params);
std::string eth_send_transaction(const std::string &params); std::string eth_send_transaction(const std::string &params);
std::string eth_send_raw_transaction(const std::string &params);
std::string eth_get_transaction_receipt(const std::string &params); std::string eth_get_transaction_receipt(const std::string &params);
std::string eth_get_transaction_by_hash(const std::string &params);
virtual uint64_t ping(rpc_connection &conn) const override;
}; };
class sidechain_net_handler_ethereum : public sidechain_net_handler { class sidechain_net_handler_ethereum : public sidechain_net_handler {
@ -45,19 +53,20 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string rpc_url; std::vector<rpc_credentials> _rpc_credentials;
std::string rpc_user;
std::string rpc_password;
std::string wallet_contract_address; std::string wallet_contract_address;
using bimap_type = boost::bimap<std::string, std::string>;
bimap_type erc20_addresses;
ethereum_rpc_client *rpc_client; ethereum_rpc_client *rpc_client;
ethereum::chain_id_type chain_id; ethereum::chain_id_type chain_id;
ethereum::network_id_type network_id; ethereum::network_id_type network_id;
std::string create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string& object_id); std::string create_primary_wallet_transaction(const std::vector<son_sidechain_info> &son_pubkeys, const std::string &object_id);
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo);
@ -68,7 +77,7 @@ private:
boost::signals2::signal<void(const std::string &)> event_received; boost::signals2::signal<void(const std::string &)> event_received;
void schedule_ethereum_listener(); void schedule_ethereum_listener();
void ethereum_listener_loop(); void ethereum_listener_loop();
void handle_event(const std::string &event_data); void handle_event(const std::string &block_number);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,23 @@
#pragma once
#include <graphene/chain/sidechain_defs.hpp>
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <vector>
#include <boost/program_options.hpp>
namespace graphene { namespace peerplays_sidechain {
class sidechain_net_handler_factory {
public:
sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin);
std::unique_ptr<sidechain_net_handler> create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const;
private:
peerplays_sidechain_plugin &plugin;
};
}} // namespace graphene::peerplays_sidechain

View file

@ -13,7 +13,7 @@ namespace graphene { namespace peerplays_sidechain {
class hive_rpc_client : public rpc_client { class hive_rpc_client : public rpc_client {
public: public:
hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); hive_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection);
std::string account_history_api_get_transaction(std::string transaction_id); std::string account_history_api_get_transaction(std::string transaction_id);
std::string block_api_get_block(uint32_t block_number); std::string block_api_get_block(uint32_t block_number);
@ -30,6 +30,8 @@ public:
std::string get_head_block_time(); std::string get_head_block_time();
std::string get_is_test_net(); std::string get_is_test_net();
std::string get_last_irreversible_block_num(); std::string get_last_irreversible_block_num();
virtual uint64_t ping(rpc_connection &conn) const override;
}; };
class sidechain_net_handler_hive : public sidechain_net_handler { class sidechain_net_handler_hive : public sidechain_net_handler {
@ -45,11 +47,11 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string rpc_url; std::vector<rpc_credentials> _rpc_credentials;
std::string rpc_user;
std::string rpc_password;
std::string wallet_account_name; std::string wallet_account_name;
hive_rpc_client *rpc_client; hive_rpc_client *rpc_client;

View file

@ -19,6 +19,7 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
}; };

View file

@ -1,38 +0,0 @@
#pragma once
#include <graphene/chain/sidechain_defs.hpp>
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <vector>
#include <boost/program_options.hpp>
namespace graphene { namespace peerplays_sidechain {
class sidechain_net_manager {
public:
sidechain_net_manager(peerplays_sidechain_plugin &_plugin);
virtual ~sidechain_net_manager();
bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options);
void process_proposals();
void process_active_sons_change();
void create_deposit_addresses();
void process_deposits();
void process_withdrawals();
void process_sidechain_transactions();
void send_sidechain_transactions();
void settle_sidechain_transactions();
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
private:
peerplays_sidechain_plugin &plugin;
graphene::chain::database &database;
std::vector<std::unique_ptr<sidechain_net_handler>> net_handlers;
void on_applied_block(const signed_block &b);
};
}} // namespace graphene::peerplays_sidechain

View file

@ -3,6 +3,8 @@
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/range/algorithm_ext/insert.hpp> #include <boost/range/algorithm_ext/insert.hpp>
#include <future>
#include <thread>
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <graphene/chain/proposal_object.hpp> #include <graphene/chain/proposal_object.hpp>
@ -11,7 +13,7 @@
#include <graphene/chain/son_wallet_object.hpp> #include <graphene/chain/son_wallet_object.hpp>
#include <graphene/chain/son_wallet_withdraw_object.hpp> #include <graphene/chain/son_wallet_withdraw_object.hpp>
#include <graphene/peerplays_sidechain/sidechain_api.hpp> #include <graphene/peerplays_sidechain/sidechain_api.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp>
#include <graphene/utilities/key_conversion.hpp> #include <graphene/utilities/key_conversion.hpp>
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
@ -33,36 +35,37 @@ public:
void plugin_shutdown(); void plugin_shutdown();
std::set<chain::son_id_type> &get_sons(); std::set<chain::son_id_type> &get_sons();
const son_id_type get_current_son_id(); const son_id_type get_current_son_id(sidechain_type sidechain);
const son_object get_current_son_object(); const son_object get_current_son_object(sidechain_type sidechain);
const son_object get_son_object(son_id_type son_id); const son_object get_son_object(son_id_type son_id);
bool is_active_son(son_id_type son_id); bool is_active_son(sidechain_type sidechain, son_id_type son_id);
bool is_son_deregistered(son_id_type son_id); bool is_son_deregistered(son_id_type son_id);
bool is_son_deregister_op_valid(const chain::operation &op); bool is_son_deregister_op_valid(const chain::operation &op);
bool is_son_down_op_valid(const chain::operation &op); bool is_son_down_op_valid(const chain::operation &op);
bool is_valid_son_proposal(const chain::proposal_object &proposal); bool is_valid_son_proposal(const chain::proposal_object &proposal);
fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id);
fc::ecc::private_key get_private_key(chain::public_key_type public_key); fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id); void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
void schedule_heartbeat_loop(); void schedule_heartbeat_loop();
void heartbeat_loop(); void heartbeat_loop();
void schedule_son_processing(); void schedule_son_processing();
void son_processing(); void son_processing(sidechain_type sidechain);
void approve_proposals(); void approve_proposals(sidechain_type sidechain);
void create_son_down_proposals(); void create_son_down_proposals(sidechain_type sidechain);
void create_son_deregister_proposals(); void create_son_deregister_proposals(sidechain_type sidechain);
void process_proposals(); void process_proposals(sidechain_type sidechain);
void process_active_sons_change(); void process_active_sons_change(sidechain_type sidechain);
void create_deposit_addresses(); void create_deposit_addresses(sidechain_type sidechain);
void process_deposits(); void process_deposits(sidechain_type sidechain);
void process_withdrawals(); void process_withdrawals(sidechain_type sidechain);
void process_sidechain_transactions(); void process_sidechain_transactions(sidechain_type sidechain);
void send_sidechain_transactions(); void send_sidechain_transactions(sidechain_type sidechain);
void settle_sidechain_transactions(); void settle_sidechain_transactions(sidechain_type sidechain);
private: private:
peerplays_sidechain_plugin &plugin; peerplays_sidechain_plugin &plugin;
@ -80,17 +83,24 @@ private:
bool sidechain_enabled_hive; bool sidechain_enabled_hive;
bool sidechain_enabled_peerplays; bool sidechain_enabled_peerplays;
son_id_type current_son_id; std::map<sidechain_type, son_id_type> current_son_id;
std::mutex current_son_id_mutex;
std::mutex access_db_mutex;
std::mutex access_approve_prop_mutex;
std::mutex access_son_down_prop_mutex;
std::mutex access_son_deregister_prop_mutex;
std::unique_ptr<peerplays_sidechain::sidechain_net_manager> net_manager; std::map<sidechain_type, bool> sidechain_enabled;
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
std::set<chain::son_id_type> sons; std::set<chain::son_id_type> sons;
std::map<chain::public_key_type, fc::ecc::private_key> private_keys; std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
fc::future<void> _heartbeat_task; fc::future<void> _heartbeat_task;
fc::future<void> _son_processing_task; std::map<sidechain_type, std::future<void>> _son_processing_task;
std::map<son_proposal_type, uint16_t> son_retry_count; std::map<son_proposal_type, uint16_t> son_retry_count;
uint16_t retries_threshold; uint16_t retries_threshold = 150;
bool first_block_skipped; bool first_block_skipped;
bool son_processing_enabled;
void on_applied_block(const signed_block &b); void on_applied_block(const signed_block &b);
}; };
@ -105,9 +115,29 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec
sidechain_enabled_ethereum(false), sidechain_enabled_ethereum(false),
sidechain_enabled_hive(false), sidechain_enabled_hive(false),
sidechain_enabled_peerplays(false), sidechain_enabled_peerplays(false),
current_son_id(son_id_type(std::numeric_limits<uint32_t>().max())), current_son_id([] {
net_manager(nullptr), std::map<sidechain_type, son_id_type> current_son_id;
first_block_skipped(false) { for (const auto &active_sidechain_type : all_sidechain_types) {
current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits<uint32_t>().max()));
}
return current_son_id;
}()),
sidechain_enabled([] {
std::map<sidechain_type, bool> sidechain_enabled;
for (const auto &active_sidechain_type : all_sidechain_types) {
sidechain_enabled.emplace(active_sidechain_type, false);
}
return sidechain_enabled;
}()),
net_handlers([] {
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
for (const auto &active_sidechain_type : all_sidechain_types) {
net_handlers.emplace(active_sidechain_type, nullptr);
}
return net_handlers;
}()),
first_block_skipped(false),
son_processing_enabled(false) {
} }
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
@ -121,8 +151,10 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
} }
try { try {
if (_son_processing_task.valid()) for (const auto &active_sidechain_type : all_sidechain_types) {
_son_processing_task.cancel_and_wait(__FUNCTION__); if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid())
_son_processing_task.at(active_sidechain_type).wait();
}
} catch (fc::canceled_exception &) { } catch (fc::canceled_exception &) {
// Expected exception. Move along. // Expected exception. Move along.
} catch (fc::exception &e) { } catch (fc::exception &e) {
@ -145,9 +177,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("sidechain-retry-threshold", bpo::value<uint16_t>()->default_value(150), "Sidechain retry throttling threshold"); cli.add_options()("sidechain-retry-threshold", bpo::value<uint16_t>()->default_value(150), "Sidechain retry throttling threshold");
cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console"); cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console");
cli.add_options()("simulate-rpc-connection-reselection", bpo::value<bool>()->default_value(false), "Simulate RPC connection reselection by altering their response times by a random value");
cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled");
cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-ip", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1"), "IP address of Bitcoin node");
cli.add_options()("use-bitcoind-client", bpo::value<bool>()->default_value(false), "Use bitcoind client instead of libbitcoin client");
cli.add_options()("libbitcoin-server-ip", bpo::value<string>()->default_value("127.0.0.1"), "Libbitcoin server IP address");
cli.add_options()("libbitcoin-server-block-zmq-port", bpo::value<uint32_t>()->default_value(9093), "Block ZMQ port of libbitcoin server");
cli.add_options()("libbitcoin-server-trx-zmq-port", bpo::value<uint32_t>()->default_value(9094), "Trx ZMQ port of libbitcoin server");
cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node");
cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node");
cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
@ -158,18 +195,20 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
cli.add_options()("ethereum-sidechain-enabled", bpo::value<bool>()->default_value(false), "Ethereum sidechain handler enabled"); cli.add_options()("ethereum-sidechain-enabled", bpo::value<bool>()->default_value(false), "Ethereum sidechain handler enabled");
cli.add_options()("ethereum-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); cli.add_options()("ethereum-node-rpc-url", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]");
cli.add_options()("ethereum-node-rpc-user", bpo::value<string>(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-user", bpo::value<string>(), "Ethereum RPC user");
cli.add_options()("ethereum-node-rpc-password", bpo::value<string>(), "Ethereum RPC password"); cli.add_options()("ethereum-node-rpc-password", bpo::value<string>(), "Ethereum RPC password");
cli.add_options()("ethereum-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address"); cli.add_options()("ethereum-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address");
cli.add_options()("ethereum-erc-20-address", bpo::value<vector<string>>()->composing()->multitoken(),
"Tuple of [ERC-20 symbol, ERC-20 address] (may specify multiple times)");
cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")),
"Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)");
cli.add_options()("hive-sidechain-enabled", bpo::value<bool>()->default_value(false), "Hive sidechain handler enabled"); cli.add_options()("hive-sidechain-enabled", bpo::value<bool>()->default_value(false), "Hive sidechain handler enabled");
cli.add_options()("hive-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-url", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]");
cli.add_options()("hive-node-rpc-user", bpo::value<string>(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-user", bpo::value<string>(), "Hive node RPC user");
cli.add_options()("hive-node-rpc-password", bpo::value<string>(), "Hive node RPC password"); cli.add_options()("hive-node-rpc-password", bpo::value<string>(), "Hive node RPC password");
cli.add_options()("hive-wallet-account-name", bpo::value<string>(), "Hive wallet account name"), cli.add_options()("hive-wallet-account-name", bpo::value<string>(), "Hive wallet account name");
cli.add_options()("hive-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")), cli.add_options()("hive-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")),
"Tuple of [Hive public key, Hive private key] (may specify multiple times)"); "Tuple of [Hive public key, Hive private key] (may specify multiple times)");
@ -217,12 +256,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
} }
sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as<bool>(); sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as<bool>();
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && config_ready_bitcoin = (((options.count("libbitcoin-server-ip") && options.count("libbitcoin-server-zmq-port")) ||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && (options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") &&
options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") && options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") &&
options.count("bitcoin-private-key"); options.count("bitcoin-node-rpc-password") && options.count("bitcoin-wallet-name") &&
if (sidechain_enabled_bitcoin && !config_ready_bitcoin) { options.count("bitcoin-wallet-password"))) &&
options.count("bitcoin-private-key"));
if (!config_ready_bitcoin) {
wlog("Haven't set up Bitcoin sidechain parameters"); wlog("Haven't set up Bitcoin sidechain parameters");
} }
@ -245,22 +286,17 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
} }
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
sidechain_enabled_peerplays = true; //options.at("peerplays-sidechain-enabled").as<bool>(); sidechain_enabled_peerplays = true;
#else #else
sidechain_enabled_peerplays = false; sidechain_enabled_peerplays = false;
#endif #endif
config_ready_peerplays = true; config_ready_peerplays = true;
if (!config_ready_peerplays) { if (sidechain_enabled_peerplays && !config_ready_peerplays) {
wlog("Haven't set up Peerplays sidechain parameters"); wlog("Haven't set up Peerplays sidechain parameters");
} }
if (!(config_ready_bitcoin && if (options.at("simulate-rpc-connection-reselection").as<bool>())
config_ready_ethereum && ilog("### RPC connection reselection will be simulated");
config_ready_hive &&
config_ready_peerplays)) {
wlog("Haven't set up any sidechain parameters");
throw;
}
} }
void peerplays_sidechain_plugin_impl::plugin_startup() { void peerplays_sidechain_plugin_impl::plugin_startup() {
@ -273,25 +309,29 @@ void peerplays_sidechain_plugin_impl::plugin_startup() {
elog("No sons configured! Please add SON IDs and private keys to configuration."); elog("No sons configured! Please add SON IDs and private keys to configuration.");
} }
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin)); sidechain_net_handler_factory net_handler_factory(plugin);
if (sidechain_enabled_bitcoin && config_ready_bitcoin) { if (sidechain_enabled_bitcoin && config_ready_bitcoin) {
net_manager->create_handler(sidechain_type::bitcoin, options); sidechain_enabled.at(sidechain_type::bitcoin) = true;
net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options);
ilog("Bitcoin sidechain handler running"); ilog("Bitcoin sidechain handler running");
} }
if (sidechain_enabled_ethereum && config_ready_ethereum) { if (sidechain_enabled_ethereum && config_ready_ethereum) {
net_manager->create_handler(sidechain_type::ethereum, options); sidechain_enabled.at(sidechain_type::ethereum) = true;
net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options);
ilog("Ethereum sidechain handler running"); ilog("Ethereum sidechain handler running");
} }
if (sidechain_enabled_hive && config_ready_hive) { if (sidechain_enabled_hive && config_ready_hive) {
net_manager->create_handler(sidechain_type::hive, options); sidechain_enabled.at(sidechain_type::hive) = true;
net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options);
ilog("Hive sidechain handler running"); ilog("Hive sidechain handler running");
} }
if (sidechain_enabled_peerplays && config_ready_peerplays) { if (sidechain_enabled_peerplays && config_ready_peerplays) {
net_manager->create_handler(sidechain_type::peerplays, options); sidechain_enabled.at(sidechain_type::peerplays) = true;
net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options);
ilog("Peerplays sidechain handler running"); ilog("Peerplays sidechain handler running");
} }
@ -307,12 +347,13 @@ std::set<chain::son_id_type> &peerplays_sidechain_plugin_impl::get_sons() {
return sons; return sons;
} }
const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id(sidechain_type sidechain) {
return current_son_id; const std::lock_guard<std::mutex> lock(current_son_id_mutex);
return current_son_id.at(sidechain);
} }
const son_object peerplays_sidechain_plugin_impl::get_current_son_object() { const son_object peerplays_sidechain_plugin_impl::get_current_son_object(sidechain_type sidechain) {
return get_son_object(current_son_id); return get_son_object(get_current_son_id(sidechain));
} }
const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) {
@ -323,18 +364,17 @@ const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son
return *son_obj; return *son_obj;
} }
bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, son_id_type son_id) {
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>(); const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
auto son_obj = idx.find(son_id); auto son_obj = idx.find(son_id);
if (son_obj == idx.end()) if (son_obj == idx.end())
return false; return false;
const chain::global_property_object &gpo = plugin.database().get_global_properties(); const chain::global_property_object &gpo = plugin.database().get_global_properties();
vector<son_id_type> active_son_ids; set<son_id_type> active_son_ids;
active_son_ids.reserve(gpo.active_sons.size()); std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(),
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
@ -349,7 +389,13 @@ bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) {
if (son_obj == idx.end()) if (son_obj == idx.end())
return true; return true;
if (son_obj->status == chain::son_status::deregistered) { bool status_deregistered = true;
for (const auto &status : son_obj->statuses) {
if ((status.second != son_status::deregistered))
status_deregistered = false;
}
if (status_deregistered) {
return true; return true;
} }
@ -366,20 +412,32 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio
const chain::global_property_object &gpo = d.get_global_properties(); const chain::global_property_object &gpo = d.get_global_properties();
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>(); const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
son_report_down_operation down_op = op.get<son_report_down_operation>(); const son_report_down_operation down_op = op.get<son_report_down_operation>();
auto son_obj = idx.find(down_op.son_id); const auto son_obj = idx.find(down_op.son_id);
if (son_obj == idx.end()) { if (son_obj == idx.end()) {
return false; return false;
} }
auto stats = son_obj->statistics(d); const auto stats = son_obj->statistics(d);
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); const int64_t down_threshold = gpo.parameters.son_down_time();
int64_t down_threshold = gpo.parameters.son_down_time();
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && bool status_son_down_op_valid = true;
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { for (const auto &status : son_obj->statuses) {
return true; if ((status.second != son_status::active) && (status.second != son_status::request_maintenance))
status_son_down_op_valid = false;
} }
return false; if (status_son_down_op_valid) {
for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
if (stats.last_active_timestamp.contains(active_sidechain_type)) {
const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time);
if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) {
status_son_down_op_valid = false;
}
}
}
}
return status_son_down_op_valid;
} }
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) { fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) {
@ -411,7 +469,25 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
chain::database &d = plugin.database(); chain::database &d = plugin.database();
for (son_id_type son_id : sons) { for (son_id_type son_id : sons) {
if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { const auto &son_obj = get_son_object(son_id);
//! Check that son is in_maintenance
bool status_in_maintenance = false;
for (const auto &status : son_obj.statuses) {
if ((status.second == son_status::in_maintenance))
status_in_maintenance = true;
}
//! Check that son is active (at least for one sidechain_type)
bool is_son_active = false;
for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
if (sidechain_enabled.at(active_sidechain_type)) {
if (is_active_son(active_sidechain_type, son_id))
is_son_active = true;
}
}
if (is_son_active || status_in_maintenance) {
ilog("Sending heartbeat for SON ${son}", ("son", son_id)); ilog("Sending heartbeat for SON ${son}", ("son", son_id));
chain::son_heartbeat_operation op; chain::son_heartbeat_operation op;
@ -437,19 +513,30 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
} }
void peerplays_sidechain_plugin_impl::schedule_son_processing() { void peerplays_sidechain_plugin_impl::schedule_son_processing() {
fc::time_point now = fc::time_point::now(); const auto now = std::chrono::steady_clock::now();
int64_t time_to_next_son_processing = 500000; static const int64_t time_to_next_son_processing = 500000;
fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing)); const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
_son_processing_task = fc::schedule([this] { for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
son_processing(); if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
}, wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type));
next_wakeup, "SON Processing"); _son_processing_task.at(active_sidechain_type).wait();
} }
void peerplays_sidechain_plugin_impl::son_processing() { _son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] {
if (plugin.database().get_global_properties().active_sons.size() <= 0) { if (sidechain_enabled.at(active_sidechain_type)) {
std::this_thread::sleep_until(next_wakeup);
son_processing(active_sidechain_type);
}
});
}
}
void peerplays_sidechain_plugin_impl::son_processing(sidechain_type sidechain) {
//! Check whether we have active SONs
if (plugin.database().get_global_properties().active_sons.count(sidechain) == 0 ||
plugin.database().get_global_properties().active_sons.at(sidechain).empty()) {
return; return;
} }
@ -459,50 +546,55 @@ void peerplays_sidechain_plugin_impl::son_processing() {
// return; // Not synced // return; // Not synced
// } // }
fc::time_point now_fine = fc::time_point::now(); const fc::time_point now_fine = fc::time_point::now();
fc::time_point_sec now = now_fine - fc::milliseconds(3000); const fc::time_point_sec now = now_fine - fc::milliseconds(3000);
if (plugin.database().head_block_time() < now) { if (plugin.database().head_block_time() < now) {
return; // Not synced return; // Not synced
} }
chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1); //! Get scheduled_son_id according to sidechain_type
ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ", const chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(sidechain, 1);
("scheduled_son_id", scheduled_son_id)("now", now)); ilog("Scheduled SON: ${scheduled_son_id} Sidechain: ${sidechain} Now: ${now}",
("scheduled_son_id", scheduled_son_id)("sidechain", sidechain)("now", now));
for (son_id_type son_id : plugin.get_sons()) { for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_son_deregistered(son_id)) { if (plugin.is_son_deregistered(son_id)) {
continue; continue;
} }
current_son_id = son_id;
{
const std::lock_guard<std::mutex> lock(current_son_id_mutex);
current_son_id.at(sidechain) = son_id;
}
// These tasks are executed by // These tasks are executed by
// - All active SONs, no matter if scheduled // - All active SONs, no matter if scheduled
// - All previously active SONs // - All previously active SONs
approve_proposals(); approve_proposals(sidechain);
process_proposals(); process_proposals(sidechain);
process_sidechain_transactions(); process_sidechain_transactions(sidechain);
if (plugin.is_active_son(son_id)) { if (plugin.is_active_son(sidechain, son_id)) {
// Tasks that are executed by scheduled and active SON only // Tasks that are executed by scheduled and active SON only
if (current_son_id == scheduled_son_id) { if (get_current_son_id(sidechain) == scheduled_son_id) {
create_son_down_proposals(); create_son_down_proposals(sidechain);
create_son_deregister_proposals(); create_son_deregister_proposals(sidechain);
process_active_sons_change(); process_active_sons_change(sidechain);
create_deposit_addresses(); create_deposit_addresses(sidechain);
process_deposits(); process_deposits(sidechain);
process_withdrawals(); process_withdrawals(sidechain);
process_sidechain_transactions(); process_sidechain_transactions(sidechain);
send_sidechain_transactions(); send_sidechain_transactions(sidechain);
settle_sidechain_transactions(); settle_sidechain_transactions(sidechain);
} }
} }
} }
@ -525,8 +617,8 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa
return false; return false;
} }
void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object_id_type object_id) { void peerplays_sidechain_plugin_impl::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) {
son_proposal_type prop_type(op_type, get_current_son_id(), object_id); son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id);
auto itr = son_retry_count.find(prop_type); auto itr = son_retry_count.find(prop_type);
if (itr != son_retry_count.end()) { if (itr != son_retry_count.end()) {
itr->second++; itr->second++;
@ -535,18 +627,43 @@ void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object
} }
} }
bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id_type object_id) { bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) {
son_proposal_type prop_type(op_type, get_current_son_id(), object_id); son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id);
auto itr = son_retry_count.find(prop_type); auto itr = son_retry_count.find(prop_type);
return (itr == son_retry_count.end() || itr->second < retries_threshold); return (itr == son_retry_count.end() || itr->second < retries_threshold);
} }
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() { std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
return net_manager->get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> result;
for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
if (net_handlers.at(active_sidechain_type)) {
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
}
}
return result;
} }
void peerplays_sidechain_plugin_impl::approve_proposals() { optional<asset> peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
if (net_handlers.count(sidechain) == 0) {
wlog("No net handler for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>();
}
if (!net_handlers.at(sidechain)) {
wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>();
}
return net_handlers.at(sidechain)->estimate_withdrawal_transaction_fee();
}
void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) {
// prevent approving duplicate proposals with lock for parallel execution.
// We can have the same propsals, but in the case of parallel execution we can run
// into problem of approving the same propsal since it might happens that previous
// approved proposal didn't have time or chance to populate the list of available
// active proposals which is consulted here in the code.
const std::lock_guard<std::mutex> lck{access_approve_prop_mutex};
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
if (!is_valid_son_proposal(proposal)) { if (!is_valid_son_proposal(proposal)) {
return; return;
@ -560,6 +677,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
fc::future<bool> fut = fc::async([&]() { fc::future<bool> fut = fc::async([&]() {
try { try {
trx.validate(); trx.validate();
std::lock_guard<std::mutex> lck(access_db_mutex);
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -579,7 +697,6 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
} }
for (const auto proposal_id : proposals) { for (const auto proposal_id : proposals) {
const object *obj = plugin.database().find_object(proposal_id); const object *obj = plugin.database().find_object(proposal_id);
const chain::proposal_object *proposal_ptr = dynamic_cast<const chain::proposal_object *>(obj); const chain::proposal_object *proposal_ptr = dynamic_cast<const chain::proposal_object *>(obj);
if (proposal_ptr == nullptr) { if (proposal_ptr == nullptr) {
@ -587,15 +704,16 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
} }
const proposal_object proposal = *proposal_ptr; const proposal_object proposal = *proposal_ptr;
if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { if (proposal.available_active_approvals.find(get_current_son_object(sidechain).son_account) != proposal.available_active_approvals.end()) {
continue; continue;
} }
check_approve_proposal(get_current_son_id(), proposal); check_approve_proposal(get_current_son_id(sidechain), proposal);
} }
} }
void peerplays_sidechain_plugin_impl::create_son_down_proposals() { void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) {
const std::lock_guard<std::mutex> lck{access_son_down_prop_mutex};
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
chain::database &d = plugin.database(); chain::database &d = plugin.database();
const chain::global_property_object &gpo = d.get_global_properties(); const chain::global_property_object &gpo = d.get_global_properties();
@ -606,9 +724,9 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
son_down_op.down_ts = last_active_ts; son_down_op.down_ts = last_active_ts;
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = get_current_son_object().son_account; proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account;
proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op));
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime);
return proposal_op; return proposal_op;
}; };
@ -617,25 +735,41 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
const chain::global_property_object &gpo = d.get_global_properties(); const chain::global_property_object &gpo = d.get_global_properties();
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>(); const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down(); const std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
chain::son_id_type my_son_id = get_current_son_id(); const chain::son_id_type my_son_id = get_current_son_id(sidechain);
for (auto son_inf : gpo.active_sons) {
//! Fixme - check this part of the code
for (auto son_inf : gpo.active_sons.at(sidechain)) {
if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) {
continue; continue;
} }
auto son_obj = idx.find(son_inf.son_id);
auto stats = son_obj->statistics(d); const auto son_obj = idx.find(son_inf.son_id);
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; const auto stats = son_obj->statistics(d);
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
int64_t down_threshold = gpo.parameters.son_down_time(); const fc::time_point_sec last_active_ts = [&stats, &sidechain, &last_maintenance_time] {
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && fc::time_point_sec last_active_ts;
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { if (stats.last_active_timestamp.contains(sidechain)) {
last_active_ts = (stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time;
} else
last_active_ts = last_maintenance_time;
return last_active_ts;
}();
const int64_t down_threshold = gpo.parameters.son_down_time();
bool status_son_down_valid = true;
for (const auto &status : son_obj->statuses) {
if ((status.second != son_status::active) && (status.second != son_status::request_maintenance))
status_son_down_valid = false;
}
if ((status_son_down_valid) && ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id))));
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
fc::future<bool> fut = fc::async([&]() { fc::future<bool> fut = fc::async([&]() {
try { try {
trx.validate(); trx.validate();
std::lock_guard<std::mutex> lck(access_db_mutex);
d.push_transaction(trx, database::validation_steps::skip_block_size_check); d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -650,10 +784,11 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
} }
} }
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) {
const std::lock_guard<std::mutex> lck{access_son_deregister_prop_mutex};
chain::database &d = plugin.database(); chain::database &d = plugin.database();
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered(); std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
chain::son_id_type my_son_id = get_current_son_id(); chain::son_id_type my_son_id = get_current_son_id(sidechain);
if (sons_to_be_dereg.size() > 0) { if (sons_to_be_dereg.size() > 0) {
// We shouldn't raise proposals for the SONs for which a de-reg // We shouldn't raise proposals for the SONs for which a de-reg
@ -671,6 +806,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
fc::future<bool> fut = fc::async([&]() { fc::future<bool> fut = fc::async([&]() {
try { try {
trx.validate(); trx.validate();
std::lock_guard<std::mutex> lck(access_db_mutex);
d.push_transaction(trx, database::validation_steps::skip_block_size_check); d.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
@ -687,41 +823,66 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
} }
} }
void peerplays_sidechain_plugin_impl::process_proposals() { void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) {
net_manager->process_proposals(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->process_proposals();
}
} }
void peerplays_sidechain_plugin_impl::process_active_sons_change() { void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) {
net_manager->process_active_sons_change(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->process_active_sons_change();
}
} }
void peerplays_sidechain_plugin_impl::create_deposit_addresses() { void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) {
net_manager->create_deposit_addresses(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->create_deposit_addresses();
}
} }
void peerplays_sidechain_plugin_impl::process_deposits() { void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) {
net_manager->process_deposits(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->process_deposits();
}
} }
void peerplays_sidechain_plugin_impl::process_withdrawals() { void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) {
net_manager->process_withdrawals(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->process_withdrawals();
}
} }
void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) {
net_manager->process_sidechain_transactions(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->process_sidechain_transactions();
}
} }
void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) {
net_manager->send_sidechain_transactions(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->send_sidechain_transactions();
}
} }
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() { void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) {
net_manager->settle_sidechain_transactions(); if (net_handlers.at(sidechain)) {
net_handlers.at(sidechain)->settle_sidechain_transactions();
}
} }
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
if (first_block_skipped) { if (first_block_skipped) {
if (son_processing_enabled) {
schedule_son_processing(); schedule_son_processing();
} else {
const fc::time_point now_fine = fc::time_point::now();
const fc::time_point_sec now = now_fine + fc::microseconds(500000);
if (plugin.database().get_slot_time(1) >= now) {
son_processing_enabled = true;
schedule_son_processing();
}
}
} else { } else {
first_block_skipped = true; first_block_skipped = true;
} }
@ -769,20 +930,20 @@ std::set<chain::son_id_type> &peerplays_sidechain_plugin::get_sons() {
return my->get_sons(); return my->get_sons();
} }
const son_id_type peerplays_sidechain_plugin::get_current_son_id() { const son_id_type peerplays_sidechain_plugin::get_current_son_id(sidechain_type sidechain) {
return my->get_current_son_id(); return my->get_current_son_id(sidechain);
} }
const son_object peerplays_sidechain_plugin::get_current_son_object() { const son_object peerplays_sidechain_plugin::get_current_son_object(sidechain_type sidechain) {
return my->get_current_son_object(); return my->get_current_son_object(sidechain);
} }
const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) {
return my->get_son_object(son_id); return my->get_son_object(son_id);
} }
bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { bool peerplays_sidechain_plugin::is_active_son(sidechain_type sidechain, son_id_type son_id) {
return my->is_active_son(son_id); return my->is_active_son(sidechain, son_id);
} }
bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) { bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) {
@ -797,16 +958,20 @@ fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_k
return my->get_private_key(public_key); return my->get_private_key(public_key);
} }
void peerplays_sidechain_plugin::log_son_proposal_retry(int op_type, object_id_type object_id) { void peerplays_sidechain_plugin::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) {
my->log_son_proposal_retry(op_type, object_id); my->log_son_proposal_retry(sidechain, op_type, object_id);
} }
bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type object_id) { bool peerplays_sidechain_plugin::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) {
return my->can_son_participate(op_type, object_id); return my->can_son_participate(sidechain, op_type, object_id);
} }
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin::get_son_listener_log() { std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin::get_son_listener_log() {
return my->get_son_listener_log(); return my->get_son_listener_log();
} }
optional<asset> peerplays_sidechain_plugin::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return my->estimate_withdrawal_transaction_fee(sidechain);
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -12,6 +12,7 @@ public:
std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin(); std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin();
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
private: private:
app::application &app; app::application &app;
@ -32,6 +33,10 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api_impl::get_son_l
return get_plugin()->get_son_listener_log(); return get_plugin()->get_son_listener_log();
} }
optional<asset> sidechain_api_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return get_plugin()->estimate_withdrawal_transaction_fee(sidechain);
}
} // namespace detail } // namespace detail
sidechain_api::sidechain_api(graphene::app::application &_app) : sidechain_api::sidechain_api(graphene::app::application &_app) :
@ -45,4 +50,8 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api::get_son_listen
return my->get_son_listener_log(); return my->get_son_listener_log();
} }
optional<asset> sidechain_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return my->estimate_withdrawal_transaction_fee(sidechain);
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -9,7 +9,8 @@
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler::sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
sidechain(_sidechain),
plugin(_plugin), plugin(_plugin),
database(_plugin.database()) { database(_plugin.database()) {
@ -21,11 +22,11 @@ sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin
sidechain_net_handler::~sidechain_net_handler() { sidechain_net_handler::~sidechain_net_handler() {
} }
sidechain_type sidechain_net_handler::get_sidechain() { sidechain_type sidechain_net_handler::get_sidechain() const {
return sidechain; return sidechain;
} }
std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() { std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() const {
std::vector<std::string> result; std::vector<std::string> result;
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>(); const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
@ -38,7 +39,7 @@ std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses(
return result; return result;
} }
std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() { std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() const {
std::vector<std::string> result; std::vector<std::string> result;
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>(); const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
@ -51,7 +52,20 @@ std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses
return result; return result;
} }
std::string sidechain_net_handler::get_private_key(std::string public_key) { std::vector<sidechain_transaction_object> sidechain_net_handler::get_sidechain_transaction_objects(sidechain_transaction_status status) const {
std::vector<sidechain_transaction_object> result;
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, status));
std::for_each(idx_range.first, idx_range.second,
[&result](const sidechain_transaction_object &sto) {
result.push_back(sto);
});
return result;
}
std::string sidechain_net_handler::get_private_key(std::string public_key) const {
auto private_key_itr = private_keys.find(public_key); auto private_key_itr = private_keys.find(public_key);
if (private_key_itr != private_keys.end()) { if (private_key_itr != private_keys.end()) {
return private_key_itr->second; return private_key_itr->second;
@ -177,24 +191,27 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
// (sed.sidechain_currency.compare("HIVE") != 0); // (sed.sidechain_currency.compare("HIVE") != 0);
#endif #endif
bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && const bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
(sed.sidechain == sidechain) &&
(sed.type == sidechain_event_type::deposit) &&
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) ||
((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) || ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
enable_peerplays_asset_deposits); enable_peerplays_asset_deposits);
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_type::peerplays) && const bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || (sed.sidechain == sidechain) &&
(sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) || (sed.type == sidechain_event_type::withdrawal) &&
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset()))) ||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset()))) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))));
// Deposit request // Deposit request
if (deposit_condition) { if (deposit_condition) {
for (son_id_type son_id : plugin.get_sons()) { for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(son_id)) { if (plugin.is_active_son(sidechain, son_id)) {
son_wallet_deposit_create_operation op; son_wallet_deposit_create_operation op;
op.payer = plugin.get_son_object(son_id).son_account; op.payer = plugin.get_son_object(son_id).son_account;
@ -256,11 +273,21 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate; withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate;
} }
if (withdraw_currency.empty()) { if (withdraw_currency.empty()) {
//! This is ERC-20 withdrawal
const auto asset_object_id = string_to_object_id(sed.sidechain_currency);
const auto &assets_by_id = database.get_index_type<asset_index>().indices().get<by_id>();
const auto asset_itr = assets_by_id.find(asset_object_id);
if (asset_itr == assets_by_id.end()) {
wlog("Could not find asset: ${asset_object_id}", ("asset_object_id", asset_object_id));
return; return;
} }
withdraw_currency = asset_itr->symbol;
withdraw_currency_price = asset_itr->options.core_exchange_rate;
}
for (son_id_type son_id : plugin.get_sons()) { for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(son_id)) { if (plugin.is_active_son(sidechain, son_id)) {
son_wallet_withdraw_create_operation op; son_wallet_withdraw_create_operation op;
op.payer = plugin.get_son_object(son_id).son_account; op.payer = plugin.get_son_object(son_id).son_account;
@ -304,7 +331,7 @@ void sidechain_net_handler::process_proposals() {
const auto po = idx.find(proposal_id); const auto po = idx.find(proposal_id);
if (po != idx.end()) { if (po != idx.end()) {
if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) { if (po->available_active_approvals.find(plugin.get_current_son_object(sidechain).son_account) != po->available_active_approvals.end()) {
continue; continue;
} }
@ -387,12 +414,12 @@ void sidechain_net_handler::process_proposals() {
elog("=================================================="); elog("==================================================");
} }
if (should_process && (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_sign_operation>::value || plugin.can_son_participate(op_idx_0, object_id))) { if (should_process && (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_sign_operation>::value || plugin.can_son_participate(sidechain, op_idx_0, object_id))) {
bool should_approve = process_proposal(*po); bool should_approve = process_proposal(*po);
if (should_approve) { if (should_approve) {
if (approve_proposal(po->id, plugin.get_current_son_id())) { if (approve_proposal(po->id, plugin.get_current_son_id(sidechain))) {
if (op_idx_0 != chain::operation::tag<chain::sidechain_transaction_sign_operation>::value) { if (op_idx_0 != chain::operation::tag<chain::sidechain_transaction_sign_operation>::value) {
plugin.log_son_proposal_retry(op_idx_0, object_id); plugin.log_son_proposal_retry(sidechain, op_idx_0, object_id);
} }
} }
} }
@ -406,14 +433,14 @@ void sidechain_net_handler::process_active_sons_change() {
} }
void sidechain_net_handler::create_deposit_addresses() { void sidechain_net_handler::create_deposit_addresses() {
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
return; return;
} }
process_sidechain_addresses(); process_sidechain_addresses();
} }
void sidechain_net_handler::process_deposits() { void sidechain_net_handler::process_deposits() {
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
return; return;
} }
@ -421,7 +448,7 @@ void sidechain_net_handler::process_deposits() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) { if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
return; return;
} }
// Ignore the deposits which are not valid anymore, considered refunds. // Ignore the deposits which are not valid anymore, considered refunds.
@ -443,12 +470,12 @@ void sidechain_net_handler::process_deposits() {
wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
return; return;
} }
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id); plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id);
}); });
} }
void sidechain_net_handler::process_withdrawals() { void sidechain_net_handler::process_withdrawals() {
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
return; return;
} }
@ -456,7 +483,7 @@ void sidechain_net_handler::process_withdrawals() {
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) { if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) {
return; return;
} }
@ -468,16 +495,15 @@ void sidechain_net_handler::process_withdrawals() {
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
return; return;
} }
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id); plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id);
}); });
} }
void sidechain_net_handler::process_sidechain_transactions() { void sidechain_net_handler::process_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>(); const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::valid);
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) { if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) {
return; return;
} }
@ -492,13 +518,13 @@ void sidechain_net_handler::process_sidechain_transactions() {
const chain::global_property_object &gpo = database.get_global_properties(); const chain::global_property_object &gpo = database.get_global_properties();
sidechain_transaction_sign_operation sts_op; sidechain_transaction_sign_operation sts_op;
sts_op.signer = plugin.get_current_son_id(); sts_op.signer = plugin.get_current_son_id(sidechain);
sts_op.payer = gpo.parameters.son_account(); sts_op.payer = gpo.parameters.son_account();
sts_op.sidechain_transaction_id = sto.id; sts_op.sidechain_transaction_id = sto.id;
sts_op.signature = processed_sidechain_tx; sts_op.signature = processed_sidechain_tx;
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
proposal_op.proposed_ops.emplace_back(sts_op); proposal_op.proposed_ops.emplace_back(sts_op);
@ -507,7 +533,7 @@ void sidechain_net_handler::process_sidechain_transactions() {
return; return;
} }
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
try { try {
trx.validate(); trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
@ -520,10 +546,9 @@ void sidechain_net_handler::process_sidechain_transactions() {
} }
void sidechain_net_handler::send_sidechain_transactions() { void sidechain_net_handler::send_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>(); const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::complete);
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
if (sto.id == object_id_type(0, 0, 0)) { if (sto.id == object_id_type(0, 0, 0)) {
return; return;
} }
@ -538,11 +563,11 @@ void sidechain_net_handler::send_sidechain_transactions() {
} }
sidechain_transaction_send_operation sts_op; sidechain_transaction_send_operation sts_op;
sts_op.payer = plugin.get_current_son_object().son_account; sts_op.payer = plugin.get_current_son_object(sidechain).son_account;
sts_op.sidechain_transaction_id = sto.id; sts_op.sidechain_transaction_id = sto.id;
sts_op.sidechain_transaction = sidechain_transaction; sts_op.sidechain_transaction = sidechain_transaction;
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), sts_op);
try { try {
trx.validate(); trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
@ -555,10 +580,9 @@ void sidechain_net_handler::send_sidechain_transactions() {
} }
void sidechain_net_handler::settle_sidechain_transactions() { void sidechain_net_handler::settle_sidechain_transactions() {
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>(); const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::sent);
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent));
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
if (sto.id == object_id_type(0, 0, 0)) { if (sto.id == object_id_type(0, 0, 0)) {
return; return;
} }
@ -567,7 +591,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
return; return;
} }
if (!plugin.can_son_participate(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id)) { if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id)) {
return; return;
} }
@ -584,7 +608,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
const chain::global_property_object &gpo = database.get_global_properties(); const chain::global_property_object &gpo = database.get_global_properties();
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
@ -612,13 +636,13 @@ void sidechain_net_handler::settle_sidechain_transactions() {
} }
} }
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
try { try {
trx.validate(); trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id); plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id);
} catch (fc::exception &e) { } catch (fc::exception &e) {
elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what()));
} }
@ -653,9 +677,10 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
continue; continue;
} }
bool is_tracked_asset = const bool is_tracked_asset =
((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) ||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) ||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset()) && (transfer_op.amount.asset_id != asset_id_type())) ||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) ||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset()));
@ -681,7 +706,8 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
sidechain_event_data sed; sidechain_event_data sed;
sed.timestamp = database.head_block_time(); sed.timestamp = database.head_block_time();
sed.block_num = database.head_block_num(); sed.block_num = database.head_block_num();
sed.sidechain = sidechain_type::peerplays; sed.sidechain = sidechain;
sed.type = sidechain_event_type::withdrawal;
sed.sidechain_uid = sidechain_uid; sed.sidechain_uid = sidechain_uid;
sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_transaction_id = trx.id().str();
sed.sidechain_from = sidechain_from; sed.sidechain_from = sidechain_from;

Some files were not shown because too many files have changed in this diff Show more