From a481624577dd17e0056230b7f4a5bd3cc9a49dd2 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 15 Aug 2017 01:42:29 +0300 Subject: [PATCH 01/18] RPC ledger & representatives sorting --- rai/node/rpc.cpp | 145 +++++++++++++++++++++++++++++++++++++++++++---- rai/node/rpc.hpp | 1 + 2 files changed, 135 insertions(+), 11 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 5a1257ee..29d2bf2e 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -629,7 +629,7 @@ void rai::rpc_handler::accounts_pending () auto error (decode_unsigned (count_text, count)); if (error) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -1297,6 +1297,90 @@ void rai::rpc_handler::key_expand () } } +void rai::rpc_handler::ledger () +{ + if (rpc.config.enable_control) + { + rai::account start (0); + uint64_t count (std::numeric_limits ::max ()); + bool sorting (false); + boost::optional account_text (request.get_optional ("account")); + if (account_text.is_initialized()) + { + auto error (start.decode_account (account_text.get ())); + if (error) + { + error_response (response, "Invalid starting account"); + } + } + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized()) + { + auto error_count (decode_unsigned (count_text.get (), count)); + if (error_count) + { + error_response (response, "Invalid count limit"); + } + } + boost::optional sorting_text (request.get_optional ("sorting")); + if (count_text.is_initialized()) + { + sorting = sorting_text.get (); + } + boost::property_tree::ptree response_l; + boost::property_tree::ptree accounts; + rai::transaction transaction (node.store.environment, nullptr, false); + if (!sorting) // Simple + { + for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && accounts.size () < count; ++i) + { + rai::account_info info (i->second); + boost::property_tree::ptree response_l; + response_l.put ("frontier", info.head.to_string ()); + response_l.put ("open_block", info.open_block.to_string ()); + response_l.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + rai::uint128_union (info.balance).encode_dec (balance); + response_l.put ("balance", balance); + response_l.put ("modified_timestamp", std::to_string (info.modified)); + response_l.put ("block_count", std::to_string (info.block_count)); + accounts.push_back (std::make_pair (rai::account (i->first).to_account (), response_l)); + } + } + else // Sorting + { + std::vector > ledger_l; + for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n; ++i) + { + rai::uint128_union balance (rai::account_info (i->second).balance); + ledger_l.push_back (std::make_pair (balance, rai::account (i->first))); + } + std::sort (ledger_l.begin (), ledger_l.end ()); + std::reverse (ledger_l.begin (), ledger_l.end ()); + rai::account_info info; + for (auto i (ledger_l.begin ()), n (ledger_l.end ()); i != n && accounts.size () < count; ++i) + { + node.store.account_get (transaction, i->second, info); + response_l.put ("frontier", info.head.to_string ()); + response_l.put ("open_block", info.open_block.to_string ()); + response_l.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + (i->first).encode_dec (balance); + response_l.put ("balance", balance); + response_l.put ("modified_timestamp", std::to_string (info.modified)); + response_l.put ("block_count", std::to_string (info.block_count)); + accounts.push_back (std::make_pair (rai::account (i->second).to_account (), response_l)); + } + } + response_l.add_child ("accounts", accounts); + response (response_l); + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::mrai_from_raw () { std::string amount_text (request.get ("amount")); @@ -1498,7 +1582,7 @@ void rai::rpc_handler::pending () auto error (decode_unsigned (count_text, count)); if (error) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -1967,14 +2051,49 @@ void rai::rpc_handler::receive_minimum_set () void rai::rpc_handler::representatives () { + uint64_t count (std::numeric_limits ::max ()); + bool sorting (false); + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized()) + { + auto error (decode_unsigned (count_text.get (), count)); + if (error) + { + error_response (response, "Invalid count limit"); + } + } + boost::optional sorting_text (request.get_optional ("sorting")); + if (count_text.is_initialized()) + { + sorting = sorting_text.get (); + } boost::property_tree::ptree response_l; boost::property_tree::ptree representatives; rai::transaction transaction (node.store.environment, nullptr, false); - for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n; ++i) + if (!sorting) // Simple { - rai::account account(i->first); - auto amount (node.store.representation_get (transaction, account)); - representatives.put (account.to_account (), amount.convert_to ()); + for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n && representatives.size () < count; ++i) + { + rai::account account(i->first); + auto amount (node.store.representation_get (transaction, account)); + representatives.put (account.to_account (), amount.convert_to ()); + } + } + else // Sorting + { + std::vector > representation; + for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n; ++i) + { + rai::account account(i->first); + auto amount (node.store.representation_get (transaction, account)); + representation.push_back (std::make_pair (amount, account.to_account ())); + } + std::sort (representation.begin (), representation.end ()); + std::reverse (representation.begin (), representation.end ()); + for (auto i (representation.begin ()), n (representation.end ()); i != n && representatives.size () < count; ++i) + { + representatives.put (i->second, (i->first).number ().convert_to ()); + } } response_l.add_child ("representatives", representatives); response (response_l); @@ -1990,7 +2109,7 @@ void rai::rpc_handler::republish () auto error (decode_unsigned (count_text, count)); if (error) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -2204,7 +2323,7 @@ void rai::rpc_handler::unchecked () auto error (decode_unsigned (count_text, count)); if (error) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -2287,7 +2406,7 @@ void rai::rpc_handler::unchecked_keys () auto error (decode_unsigned (count_text, count)); if (error) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -2713,7 +2832,7 @@ void rai::rpc_handler::wallet_pending () auto error_count (decode_unsigned (count_text, count)); if (error_count) { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } catch (std::runtime_error &) @@ -2895,7 +3014,7 @@ void rai::rpc_handler::wallet_republish () } else { - error_response (response, "Invalid count"); + error_response (response, "Invalid count limit"); } } else @@ -3465,6 +3584,10 @@ void rai::rpc_handler::process_request () { krai_to_raw (); } + else if (action == "ledger") + { + ledger (); + } else if (action == "mrai_from_raw") { mrai_from_raw (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index 8c17e77c..02d1f0e6 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -127,6 +127,7 @@ public: void key_expand (); void krai_to_raw (); void krai_from_raw (); + void ledger (); void mrai_to_raw (); void mrai_from_raw (); void password_change (); From 01f70fbdfedbd3c9caaaf1ea3da173dc425958c2 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 15 Aug 2017 01:56:36 +0300 Subject: [PATCH 02/18] boost::optional values --- rai/node/rpc.cpp | 112 ++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 29d2bf2e..702f65dd 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -623,32 +623,24 @@ void rai::rpc_handler::accounts_pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); - try + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized ()) { - std::string count_text (request.get ("count")); - auto error (decode_unsigned (count_text, count)); + auto error (decode_unsigned (count_text.get (), count)); if (error) { error_response (response, "Invalid count limit"); } } - catch (std::runtime_error &) + boost::optional threshold_text (request.get_optional ("threshold")); + if (threshold_text.is_initialized ()) { - // If there is no "count" in request - } - try - { - std::string threshold_text (request.get ("threshold")); - auto error_threshold (threshold.decode_dec (threshold_text)); + auto error_threshold (threshold.decode_dec (threshold_text.get ())); if (error_threshold) { error_response (response, "Bad threshold number"); } } - catch (std::runtime_error &) - { - // If there is no "threshold" in request - } boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -1305,7 +1297,7 @@ void rai::rpc_handler::ledger () uint64_t count (std::numeric_limits ::max ()); bool sorting (false); boost::optional account_text (request.get_optional ("account")); - if (account_text.is_initialized()) + if (account_text.is_initialized ()) { auto error (start.decode_account (account_text.get ())); if (error) @@ -1314,7 +1306,7 @@ void rai::rpc_handler::ledger () } } boost::optional count_text (request.get_optional ("count")); - if (count_text.is_initialized()) + if (count_text.is_initialized ()) { auto error_count (decode_unsigned (count_text.get (), count)); if (error_count) @@ -1322,10 +1314,10 @@ void rai::rpc_handler::ledger () error_response (response, "Invalid count limit"); } } - boost::optional sorting_text (request.get_optional ("sorting")); - if (count_text.is_initialized()) + boost::optional sorting_optional (request.get_optional ("sorting")); + if (sorting_optional.is_initialized ()) { - sorting = sorting_text.get (); + sorting = sorting_optional.get (); } boost::property_tree::ptree response_l; boost::property_tree::ptree accounts; @@ -1576,32 +1568,24 @@ void rai::rpc_handler::pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); - try + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized ()) { - std::string count_text (request.get ("count")); - auto error (decode_unsigned (count_text, count)); + auto error (decode_unsigned (count_text.get (), count)); if (error) { error_response (response, "Invalid count limit"); } } - catch (std::runtime_error &) + boost::optional threshold_text (request.get_optional ("threshold")); + if (threshold_text.is_initialized ()) { - // If there is no "count" in request - } - try - { - std::string threshold_text (request.get ("threshold")); - auto error_threshold (threshold.decode_dec (threshold_text)); + auto error_threshold (threshold.decode_dec (threshold_text.get ())); if (error_threshold) { error_response (response, "Bad threshold number"); } } - catch (std::runtime_error &) - { - // If there is no "threshold" in request - } boost::property_tree::ptree response_l; boost::property_tree::ptree peers_l; { @@ -2054,7 +2038,7 @@ void rai::rpc_handler::representatives () uint64_t count (std::numeric_limits ::max ()); bool sorting (false); boost::optional count_text (request.get_optional ("count")); - if (count_text.is_initialized()) + if (count_text.is_initialized ()) { auto error (decode_unsigned (count_text.get (), count)); if (error) @@ -2062,10 +2046,10 @@ void rai::rpc_handler::representatives () error_response (response, "Invalid count limit"); } } - boost::optional sorting_text (request.get_optional ("sorting")); - if (count_text.is_initialized()) + boost::optional sorting_optional (request.get_optional ("sorting")); + if (sorting_optional.is_initialized ()) { - sorting = sorting_text.get (); + sorting = sorting_optional.get (); } boost::property_tree::ptree response_l; boost::property_tree::ptree representatives; @@ -2317,19 +2301,15 @@ void rai::rpc_handler::stop () void rai::rpc_handler::unchecked () { uint64_t count (std::numeric_limits ::max ()); - try + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized ()) { - std::string count_text (request.get ("count")); - auto error (decode_unsigned (count_text, count)); + auto error (decode_unsigned (count_text.get (), count)); if (error) { error_response (response, "Invalid count limit"); } } - catch (std::runtime_error &) - { - // If there is no "count" in request - } boost::property_tree::ptree response_l; boost::property_tree::ptree unchecked; rai::transaction transaction (node.store.environment, nullptr, false); @@ -2400,33 +2380,25 @@ void rai::rpc_handler::unchecked_get () void rai::rpc_handler::unchecked_keys () { uint64_t count (std::numeric_limits ::max ()); - try + rai::uint256_union key (0); + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized ()) { - std::string count_text (request.get ("count")); - auto error (decode_unsigned (count_text, count)); + auto error (decode_unsigned (count_text.get (), count)); if (error) { error_response (response, "Invalid count limit"); } } - catch (std::runtime_error &) + boost::optional hash_text (request.get_optional ("key")); + if (hash_text.is_initialized ()) { - // If there is no "count" in request - } - rai::uint256_union key (0); - try - { - std::string hash_text (request.get ("key")); - auto error_hash (key.decode_hex (hash_text)); + auto error_hash (key.decode_hex (hash_text.get ())); if (error_hash) { error_response (response, "Bad key hash number"); } } - catch (std::runtime_error &) - { - // If there is no "key" in request - } boost::property_tree::ptree response_l; boost::property_tree::ptree unchecked; rai::transaction transaction (node.store.environment, nullptr, false); @@ -2826,32 +2798,24 @@ void rai::rpc_handler::wallet_pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); - try + boost::optional count_text (request.get_optional ("count")); + if (count_text.is_initialized ()) { - std::string count_text (request.get ("count")); - auto error_count (decode_unsigned (count_text, count)); - if (error_count) + auto error (decode_unsigned (count_text.get (), count)); + if (error) { error_response (response, "Invalid count limit"); } } - catch (std::runtime_error &) + boost::optional threshold_text (request.get_optional ("threshold")); + if (threshold_text.is_initialized ()) { - // If there is no "count" in request - } - try - { - std::string threshold_text (request.get ("threshold")); - auto error_threshold (threshold.decode_dec (threshold_text)); + auto error_threshold (threshold.decode_dec (threshold_text.get ())); if (error_threshold) { error_response (response, "Bad threshold number"); } } - catch (std::runtime_error &) - { - // If there is no "threshold" in request - } boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); From 8426b619560d84a8ad1b0288ca55263fe7e5a56d Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 15 Aug 2017 15:53:20 +0300 Subject: [PATCH 03/18] RPC tests for block_count_type & ledger --- rai/core_test/rpc.cpp | 82 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index 20111efa..6818b13c 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -1789,18 +1789,18 @@ TEST (rpc, account_remove) TEST (rpc, representatives) { - rai::system system0 (24000, 1); - rai::rpc rpc (system0.service, *system0.nodes [0], rai::rpc_config (true)); + rai::system system0 (24000, 1); + rai::rpc rpc (system0.service, *system0.nodes [0], rai::rpc_config (true)); rpc.start (); - boost::property_tree::ptree request; + boost::property_tree::ptree request; request.put ("action", "representatives"); test_response response (request, rpc, system0.service); while (response.status == 0) { system0.poll (); } - ASSERT_EQ (200, response.status); - auto & representatives_node (response.json.get_child ("representatives")); + ASSERT_EQ (200, response.status); + auto & representatives_node (response.json.get_child ("representatives")); std::vector representatives; for (auto i (representatives_node.begin ()), n (representatives_node.end ()); i != n; ++i) { @@ -2712,3 +2712,75 @@ TEST (rpc, work_peers_all) peers_node = response3.json.get_child ("work_peers"); ASSERT_EQ (0, peers_node.size ()); } + +TEST (rpc, block_count_type) +{ + rai::system system (24000, 1); + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + auto send (system.wallet (0)->send_action (rai::test_genesis_key.pub, rai::test_genesis_key.pub, system.nodes [0]->config.receive_minimum.number ())); + ASSERT_NE (nullptr, send); + auto receive (system.wallet (0)->receive_action (static_cast (*send), rai::test_genesis_key.pub, system.nodes [0]->config.receive_minimum.number ())); + ASSERT_NE (nullptr, receive); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "block_count_type"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string send_count (response.json.get ("send")); + ASSERT_EQ ("1", send_count); + std::string receive_count (response.json.get ("receive")); + ASSERT_EQ ("1", receive_count); + std::string open_count (response.json.get ("open")); + ASSERT_EQ ("1", open_count); + std::string change_count (response.json.get ("change")); + ASSERT_EQ ("0", change_count); +} + +TEST (rpc, ledger) +{ + rai::system system (24000, 1); + rai::keypair key; + rai::genesis genesis; + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + system.wallet (0)->insert_adhoc (key.prv); + auto & node1 (*system.nodes [0]); + auto latest (system.nodes [0]->latest (rai::test_genesis_key.pub)); + rai::send_block send (latest, key.pub, 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, node1.generate_work (latest)); + system.nodes [0]->process (send); + rai::open_block open (send.hash (), rai::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.generate_work (key.pub)); + ASSERT_EQ (rai::process_result::progress, system.nodes [0]->process (open).code); + auto time (std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "ledger"); + request.put ("sorting", "1"); + request.put ("count", "1"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + for (auto & accounts : response.json.get_child("accounts")) + { + std::string account_text (accounts.first); + ASSERT_EQ (key.pub.to_account (), account_text); + std::string frontier (accounts.second.get ("frontier")); + ASSERT_EQ (open.hash ().to_string (), frontier); + std::string open_block (accounts.second.get ("open_block")); + ASSERT_EQ (open.hash ().to_string (), open_block); + std::string representative_block (accounts.second.get ("representative_block")); + ASSERT_EQ (open.hash ().to_string (), representative_block); + std::string balance_text (accounts.second.get ("balance")); + ASSERT_EQ ("340282366920938463463374607431768211355", balance_text); + std::string modified_timestamp (accounts.second.get ("modified_timestamp")); + ASSERT_EQ (std::to_string (time), modified_timestamp); + std::string block_count (accounts.second.get ("block_count")); + ASSERT_EQ ("1", block_count); + } +} From fcc599140498adab566e591c46a7a32c0b4f374b Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sat, 2 Sep 2017 01:03:49 +0300 Subject: [PATCH 04/18] RPC wallet_balances optional threshold --- rai/node/rpc.cpp | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 45a23bae..e212d54a 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -2570,6 +2570,16 @@ void rai::rpc_handler::wallet_balances () auto error (wallet.decode_hex (wallet_text)); if (!error) { + rai::uint128_union threshold (0); + boost::optional threshold_text (request.get_optional ("threshold")); + if (threshold_text.is_initialized ()) + { + auto error_threshold (threshold.decode_dec (threshold_text.get ())); + if (error_threshold) + { + error_response (response, "Bad threshold number"); + } + } auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { @@ -2579,12 +2589,26 @@ void rai::rpc_handler::wallet_balances () for (auto i (existing->second->store.begin (transaction)), n (existing->second->store.end ()); i != n; ++i) { rai::account account(i->first); - boost::property_tree::ptree entry; rai::uint128_t balance = node.ledger.account_balance (transaction, account); - rai::uint128_t pending = node.ledger.account_pending (transaction, account); - entry.put ("balance", balance.convert_to ()); - entry.put ("pending", pending.convert_to ()); - balances.push_back (std::make_pair (account.to_account (), entry)); + if (threshold.is_zero ()) + { + boost::property_tree::ptree entry; + rai::uint128_t pending = node.ledger.account_pending (transaction, account); + entry.put ("balance", balance.convert_to ()); + entry.put ("pending", pending.convert_to ()); + balances.push_back (std::make_pair (account.to_account (), entry)); + } + else + { + if (balance >= threshold.number ()) + { + boost::property_tree::ptree entry; + rai::uint128_t pending = node.ledger.account_pending (transaction, account); + entry.put ("balance", balance.convert_to ()); + entry.put ("pending", pending.convert_to ()); + balances.push_back (std::make_pair (account.to_account (), entry)); + } + } } response_l.add_child ("balances", balances); response (response_l); From 00c1db2efa379cbce1e1906ac894c1f3f5677d46 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sun, 3 Sep 2017 17:31:27 +0300 Subject: [PATCH 05/18] RPC accounts_create --- rai/node/rpc.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ rai/node/rpc.hpp | 1 + 2 files changed, 57 insertions(+) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index e212d54a..06cb2495 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -591,6 +591,58 @@ void rai::rpc_handler::accounts_balances () response (response_l); } +void rai::rpc_handler::accounts_create () +{ + if (rpc.config.enable_control) + { + std::string wallet_text (request.get ("wallet")); + rai::uint256_union wallet; + auto error (wallet.decode_hex (wallet_text)); + if (!error) + { + uint64_t count; + std::string count_text (request.get ("count")); + auto count_error (decode_unsigned (count_text, count)); + if (!count_error && count != 0) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + boost::property_tree::ptree response_l; + boost::property_tree::ptree accounts; + for (auto i(0); accounts.size ()< count; ++i) + { + rai::account new_key (existing->second->deterministic_insert ()); + if (!new_key.is_zero ()) + { + boost::property_tree::ptree entry; + entry.put ("", new_key.to_account ()); + accounts.push_back (std::make_pair ("", entry)); + } + } + response_l.add_child ("accounts", accounts); + response (response_l); + } + else + { + error_response (response, "Wallet not found"); + } + } + else + { + error_response (response, "Invalid count limit"); + } + } + else + { + error_response (response, "Bad wallet number"); + } + } + else + { + error_response (response, "RPC control is disabled"); + } +} void rai::rpc_handler::accounts_frontiers () { @@ -3517,6 +3569,10 @@ void rai::rpc_handler::process_request () { accounts_balances (); } + else if (action == "accounts_create") + { + accounts_create (); + } else if (action == "accounts_frontiers") { accounts_frontiers (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index 02d1f0e6..e4ec9a46 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -104,6 +104,7 @@ public: void account_representative_set (); void account_weight (); void accounts_balances (); + void accounts_create (); void accounts_frontiers (); void accounts_pending (); void available_supply (); From a7bbd24c7fa3989b4d20880cac3f939aa82605cd Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sun, 3 Sep 2017 23:15:24 +0300 Subject: [PATCH 06/18] RPC block_create (offline signing) --- rai/node/rpc.cpp | 253 +++++++++++++++++++++++++++++++++++++++++++++++ rai/node/rpc.hpp | 1 + 2 files changed, 254 insertions(+) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 06cb2495..ce94da11 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -892,6 +892,255 @@ void rai::rpc_handler::block_count_type () response (response_l); } + +void rai::rpc_handler::block_create () +{ + if (rpc.config.enable_control) + { + std::string type (request.get ("type")); + rai::uint256_union wallet (0); + boost::optional wallet_text (request.get_optional ("wallet")); + if (wallet_text.is_initialized ()) + { + auto error (wallet.decode_hex (wallet_text.get ())); + if (error) + { + error_response (response, "Bad wallet number"); + } + } + rai::uint256_union account (0); + boost::optional account_text (request.get_optional ("account")); + if (account_text.is_initialized ()) + { + auto error_account (account.decode_account (account_text.get ())); + if (error_account) + { + error_response (response, "Bad account number"); + } + } + rai::uint256_union representative (0); + boost::optional representative_text (request.get_optional ("representative")); + if (representative_text.is_initialized ()) + { + auto error_representative (representative.decode_account (representative_text.get ())); + if (error_representative) + { + error_response (response, "Bad representative account"); + } + } + rai::uint256_union destination (0); + boost::optional destination_text (request.get_optional ("destination")); + if (destination_text.is_initialized ()) + { + auto error_destination (destination.decode_account (destination_text.get ())); + if (error_destination) + { + error_response (response, "Bad destination account"); + } + } + rai::block_hash source (0); + boost::optional source_text (request.get_optional ("source")); + if (source_text.is_initialized ()) + { + auto error_source (source.decode_hex (source_text.get ())); + if (error_source) + { + error_response (response, "Invalid source hash"); + } + } + rai::uint128_union amount (0); + boost::optional amount_text (request.get_optional ("amount")); + if (amount_text.is_initialized ()) + { + auto error_amount (amount.decode_dec (amount_text.get ())); + if (error_amount) + { + error_response (response, "Bad amount number"); + } + } + uint64_t work (0); + boost::optional work_text (request.get_optional ("work")); + if (work_text.is_initialized ()) + { + auto work_error (rai::from_string_hex (work_text.get (), work)); + if (work_error) + { + error_response (response, "Bad work"); + } + } + rai::raw_key prv; + prv.data.clear (); + rai::uint256_union previous (0); + rai::uint128_union balance (0); + if (wallet != 0 && account != 0) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + rai::transaction transaction (node.store.environment, nullptr, false); + auto unlock_check (existing->second->store.valid_password (transaction)); + if (unlock_check) + { + auto account_check (existing->second->store.find (transaction, account)); + if (account_check != existing->second->store.end ()) + { + existing->second->store.fetch (transaction, account, prv); + previous = node.ledger.latest (transaction, account); + balance = node.ledger.account_balance (transaction, account); + } + else + { + error_response (response, "Account not found in wallet"); + } + } + else + { + error_response (response, "Wallet is locked"); + } + } + else + { + error_response (response, "Wallet not found"); + } + } + boost::optional key_text (request.get_optional ("key")); + if (key_text.is_initialized ()) + { + auto error_key (prv.data.decode_hex (key_text.get ())); + if (error_key) + { + error_response (response, "Bad private key"); + } + } + boost::optional previous_text (request.get_optional ("previous")); + if (previous_text.is_initialized ()) + { + auto error_previous (previous.decode_hex (previous_text.get ())); + if (error_previous) + { + error_response (response, "Invalid previous hash"); + } + } + boost::optional balance_text (request.get_optional ("balance")); + if (balance_text.is_initialized ()) + { + auto error_balance (balance.decode_dec (balance_text.get ())); + if (error_balance) + { + error_response (response, "Bad balance number"); + } + } + if (prv.data != 0) + { + rai::uint256_union pub; + ed25519_publickey (prv.data.bytes.data (), pub.bytes.data ()); + if (type == "open") + { + if (representative != 0 && source != 0) + { + if (work == 0) + { + work = node.generate_work (pub); + } + rai::open_block open (source, representative, pub, prv, pub, work); + boost::property_tree::ptree response_l; + response_l.put ("hash", open.hash ().to_string ()); + std::string contents; + open.serialize_json (contents); + response_l.put ("block", contents); + response (response_l); + } + else + { + error_response (response, "Representative account and source hash required"); + } + } + else if (type == "receive") + { + if (source != 0 && previous != 0) + { + if (work == 0) + { + work = node.generate_work (previous); + } + rai::receive_block receive (previous, source, prv, pub, work); + boost::property_tree::ptree response_l; + response_l.put ("hash", receive.hash ().to_string ()); + std::string contents; + receive.serialize_json (contents); + response_l.put ("block", contents); + response (response_l); + } + else + { + error_response (response, "Previous hash and source hash required"); + } + } + else if (type == "change") + { + if (representative != 0 && previous != 0) + { + if (work == 0) + { + work = node.generate_work (previous); + } + rai::change_block change (previous, representative, prv, pub, work); + boost::property_tree::ptree response_l; + response_l.put ("hash", change.hash ().to_string ()); + std::string contents; + change.serialize_json (contents); + response_l.put ("block", contents); + response (response_l); + } + else + { + error_response (response, "Representative account and previous hash required"); + } + } + else if (type == "send") + { + if (destination != 0 && previous != 0 && balance != 0 && amount != 0) + { + if (balance.number () >= amount.number ()) + { + if (work == 0) + { + work = node.generate_work (previous); + } + rai::send_block send (previous, destination, balance.number () - amount.number (), prv, pub, work); + boost::property_tree::ptree response_l; + response_l.put ("hash", send.hash ().to_string ()); + std::string contents; + send.serialize_json (contents); + response_l.put ("block", contents); + response (response_l); + } + else + { + error_response (response, "Insufficient balance"); + } + } + else + { + error_response (response, "Destination account, previous hash, current balance and amount required"); + } + } + else + { + error_response (response, "Invalid block type"); + } + } + else + { + error_response (response, "Private key or local wallet and account required"); + } + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::successors () { std::string block_text (request.get ("block")); @@ -3609,6 +3858,10 @@ void rai::rpc_handler::process_request () { block_count_type (); } + else if (action == "block_create") + { + block_create (); + } else if (action == "successors") { successors (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index e4ec9a46..36744772 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -114,6 +114,7 @@ public: void block_account (); void block_count (); void block_count_type (); + void block_create (); void bootstrap (); void bootstrap_any (); void chain (); From 7316c7e737559e4e86240e89a1b4c6e30cdc7765 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sun, 3 Sep 2017 23:43:34 +0300 Subject: [PATCH 07/18] RPC process should return new block hash, RPC stop should return success --- rai/node/rpc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index ce94da11..0ac3c3ba 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -2157,8 +2157,10 @@ void rai::rpc_handler::process () { if (!node.work.work_validate (*block)) { + auto hash (block->hash ()); node.process_receive_republish (std::move (block)); boost::property_tree::ptree response_l; + response_l.put ("hash", hash.to_string ()); response (response_l); } else @@ -2631,6 +2633,9 @@ void rai::rpc_handler::stop () { if (rpc.config.enable_control) { + boost::property_tree::ptree response_l; + response_l.put ("success", ""); + response (response_l); rpc.stop (); node.stop (); } From 5f3ae2cc2c090eb1a5ebc2d5fbbda74385e87893 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 5 Sep 2017 23:34:57 +0300 Subject: [PATCH 08/18] RPC tests for accounts_create, block_create --- rai/core_test/rpc.cpp | 185 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 7 deletions(-) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index 2d3f0bb0..e1e518df 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -900,15 +900,15 @@ TEST (rpc, history_count) TEST (rpc, process_block) { - rai::system system (24000, 1); + rai::system system (24000, 1); rai::keypair key; auto latest (system.nodes [0]->latest (rai::test_genesis_key.pub)); auto & node1 (*system.nodes [0]); rai::send_block send (latest, key.pub, 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, node1.generate_work (latest)); - rai::rpc rpc (system.service, node1, rai::rpc_config (true)); + rai::rpc rpc (system.service, node1, rai::rpc_config (true)); rpc.start (); - boost::property_tree::ptree request; - request.put ("action", "process"); + boost::property_tree::ptree request; + request.put ("action", "process"); std::string json; send.serialize_json (json); request.put ("block", json); @@ -917,8 +917,10 @@ TEST (rpc, process_block) { system.poll (); } - ASSERT_EQ (200, response.status); + ASSERT_EQ (200, response.status); ASSERT_EQ (send.hash (), system.nodes [0]->latest (rai::test_genesis_key.pub)); + std::string send_hash (response.json.get ("hash")); + ASSERT_EQ (send.hash ().to_string (), send_hash); } TEST (rpc, process_block_no_work) @@ -1959,7 +1961,7 @@ TEST (rpc, bootstrap_any) ASSERT_TRUE (success.empty()); } -TEST (rpc, DISABLED_republish) +TEST (rpc, republish) { rai::system system (24000, 2); rai::keypair key; @@ -2256,6 +2258,25 @@ TEST (rpc, wallet_balances) std::string pending_text (balances.second.get ("pending")); ASSERT_EQ ("0", pending_text); } + rai::keypair key; + system0.wallet (0)->insert_adhoc (key.prv); + auto send (system0.wallet (0)->send_action (rai::test_genesis_key.pub, key.pub, 1)); + request.put ("threshold", "2"); + test_response response1 (request, rpc, system0.service); + while (response1.status == 0) + { + system0.poll (); + } + ASSERT_EQ (200, response1.status); + for (auto & balances : response1.json.get_child("balances")) + { + std::string account_text (balances.first); + ASSERT_EQ (rai::test_genesis_key.pub.to_account (), account_text); + std::string balance_text (balances.second.get ("balance")); + ASSERT_EQ ("340282366920938463463374607431768211454", balance_text); + std::string pending_text (balances.second.get ("pending")); + ASSERT_EQ ("0", pending_text); + } } TEST (rpc, pending_exists) @@ -2487,7 +2508,7 @@ TEST (rpc, search_pending_all) } } -TEST (rpc, DISABLED_wallet_republish) +TEST (rpc, wallet_republish) { rai::system system (24000, 1); rai::genesis genesis; @@ -2785,3 +2806,153 @@ TEST (rpc, ledger) ASSERT_EQ ("1", block_count); } } + +TEST (rpc, accounts_create) +{ + rai::system system (24000, 1); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "accounts_create"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("count", "8"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + auto & accounts (response.json.get_child ("accounts")); + for (auto i (accounts.begin ()), n (accounts.end ()); i != n; ++i) + { + std::string account_text (i->second.get ("")); + rai::uint256_union account; + ASSERT_FALSE (account.decode_account (account_text)); + ASSERT_TRUE (system.wallet (0)->exists (account)); + } + ASSERT_EQ (8, accounts.size ()); +} + +TEST (rpc, block_create) +{ + rai::system system (24000, 1); + rai::keypair key; + rai::genesis genesis; + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + system.wallet (0)->insert_adhoc (key.prv); + auto & node1 (*system.nodes [0]); + auto latest (system.nodes [0]->latest (rai::test_genesis_key.pub)); + auto send_work = node1.generate_work (latest); + rai::send_block send (latest, key.pub, 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, send_work); + auto open_work = node1.generate_work (key.pub); + rai::open_block open (send.hash (), rai::test_genesis_key.pub, key.pub, key.prv, key.pub, open_work); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "block_create"); + request.put ("type", "send"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("account", rai::test_genesis_key.pub.to_account ()); + request.put ("previous", latest.to_string ()); + request.put ("amount", "340282366920938463463374607431768211355"); + request.put ("destination", key.pub.to_account ()); + request.put ("work", rai::to_string_hex (send_work)); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string send_hash (response.json.get ("hash")); + ASSERT_EQ (send.hash ().to_string (), send_hash); + auto send_text (response.json.get ("block")); + boost::property_tree::ptree block_l; + std::stringstream block_stream (send_text); + boost::property_tree::read_json (block_stream, block_l); + auto send_block (rai::deserialize_block_json (block_l)); + ASSERT_EQ (send.hash (), send_block->hash ()); + system.nodes [0]->process (send); + boost::property_tree::ptree request1; + request1.put ("action", "block_create"); + request1.put ("type", "open"); + std::string key_text; + key.prv.data.encode_hex (key_text); + request1.put ("key", key_text); + request1.put ("representative", rai::test_genesis_key.pub.to_account ()); + request1.put ("source", send.hash ().to_string ()); + request1.put ("work", rai::to_string_hex (open_work)); + test_response response1 (request1, rpc, system.service); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + std::string open_hash (response1.json.get ("hash")); + ASSERT_EQ (open.hash ().to_string (), open_hash); + auto open_text (response1.json.get ("block")); + std::stringstream block_stream1 (open_text); + boost::property_tree::read_json (block_stream1, block_l); + auto open_block (rai::deserialize_block_json (block_l)); + ASSERT_EQ (open.hash (), open_block->hash ()); + ASSERT_EQ (rai::process_result::progress, system.nodes [0]->process (open).code); + request1.put ("representative", key.pub.to_account ()); + test_response response2 (request1, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response2.status); + std::string open2_hash (response2.json.get ("hash")); + ASSERT_NE (open.hash ().to_string (), open2_hash); // different blocks with wrong representative + auto change_work = node1.generate_work (open.hash ()); + rai::change_block change (open.hash (), key.pub, key.prv, key.pub, change_work); + request1.put ("type", "change"); + request1.put ("work", rai::to_string_hex (change_work)); + test_response response3 (request1, rpc, system.service); + while (response3.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response3.status); + ASSERT_FALSE (response3.json.get ("error").empty ()); // error with missing previous block + request1.put ("previous", open.hash ().to_string ()); + test_response response4 (request1, rpc, system.service); + while (response4.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response4.status); + std::string change_hash (response4.json.get ("hash")); + ASSERT_EQ (change.hash ().to_string (), change_hash); + auto change_text (response4.json.get ("block")); + std::stringstream block_stream4 (change_text); + boost::property_tree::read_json (block_stream4, block_l); + auto change_block (rai::deserialize_block_json (block_l)); + ASSERT_EQ (change.hash (), change_block->hash ()); + ASSERT_EQ (rai::process_result::progress, node1.process (change).code); + rai::send_block send2 (send.hash (), key.pub, 0, rai::test_genesis_key.prv, rai::test_genesis_key.pub, node1.generate_work (send.hash ())); + ASSERT_EQ (rai::process_result::progress, system.nodes [0]->process (send2).code); + boost::property_tree::ptree request2; + request2.put ("action", "block_create"); + request2.put ("type", "receive"); + request2.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request2.put ("account", key.pub.to_account ()); + request2.put ("source", send2.hash ().to_string ()); + request2.put ("previous", change.hash ().to_string ()); + request2.put ("work", rai::to_string_hex (node1.generate_work (change.hash ()))); + test_response response5 (request2, rpc, system.service); + while (response5.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response5.status); + std::string receive_hash (response4.json.get ("hash")); + auto receive_text (response5.json.get ("block")); + std::stringstream block_stream5 (change_text); + boost::property_tree::read_json (block_stream5, block_l); + auto receive_block (rai::deserialize_block_json (block_l)); + ASSERT_EQ (receive_hash, receive_block->hash ().to_string ()); + system.nodes [0]->process_receive_republish (std::move (receive_block)); + latest = system.nodes [0]->latest (key.pub); + ASSERT_EQ (receive_hash, latest.to_string ()); +} From f9515bb11c1404e906e021e13ff33a1e038a29dd Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sat, 9 Sep 2017 23:03:54 +0300 Subject: [PATCH 09/18] RPC wallet_lock wallet_locked = password_valid wallet_unlock = password_enter --- rai/core_test/rpc.cpp | 42 +++++++++++++++++++++++++ rai/node/rpc.cpp | 71 ++++++++++++++++++++++++++++++++++++++++--- rai/node/rpc.hpp | 3 +- 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index e1e518df..6a2fe534 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -2956,3 +2956,45 @@ TEST (rpc, block_create) latest = system.nodes [0]->latest (key.pub); ASSERT_EQ (receive_hash, latest.to_string ()); } + +TEST (rpc, wallet_lock) +{ + rai::system system (24000, 1); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + std::string wallet; + system.nodes [0]->wallets.items.begin ()->first.encode_hex (wallet); + ASSERT_TRUE (system.wallet (0)->valid_password ()); + request.put ("wallet", wallet); + request.put ("action", "wallet_lock"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string account_text1 (response.json.get ("locked")); + ASSERT_EQ (account_text1, "1"); + ASSERT_FALSE (system.wallet (0)->valid_password ()); +} + +TEST (rpc, wallet_locked) +{ + rai::system system (24000, 1); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + std::string wallet; + system.nodes [0]->wallets.items.begin ()->first.encode_hex (wallet); + request.put ("wallet", wallet); + request.put ("action", "wallet_locked"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string account_text1 (response.json.get ("locked")); + ASSERT_EQ (account_text1, "0"); +} diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 0ac3c3ba..b93993aa 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -1782,7 +1782,7 @@ void rai::rpc_handler::password_change () } else { - error_response (response, "Bad account number"); + error_response (response, "Bad wallet number"); } } else @@ -1814,11 +1814,11 @@ void rai::rpc_handler::password_enter () } else { - error_response (response, "Bad account number"); + error_response (response, "Bad wallet number"); } } -void rai::rpc_handler::password_valid () +void rai::rpc_handler::password_valid (bool wallet_locked = false) { std::string wallet_text (request.get ("wallet")); rai::uint256_union wallet; @@ -1830,7 +1830,15 @@ void rai::rpc_handler::password_valid () { rai::transaction transaction (node.store.environment, nullptr, false); boost::property_tree::ptree response_l; - response_l.put ("valid", existing->second->store.valid_password (transaction) ? "1" : "0"); + auto valid (existing->second->store.valid_password (transaction)); + if (!wallet_locked) + { + response_l.put ("valid", valid ? "1" : "0"); + } + else + { + response_l.put ("locked", valid ? "0" : "1"); + } response (response_l); } else @@ -1840,7 +1848,7 @@ void rai::rpc_handler::password_valid () } else { - error_response (response, "Bad account number"); + error_response (response, "Bad wallet number"); } } @@ -3157,6 +3165,41 @@ void rai::rpc_handler::wallet_key_valid () } } +void rai::rpc_handler::wallet_lock () +{ + if (rpc.config.enable_control) + { + std::string wallet_text (request.get ("wallet")); + rai::uint256_union wallet; + auto error (wallet.decode_hex (wallet_text)); + if (!error) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + boost::property_tree::ptree response_l; + rai::raw_key empty; + empty.data.clear (); + existing->second->store.password.value_set (empty); + response_l.put ("locked", "1"); + response (response_l); + } + else + { + error_response (response, "Wallet not found"); + } + } + else + { + error_response (response, "Bad wallet number"); + } + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::wallet_pending () { std::string wallet_text (request.get ("wallet")); @@ -3763,6 +3806,12 @@ void rai::rpc_handler::process_request () request.erase ("password"); reprocess_body (body, request); } + else if (action == "wallet_unlock") + { + password_enter (); + request.erase ("password"); + reprocess_body (body, request); + } if (node.config.logging.log_rpc ()) { BOOST_LOG (node.log) << body; @@ -4091,6 +4140,14 @@ void rai::rpc_handler::process_request () { wallet_key_valid (); } + else if (action == "wallet_lock") + { + wallet_lock (); + } + else if (action == "wallet_locked") + { + password_valid (true); + } else if (action == "wallet_pending") { wallet_pending (); @@ -4107,6 +4164,10 @@ void rai::rpc_handler::process_request () { wallet_republish (); } + else if (action == "wallet_unlock") + { + // Processed before logging + } else if (action == "wallet_work_get") { wallet_work_get (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index 36744772..1b254ccc 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -134,7 +134,7 @@ public: void mrai_from_raw (); void password_change (); void password_enter (); - void password_valid (); + void password_valid (bool wallet_locked); void payment_begin (); void payment_init (); void payment_end (); @@ -171,6 +171,7 @@ public: void wallet_export (); void wallet_frontiers (); void wallet_key_valid (); + void wallet_lock (); void wallet_pending (); void wallet_representative (); void wallet_representative_set (); From 0ad81344abb288d703bfd80ef8e2b8ae86cd3bce Mon Sep 17 00:00:00 2001 From: SergiySW Date: Thu, 28 Sep 2017 15:09:41 +0300 Subject: [PATCH 10/18] Update with changes from clemahieu/master --- rai/node/rpc.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index b93993aa..ef5f6edd 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -2165,8 +2165,7 @@ void rai::rpc_handler::process () { if (!node.work.work_validate (*block)) { - auto hash (block->hash ()); - node.process_receive_republish (std::move (block)); + node.process_active (std::move (block)); boost::property_tree::ptree response_l; response_l.put ("hash", hash.to_string ()); response (response_l); From e223c91a7770200a06e6b3a36090e3af76371b58 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Thu, 28 Sep 2017 22:59:56 +0300 Subject: [PATCH 11/18] RPC account_representative_set, receive, send to allow optional "work" value --- rai/node/rpc.cpp | 128 +++++++++++++++++++++++++++++++++++++++----- rai/node/wallet.cpp | 92 ++++++++++++++++++++++++------- rai/node/wallet.hpp | 34 +++++++----- 3 files changed, 209 insertions(+), 45 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index ef5f6edd..dca941ca 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -520,6 +520,36 @@ void rai::rpc_handler::account_representative_set () auto error (representative.decode_account (representative_text)); if (!error) { + uint64_t work (0); + boost::optional work_text (request.get_optional ("work")); + if (work_text.is_initialized ()) + { + auto work_error (rai::from_string_hex (work_text.get (), work)); + if (work_error) + { + error_response (response, "Bad work"); + } + } + if (work) + { + rai::transaction transaction (node.store.environment, nullptr, true); + rai::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + if (!node.work.work_validate (info.head, work)) + { + existing->second->store.work_put (transaction, account, work); + } + else + { + error_response (response, "Invalid work"); + } + } + else + { + error_response (response, "Account not found"); + } + } auto response_a (response); wallet->change_async (account, representative, [response_a] (std::shared_ptr block) { @@ -531,7 +561,7 @@ void rai::rpc_handler::account_representative_set () boost::property_tree::ptree response_l; response_l.put ("block", hash.to_string ()); response_a (response_l); - }); + }, work == 0); } } else @@ -2165,6 +2195,7 @@ void rai::rpc_handler::process () { if (!node.work.work_validate (*block)) { + auto hash (block->hash ()); node.process_active (std::move (block)); boost::property_tree::ptree response_l; response_l.put ("hash", hash.to_string ()); @@ -2253,6 +2284,38 @@ void rai::rpc_handler::receive () { if (node.store.pending_exists (transaction, rai::pending_key (account, hash))) { + uint64_t work (0); + boost::optional work_text (request.get_optional ("work")); + if (work_text.is_initialized ()) + { + auto work_error (rai::from_string_hex (work_text.get (), work)); + if (work_error) + { + error_response (response, "Bad work"); + } + } + if (work) + { + rai::account_info info; + rai::uint256_union head; + if (!node.store.account_get (transaction, account, info)) + { + head = info.head; + } + else + { + head = account; + } + if (!node.work.work_validate (head, work)) + { + rai::transaction transaction_a (node.store.environment, nullptr, true); + existing->second->store.work_put (transaction_a, account, work); + } + else + { + error_response (response, "Invalid work"); + } + } auto response_a (response); existing->second->receive_async (std::move (block), account, rai::genesis_amount, [response_a] (std::shared_ptr block_a) { @@ -2264,7 +2327,7 @@ void rai::rpc_handler::receive () boost::property_tree::ptree response_l; response_l.put ("block", hash_a.to_string ()); response_a (response_l); - }); + }, work == 0); } else { @@ -2591,19 +2654,60 @@ void rai::rpc_handler::send () auto error (amount.decode_dec (amount_text)); if (!error) { - auto rpc_l (shared_from_this ()); - auto response_a (response); - existing->second->send_async (source, destination, amount.number (), [response_a] (std::shared_ptr block_a) + uint64_t work (0); + boost::optional work_text (request.get_optional ("work")); + if (work_text.is_initialized ()) { - rai::uint256_union hash (0); - if (block_a != nullptr) + auto work_error (rai::from_string_hex (work_text.get (), work)); + if (work_error) { - hash = block_a->hash (); + error_response (response, "Bad work"); } - boost::property_tree::ptree response_l; - response_l.put ("block", hash.to_string ()); - response_a (response_l); - }); + } + rai::uint128_t balance (0); + { + rai::transaction transaction (node.store.environment, nullptr, work != 0); // false if no "work" in request, true if work > 0 + rai::account_info info; + if (!node.store.account_get (transaction, source, info)) + { + balance = (info.balance).number(); + } + else + { + error_response (response, "Account not found"); + } + if (work) + { + if (!node.work.work_validate (info.head, work)) + { + existing->second->store.work_put (transaction, source, work); + } + else + { + error_response (response, "Invalid work"); + } + } + } + if (balance >= amount.number ()) + { + auto rpc_l (shared_from_this ()); + auto response_a (response); + existing->second->send_async (source, destination, amount.number (), [response_a] (std::shared_ptr block_a) + { + rai::uint256_union hash (0); + if (block_a != nullptr) + { + hash = block_a->hash (); + } + boost::property_tree::ptree response_l; + response_l.put ("block", hash.to_string ()); + response_a (response_l); + }, work == 0); + } + else + { + error_response (response, "Insufficient balance"); + } } else { diff --git a/rai/node/wallet.cpp b/rai/node/wallet.cpp index 88665ec6..352bc392 100644 --- a/rai/node/wallet.cpp +++ b/rai/node/wallet.cpp @@ -994,6 +994,11 @@ bool check_ownership (rai::wallets & wallets_a, rai::account const & account_a) } std::shared_ptr rai::wallet::receive_action (rai::send_block const & send_a, rai::account const & representative_a, rai::uint128_union const & amount_a) +{ + return receive_action (send_a, representative_a, amount_a, true); +} + +std::shared_ptr rai::wallet::receive_action (rai::send_block const & send_a, rai::account const & representative_a, rai::uint128_union const & amount_a, bool const & generate_work_a) { auto hash (send_a.hash ()); std::shared_ptr block; @@ -1037,17 +1042,30 @@ std::shared_ptr rai::wallet::receive_action (rai::send_block const assert (block != nullptr); node.process_active (block); auto hash (block->hash ()); - auto this_l (shared_from_this ()); auto source (send_a.hashables.destination); - node.wallets.queue_wallet_action (source, rai::wallets::generate_priority, [this_l, source, hash] + if (generate_work_a) { - this_l->work_generate (source, hash); - }); + auto this_l (shared_from_this ()); + node.wallets.queue_wallet_action (source, rai::wallets::generate_priority, [this_l, source, hash] + { + this_l->work_generate (source, hash); + }); + } + else + { + rai::transaction transaction (store.environment, nullptr, true); + work_update (transaction, source, hash, 0); + } } return block; } std::shared_ptr rai::wallet::change_action (rai::account const & source_a, rai::account const & representative_a) +{ + return change_action (source_a, representative_a, true); +} + +std::shared_ptr rai::wallet::change_action (rai::account const & source_a, rai::account const & representative_a, bool const & generate_work_a) { std::shared_ptr block; { @@ -1075,16 +1093,29 @@ std::shared_ptr rai::wallet::change_action (rai::account const & so assert (block != nullptr); node.process_active (block); auto hash (block->hash ()); - auto this_l (shared_from_this ()); - node.wallets.queue_wallet_action (source_a, rai::wallets::generate_priority, [this_l, source_a, hash] + if (generate_work_a) { - this_l->work_generate (source_a, hash); - }); + auto this_l (shared_from_this ()); + node.wallets.queue_wallet_action (source_a, rai::wallets::generate_priority, [this_l, source_a, hash] + { + this_l->work_generate (source_a, hash); + }); + } + else + { + rai::transaction transaction (store.environment, nullptr, true); + work_update (transaction, source_a, hash, 0); + } } return block; } std::shared_ptr rai::wallet::send_action (rai::account const & source_a, rai::account const & account_a, rai::uint128_t const & amount_a) +{ + return send_action (source_a, account_a, amount_a, true); +} + +std::shared_ptr rai::wallet::send_action (rai::account const & source_a, rai::account const & account_a, rai::uint128_t const & amount_a, bool const & generate_work_a) { std::shared_ptr block; { @@ -1116,11 +1147,19 @@ std::shared_ptr rai::wallet::send_action (rai::account const & sour assert (block != nullptr); node.process_active (block); auto hash (block->hash ()); - auto this_l (shared_from_this ()); - node.wallets.queue_wallet_action (source_a, rai::wallets::generate_priority, [this_l, source_a, hash] + if (generate_work_a) { - this_l->work_generate (source_a, hash); - }); + auto this_l (shared_from_this ()); + node.wallets.queue_wallet_action (source_a, rai::wallets::generate_priority, [this_l, source_a, hash] + { + this_l->work_generate (source_a, hash); + }); + } + else + { + rai::transaction transaction (store.environment, nullptr, true); + work_update (transaction, source_a, hash, 0); + } } return block; } @@ -1137,10 +1176,15 @@ bool rai::wallet::change_sync (rai::account const & source_a, rai::account const void rai::wallet::change_async (rai::account const & source_a, rai::account const & representative_a, std::function )> const & action_a) { - node.wallets.queue_wallet_action (source_a, rai::wallets::high_priority, [this, source_a, representative_a, action_a] () + change_async (source_a, representative_a, action_a, true); +} + +void rai::wallet::change_async (rai::account const & source_a, rai::account const & representative_a, std::function )> const & action_a, bool const & generate_work_a) +{ + node.wallets.queue_wallet_action (source_a, rai::wallets::high_priority, [this, source_a, representative_a, action_a, generate_work_a] () { assert (!check_ownership (node.wallets, source_a)); - auto block (change_action (source_a, representative_a)); + auto block (change_action (source_a, representative_a, generate_work_a)); action_a (block); }); } @@ -1156,12 +1200,17 @@ bool rai::wallet::receive_sync (std::shared_ptr block_a, rai::accou } void rai::wallet::receive_async (std::shared_ptr block_a, rai::account const & representative_a, rai::uint128_t const & amount_a, std::function )> const & action_a) +{ + receive_async (block_a, representative_a, amount_a, action_a, true); +} + +void rai::wallet::receive_async (std::shared_ptr block_a, rai::account const & representative_a, rai::uint128_t const & amount_a, std::function )> const & action_a, bool const & generate_work_a) { assert (dynamic_cast (block_a.get ()) != nullptr); - node.wallets.queue_wallet_action (static_cast (block_a.get ())->hashables.destination, amount_a, [this, block_a, representative_a, amount_a, action_a] () + node.wallets.queue_wallet_action (static_cast (block_a.get ())->hashables.destination, amount_a, [this, block_a, representative_a, amount_a, action_a, generate_work_a] () { assert (!check_ownership (node.wallets, static_cast (block_a.get ())->hashables.destination)); - auto block (receive_action (*static_cast (block_a.get ()), representative_a, amount_a)); + auto block (receive_action (*static_cast (block_a.get ()), representative_a, amount_a, generate_work_a)); action_a (block); }); } @@ -1178,12 +1227,17 @@ rai::block_hash rai::wallet::send_sync (rai::account const & source_a, rai::acco void rai::wallet::send_async (rai::account const & source_a, rai::account const & account_a, rai::uint128_t const & amount_a, std::function )> const & action_a) { - node.background ([this, source_a, account_a, amount_a, action_a] () + send_async (source_a, account_a, amount_a, action_a, true); +} + +void rai::wallet::send_async (rai::account const & source_a, rai::account const & account_a, rai::uint128_t const & amount_a, std::function )> const & action_a, bool const & generate_work_a) +{ + node.background ([this, source_a, account_a, amount_a, action_a, generate_work_a] () { - this->node.wallets.queue_wallet_action (source_a, rai::wallets::high_priority, [this, source_a, account_a, amount_a, action_a] () + this->node.wallets.queue_wallet_action (source_a, rai::wallets::high_priority, [this, source_a, account_a, amount_a, action_a, generate_work_a] () { assert (!check_ownership (node.wallets, source_a)); - auto block (send_action (source_a, account_a, amount_a)); + auto block (send_action (source_a, account_a, amount_a, generate_work_a)); action_a (block); }); }); diff --git a/rai/node/wallet.hpp b/rai/node/wallet.hpp index 4a9bf54d..94ce2174 100644 --- a/rai/node/wallet.hpp +++ b/rai/node/wallet.hpp @@ -149,36 +149,42 @@ class wallet : public std::enable_shared_from_this { public: std::shared_ptr change_action (rai::account const &, rai::account const &); - std::shared_ptr receive_action (rai::send_block const &, rai::account const &, rai::uint128_union const &); + std::shared_ptr change_action (rai::account const &, rai::account const &, bool const &); + std::shared_ptr receive_action (rai::send_block const &, rai::account const &, rai::uint128_union const &); + std::shared_ptr receive_action (rai::send_block const &, rai::account const &, rai::uint128_union const &, bool const &); std::shared_ptr send_action (rai::account const &, rai::account const &, rai::uint128_t const &); - wallet (bool &, rai::transaction &, rai::node &, std::string const &); - wallet (bool &, rai::transaction &, rai::node &, std::string const &, std::string const &); + std::shared_ptr send_action (rai::account const &, rai::account const &, rai::uint128_t const &, bool const &); + wallet (bool &, rai::transaction &, rai::node &, std::string const &); + wallet (bool &, rai::transaction &, rai::node &, std::string const &, std::string const &); void enter_initial_password (); - bool valid_password (); - bool enter_password (std::string const &); + bool valid_password (); + bool enter_password (std::string const &); rai::public_key insert_adhoc (rai::raw_key const &); rai::public_key insert_adhoc (MDB_txn *, rai::raw_key const &); rai::public_key deterministic_insert (MDB_txn *); rai::public_key deterministic_insert (); - bool exists (rai::public_key const &); + bool exists (rai::public_key const &); bool import (std::string const &, std::string const &); void serialize (std::string &); bool change_sync (rai::account const &, rai::account const &); void change_async (rai::account const &, rai::account const &, std::function )> const &); - bool receive_sync (std::shared_ptr , rai::account const &, rai::uint128_t const &); + void change_async (rai::account const &, rai::account const &, std::function )> const &, bool const &); + bool receive_sync (std::shared_ptr , rai::account const &, rai::uint128_t const &); void receive_async (std::shared_ptr , rai::account const &, rai::uint128_t const &, std::function )> const &); + void receive_async (std::shared_ptr , rai::account const &, rai::uint128_t const &, std::function )> const &, bool const &); rai::block_hash send_sync (rai::account const &, rai::account const &, rai::uint128_t const &); void send_async (rai::account const &, rai::account const &, rai::uint128_t const &, std::function )> const &); - void work_generate (rai::account const &, rai::block_hash const &); - void work_update (MDB_txn *, rai::account const &, rai::block_hash const &, uint64_t); - uint64_t work_fetch (MDB_txn *, rai::account const &, rai::block_hash const &); + void send_async (rai::account const &, rai::account const &, rai::uint128_t const &, std::function )> const &, bool const &); + void work_generate (rai::account const &, rai::block_hash const &); + void work_update (MDB_txn *, rai::account const &, rai::block_hash const &, uint64_t); + uint64_t work_fetch (MDB_txn *, rai::account const &, rai::block_hash const &); void work_ensure (MDB_txn *, rai::account const &); bool search_pending (); void init_free_accounts (MDB_txn *); std::unordered_set free_accounts; std::function lock_observer; - rai::wallet_store store; - rai::node & node; + rai::wallet_store store; + rai::node & node; }; // The wallets set is all the wallets a node controls. A node may contain multiple wallets independently encrypted and operated. class wallets @@ -187,8 +193,8 @@ public: wallets (bool &, rai::node &); std::shared_ptr open (rai::uint256_union const &); std::shared_ptr create (rai::uint256_union const &); - bool search_pending (rai::uint256_union const &); - void search_pending_all (); + bool search_pending (rai::uint256_union const &); + void search_pending_all (); void destroy (rai::uint256_union const &); void do_wallet_actions (rai::account const &); void queue_wallet_action (rai::account const &, rai::uint128_t const &, std::function const &); From 18f838c1780c93007d72311a5eb66b47218502a9 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Thu, 28 Sep 2017 23:54:29 +0300 Subject: [PATCH 12/18] RPC account_create, accounts_create, wallet_add with optional "work" to generate accounts without work --- rai/node/rpc.cpp | 24 +++++++++++++++++++++--- rai/node/wallet.cpp | 38 ++++++++++++++++++++++++++++++++++---- rai/node/wallet.hpp | 4 ++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index dca941ca..44c8e0e7 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -218,7 +218,13 @@ void rai::rpc_handler::account_create () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { - rai::account new_key (existing->second->deterministic_insert ()); + bool generate_work (true); + boost::optional work (request.get_optional ("work")); + if (work.is_initialized ()) + { + generate_work = work.get (); + } + rai::account new_key (existing->second->deterministic_insert (generate_work)); if (!new_key.is_zero ()) { boost::property_tree::ptree response_l; @@ -638,11 +644,17 @@ void rai::rpc_handler::accounts_create () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { + bool generate_work (true); + boost::optional work (request.get_optional ("work")); + if (work.is_initialized ()) + { + generate_work = work.get (); + } boost::property_tree::ptree response_l; boost::property_tree::ptree accounts; for (auto i(0); accounts.size ()< count; ++i) { - rai::account new_key (existing->second->deterministic_insert ()); + rai::account new_key (existing->second->deterministic_insert (generate_work)); if (!new_key.is_zero ()) { boost::property_tree::ptree entry; @@ -2912,7 +2924,13 @@ void rai::rpc_handler::wallet_add () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { - auto pub (existing->second->insert_adhoc (key)); + bool generate_work (true); + boost::optional work (request.get_optional ("work")); + if (work.is_initialized ()) + { + generate_work = work.get (); + } + auto pub (existing->second->insert_adhoc (key, generate_work)); if (!pub.is_zero ()) { boost::property_tree::ptree response_l; diff --git a/rai/node/wallet.cpp b/rai/node/wallet.cpp index 352bc392..66da6ebd 100644 --- a/rai/node/wallet.cpp +++ b/rai/node/wallet.cpp @@ -908,38 +908,68 @@ bool rai::wallet::enter_password (std::string const & password_a) } rai::public_key rai::wallet::deterministic_insert (MDB_txn * transaction_a) +{ + auto result (deterministic_insert(transaction_a, true)); + return result; +} + +rai::public_key rai::wallet::deterministic_insert (MDB_txn * transaction_a, bool const & generate_work_a) { rai::public_key key (0); if (store.valid_password (transaction_a)) { key = store.deterministic_insert (transaction_a); - work_ensure (transaction_a, key); + if (generate_work_a) + { + work_ensure (transaction_a, key); + } } return key; } rai::public_key rai::wallet::deterministic_insert () +{ + auto result (deterministic_insert (true)); + return result; +} + +rai::public_key rai::wallet::deterministic_insert (bool const & generate_work_a) { rai::transaction transaction (store.environment, nullptr, true); - auto result (deterministic_insert (transaction)); + auto result (deterministic_insert (transaction, generate_work_a)); return result; } rai::public_key rai::wallet::insert_adhoc (MDB_txn * transaction_a, rai::raw_key const & key_a) +{ + auto result (insert_adhoc (transaction_a, key_a, true)); + return result; +} + +rai::public_key rai::wallet::insert_adhoc (MDB_txn * transaction_a, rai::raw_key const & key_a, bool const & generate_work_a) { rai::public_key key (0); if (store.valid_password (transaction_a)) { key = store.insert_adhoc (transaction_a, key_a); - work_ensure (transaction_a, key); + if (generate_work_a) + { + work_ensure (transaction_a, key); + } } return key; } rai::public_key rai::wallet::insert_adhoc (rai::raw_key const & account_a) +{ + auto result (insert_adhoc (account_a, true)); + return result; +} + +rai::public_key rai::wallet::insert_adhoc (rai::raw_key const & account_a, bool const & generate_work_a) { rai::transaction transaction (store.environment, nullptr, true); - auto result (insert_adhoc (transaction, account_a)); + auto result (insert_adhoc (transaction, account_a, generate_work_a)); return result; } diff --git a/rai/node/wallet.hpp b/rai/node/wallet.hpp index 94ce2174..451fd793 100644 --- a/rai/node/wallet.hpp +++ b/rai/node/wallet.hpp @@ -160,9 +160,13 @@ public: bool valid_password (); bool enter_password (std::string const &); rai::public_key insert_adhoc (rai::raw_key const &); + rai::public_key insert_adhoc (rai::raw_key const &, bool const &); rai::public_key insert_adhoc (MDB_txn *, rai::raw_key const &); + rai::public_key insert_adhoc (MDB_txn *, rai::raw_key const &, bool const &); rai::public_key deterministic_insert (MDB_txn *); + rai::public_key deterministic_insert (MDB_txn *, bool const &); rai::public_key deterministic_insert (); + rai::public_key deterministic_insert (bool const &); bool exists (rai::public_key const &); bool import (std::string const &, std::string const &); void serialize (std::string &); From dbb1f9491ab85161ce405aa36ed443ce2ea35ecf Mon Sep 17 00:00:00 2001 From: SergiySW Date: Wed, 4 Oct 2017 20:22:29 +0300 Subject: [PATCH 13/18] RPC account_info, ledger with optional "representative", "weight", "pending" --- rai/node/rpc.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 44c8e0e7..c4b679dc 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -276,6 +276,24 @@ void rai::rpc_handler::account_info () auto error (account.decode_account (account_text)); if (!error) { + bool representative (false); + boost::optional representative_optional (request.get_optional ("representative")); + if (representative_optional.is_initialized ()) + { + representative = representative_optional.get (); + } + bool weight (false); + boost::optional weight_optional (request.get_optional ("weight")); + if (weight_optional.is_initialized ()) + { + weight = weight_optional.get (); + } + bool pending (false); + boost::optional pending_optional (request.get_optional ("pending")); + if (pending_optional.is_initialized ()) + { + pending = pending_optional.get (); + } rai::transaction transaction (node.store.environment, nullptr, false); rai::account_info info; if (!node.store.account_get (transaction, account, info)) @@ -289,6 +307,22 @@ void rai::rpc_handler::account_info () response_l.put ("balance", balance); response_l.put ("modified_timestamp", std::to_string (info.modified)); response_l.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_l.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_l.put ("pending", account_pending.convert_to ()); + } response (response_l); } else @@ -1662,6 +1696,25 @@ void rai::rpc_handler::ledger () { sorting = sorting_optional.get (); } + bool representative (false); + boost::optional representative_optional (request.get_optional ("representative")); + if (representative_optional.is_initialized ()) + { + representative = representative_optional.get (); + } + bool weight (false); + boost::optional weight_optional (request.get_optional ("weight")); + if (weight_optional.is_initialized ()) + { + weight = weight_optional.get (); + } + bool pending (false); + boost::optional pending_optional (request.get_optional ("pending")); + if (pending_optional.is_initialized ()) + { + pending = pending_optional.get (); + } + boost::property_tree::ptree response_a; boost::property_tree::ptree response_l; boost::property_tree::ptree accounts; rai::transaction transaction (node.store.environment, nullptr, false); @@ -1670,6 +1723,7 @@ void rai::rpc_handler::ledger () for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && accounts.size () < count; ++i) { rai::account_info info (i->second); + rai::account account (i->first); boost::property_tree::ptree response_l; response_l.put ("frontier", info.head.to_string ()); response_l.put ("open_block", info.open_block.to_string ()); @@ -1679,7 +1733,23 @@ void rai::rpc_handler::ledger () response_l.put ("balance", balance); response_l.put ("modified_timestamp", std::to_string (info.modified)); response_l.put ("block_count", std::to_string (info.block_count)); - accounts.push_back (std::make_pair (rai::account (i->first).to_account (), response_l)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_l.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_l.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), response_l)); } } else // Sorting @@ -1696,6 +1766,7 @@ void rai::rpc_handler::ledger () for (auto i (ledger_l.begin ()), n (ledger_l.end ()); i != n && accounts.size () < count; ++i) { node.store.account_get (transaction, i->second, info); + rai::account account (i->second); response_l.put ("frontier", info.head.to_string ()); response_l.put ("open_block", info.open_block.to_string ()); response_l.put ("representative_block", info.rep_block.to_string ()); @@ -1704,11 +1775,27 @@ void rai::rpc_handler::ledger () response_l.put ("balance", balance); response_l.put ("modified_timestamp", std::to_string (info.modified)); response_l.put ("block_count", std::to_string (info.block_count)); - accounts.push_back (std::make_pair (rai::account (i->second).to_account (), response_l)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_l.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_l.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), response_l)); } } - response_l.add_child ("accounts", accounts); - response (response_l); + response_a.add_child ("accounts", accounts); + response (response_a); } else { From 78d0ee5e227a342e0ba56258d5984cc8324fb93b Mon Sep 17 00:00:00 2001 From: SergiySW Date: Wed, 4 Oct 2017 22:18:42 +0300 Subject: [PATCH 14/18] RPC blocks_info with optional "pending", "source" --- rai/node/rpc.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index c4b679dc..28586736 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -884,6 +884,18 @@ void rai::rpc_handler::blocks () void rai::rpc_handler::blocks_info () { + bool pending (false); + boost::optional pending_optional (request.get_optional ("pending")); + if (pending_optional.is_initialized ()) + { + pending = pending_optional.get (); + } + bool source (false); + boost::optional source_optional (request.get_optional ("source")); + if (source_optional.is_initialized ()) + { + source = source_optional.get (); + } std::vector hashes; boost::property_tree::ptree response_l; boost::property_tree::ptree blocks; @@ -906,6 +918,30 @@ void rai::rpc_handler::blocks_info () std::string contents; block->serialize_json (contents); entry.put ("contents", contents); + if (pending) + { + auto block_l (dynamic_cast (block.get ())); + bool exists (false); + if (block_l != nullptr) + { + auto destination (block_l->hashables.destination); + exists = node.store.pending_exists (transaction, rai::pending_key (destination, hash)); + } + entry.put ("pending", exists ? "1" : "0"); + } + if (source) + { + rai::block_hash source_hash (block->source ()); + std::unique_ptr block_a (node.store.block_get (transaction, source_hash)); + if (block_a != nullptr) + { + auto source_account (node.ledger.account (transaction, source_hash)); + entry.put ("source_account", source_account.to_account ()); + } + else { + entry.put ("source_account", "0"); + } + } blocks.push_back (std::make_pair (hash_text, entry)); } else From 2fb980818a7371289726e50684929fd0d2ca7354 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 14 Nov 2017 19:54:38 +0300 Subject: [PATCH 15/18] Returning accurate error output with RPC process --- rai/node/rpc.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 28586736..bbab102e 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -2331,10 +2331,70 @@ void rai::rpc_handler::process () if (!node.work.work_validate (*block)) { auto hash (block->hash ()); - node.process_active (std::move (block)); - boost::property_tree::ptree response_l; - response_l.put ("hash", hash.to_string ()); - response (response_l); + node.block_arrival.add (hash); + rai::process_return result; + rai::transaction transaction (node.store.environment, nullptr, true); + result = node.block_processor.process_receive_one (transaction, std::move (block)); + switch (result.code) + { + case rai::process_result::progress: + { + boost::property_tree::ptree response_l; + response_l.put ("hash", hash.to_string ()); + response (response_l); + break; + } + case rai::process_result::gap_previous: + { + error_response (response, "Gap previous block"); + break; + } + case rai::process_result::gap_source: + { + error_response (response, "Gap source block"); + break; + } + case rai::process_result::old: + { + error_response (response, "Old block"); + break; + } + case rai::process_result::bad_signature: + { + error_response (response, "Bad signature"); + break; + } + case rai::process_result::overspend: + { + error_response (response, "Overspend"); + break; + } + case rai::process_result::unreceivable: + { + error_response (response, "Unreceivable"); + break; + } + case rai::process_result::not_receive_from_send: + { + error_response (response, "Not receive from send"); + break; + } + case rai::process_result::fork: + { + error_response (response, "Fork"); + break; + } + case rai::process_result::account_mismatch: + { + error_response (response, "Account mismatch"); + break; + } + default: + { + error_response (response, "Error processing block"); + break; + } + } } else { From 943b601fc06909f3e3c4be22a16864bc43f3a4f2 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Wed, 15 Nov 2017 21:29:51 +0300 Subject: [PATCH 16/18] Adding processed RPC block to blocks observers --- rai/node/rpc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index bbab102e..3d406f10 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -2333,12 +2333,16 @@ void rai::rpc_handler::process () auto hash (block->hash ()); node.block_arrival.add (hash); rai::process_return result; - rai::transaction transaction (node.store.environment, nullptr, true); - result = node.block_processor.process_receive_one (transaction, std::move (block)); + std::shared_ptr block_a (std::move (block)); + { + rai::transaction transaction (node.store.environment, nullptr, true); + result = node.block_processor.process_receive_one (transaction, block_a); + } switch (result.code) { case rai::process_result::progress: { + node.observers.blocks (block_a, result.account, result.amount); boost::property_tree::ptree response_l; response_l.put ("hash", hash.to_string ()); response (response_l); From ef0129a6b8c6a202a553da5d90a86eca5c95b3ee Mon Sep 17 00:00:00 2001 From: SergiySW Date: Tue, 21 Nov 2017 12:31:52 +0300 Subject: [PATCH 17/18] Updating test RPC block_create --- rai/core_test/rpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index 0a223a3f..c2bcde8a 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -2952,7 +2952,7 @@ TEST (rpc, block_create) boost::property_tree::read_json (block_stream5, block_l); auto receive_block (rai::deserialize_block_json (block_l)); ASSERT_EQ (receive_hash, receive_block->hash ().to_string ()); - system.nodes [0]->process_receive_republish (std::move (receive_block)); + system.nodes [0]->process_active (std::move (receive_block)); latest = system.nodes [0]->latest (key.pub); ASSERT_EQ (receive_hash, latest.to_string ()); } From 8d52dc32fd0aaf97f0e8f62ec9154f1b161a4e44 Mon Sep 17 00:00:00 2001 From: SergiySW Date: Sat, 25 Nov 2017 22:15:52 +0300 Subject: [PATCH 18/18] RPC pending with optional "source" --- rai/core_test/rpc.cpp | 85 ++++++++++++++++++++++++++++++++++++++----- rai/node/rpc.cpp | 60 +++++++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 15 deletions(-) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index c2bcde8a..9d5bcd90 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -1288,6 +1288,27 @@ TEST (rpc, pending) ASSERT_EQ (200, response1.status); blocks_node = response1.json.get_child ("blocks"); ASSERT_EQ (0, blocks_node.size ()); + request.put ("threshold", "0"); + request.put ("source", "true"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response2.status); + blocks_node = response2.json.get_child ("blocks"); + ASSERT_EQ (1, blocks_node.size ()); + std::unordered_map amounts; + std::unordered_map sources; + for (auto i (blocks_node.begin ()), j (blocks_node.end ()); i != j; ++i) + { + rai::block_hash hash; + hash.decode_hex (i->first); + amounts[hash].decode_dec (i->second.get ("amount")); + sources[hash].decode_account (i->second.get ("source")); + } + ASSERT_EQ (amounts[block1->hash ()], 100); + ASSERT_EQ (sources[block1->hash ()], rai::test_genesis_key.pub); } TEST (rpc_config, serialization) @@ -2166,11 +2187,11 @@ TEST (rpc, accounts_pending) system.poll (); } ASSERT_EQ (200, response1.status); + std::unordered_map blocks; for (auto & pending : response1.json.get_child("blocks")) { std::string account_text (pending.first); ASSERT_EQ (key1.pub.to_account (), account_text); - std::unordered_map blocks; for (auto i (pending.second.begin ()), j (pending.second.end ()); i != j; ++i) { rai::block_hash hash; @@ -2179,8 +2200,31 @@ TEST (rpc, accounts_pending) amount.decode_dec (i->second.get ("")); blocks [hash] = amount; } - ASSERT_EQ (blocks[block1->hash ()], 100); } + ASSERT_EQ (blocks[block1->hash ()], 100); + request.put ("source", "true"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response2.status); + std::unordered_map amounts; + std::unordered_map sources; + for (auto & pending : response2.json.get_child("blocks")) + { + std::string account_text (pending.first); + ASSERT_EQ (key1.pub.to_account (), account_text); + for (auto i (pending.second.begin ()), j (pending.second.end ()); i != j; ++i) + { + rai::block_hash hash; + hash.decode_hex (i->first); + amounts[hash].decode_dec (i->second.get ("amount")); + sources[hash].decode_account (i->second.get ("source")); + } + } + ASSERT_EQ (amounts[block1->hash ()], 100); + ASSERT_EQ (sources[block1->hash ()], rai::test_genesis_key.pub); } TEST (rpc, blocks) @@ -2315,6 +2359,7 @@ TEST (rpc, wallet_pending) rai::system system0 (24000, 1); rai::keypair key1; system0.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + system0.wallet (0)->insert_adhoc (key1.prv); auto block1 (system0.wallet (0)->send_action (rai::test_genesis_key.pub, key1.pub, 100)); rai::rpc rpc (system0.service, *system0.nodes [0], rai::rpc_config (true)); rpc.start (); @@ -2331,10 +2376,8 @@ TEST (rpc, wallet_pending) for (auto & pending : response.json.get_child("blocks")) { std::string account_text (pending.first); - ASSERT_EQ (rai::test_genesis_key.pub.to_account (), account_text); - auto & blocks_node (pending.second.get_child (rai::test_genesis_key.pub.to_account ())); - ASSERT_EQ (1, blocks_node.size ()); - rai::block_hash hash1 (blocks_node.begin ()->second.get ("")); + ASSERT_EQ (key1.pub.to_account (), account_text); + rai::block_hash hash1 (pending.second.begin ()->second.get ("")); ASSERT_EQ (block1->hash (), hash1); } request.put ("threshold", "100"); // Threshold test @@ -2344,11 +2387,11 @@ TEST (rpc, wallet_pending) system0.poll (); } ASSERT_EQ (200, response0.status); + std::unordered_map blocks; for (auto & pending : response0.json.get_child("blocks")) { std::string account_text (pending.first); - ASSERT_EQ (rai::test_genesis_key.pub.to_account (), account_text); - std::unordered_map blocks; + ASSERT_EQ (key1.pub.to_account (), account_text); for (auto i (pending.second.begin ()), j (pending.second.end ()); i != j; ++i) { rai::block_hash hash; @@ -2357,8 +2400,8 @@ TEST (rpc, wallet_pending) amount.decode_dec (i->second.get ("")); blocks [hash] = amount; } - ASSERT_EQ (blocks[block1->hash ()], 100); } + ASSERT_EQ (blocks[block1->hash ()], 100); request.put ("threshold", "101"); test_response response1 (request, rpc, system0.service); while (response1.status == 0) @@ -2368,6 +2411,30 @@ TEST (rpc, wallet_pending) ASSERT_EQ (200, response1.status); auto & pending1 (response1.json.get_child ("blocks")); ASSERT_EQ (0, pending1.size ()); + request.put ("threshold", "0"); + request.put ("source", "true"); + test_response response2 (request, rpc, system0.service); + while (response2.status == 0) + { + system0.poll (); + } + ASSERT_EQ (200, response2.status); + std::unordered_map amounts; + std::unordered_map sources; + for (auto & pending : response2.json.get_child("blocks")) + { + std::string account_text (pending.first); + ASSERT_EQ (key1.pub.to_account (), account_text); + for (auto i (pending.second.begin ()), j (pending.second.end ()); i != j; ++i) + { + rai::block_hash hash; + hash.decode_hex (i->first); + amounts[hash].decode_dec (i->second.get ("amount")); + sources[hash].decode_account (i->second.get ("source")); + } + } + ASSERT_EQ (amounts[block1->hash ()], 100); + ASSERT_EQ (sources[block1->hash ()], rai::test_genesis_key.pub); } TEST (rpc, receive_minimum) diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 3d406f10..4def7a33 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -751,6 +751,7 @@ void rai::rpc_handler::accounts_pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); + bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -769,6 +770,11 @@ void rai::rpc_handler::accounts_pending () error_response (response, "Bad threshold number"); } } + boost::optional source_optional (request.get_optional ("source")); + if (source_optional.is_initialized ()) + { + source = source_optional.get (); + } boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -783,7 +789,7 @@ void rai::rpc_handler::accounts_pending () for (auto i (node.store.pending_begin (transaction, rai::pending_key (account, 0))), n (node.store.pending_begin (transaction, rai::pending_key (end, 0))); i != n && peers_l.size () < count; ++i) { rai::pending_key key (i->first); - if (threshold.is_zero ()) + if (threshold.is_zero () && !source) { boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); @@ -794,7 +800,17 @@ void rai::rpc_handler::accounts_pending () rai::pending_info info (i->second); if (info.amount.number () >= threshold.number ()) { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + if (source) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + pending_tree.put ("source", info.source.to_account ()); + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } } } } @@ -2040,6 +2056,7 @@ void rai::rpc_handler::pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); + bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -2058,6 +2075,11 @@ void rai::rpc_handler::pending () error_response (response, "Bad threshold number"); } } + boost::optional source_optional (request.get_optional ("source")); + if (source_optional.is_initialized ()) + { + source = source_optional.get (); + } boost::property_tree::ptree response_l; boost::property_tree::ptree peers_l; { @@ -2066,7 +2088,7 @@ void rai::rpc_handler::pending () for (auto i (node.store.pending_begin (transaction, rai::pending_key (account, 0))), n (node.store.pending_begin (transaction, rai::pending_key (end, 0))); i != n && peers_l.size ()< count; ++i) { rai::pending_key key (i->first); - if (threshold.is_zero ()) + if (threshold.is_zero () && !source) { boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); @@ -2077,7 +2099,17 @@ void rai::rpc_handler::pending () rai::pending_info info (i->second); if (info.amount.number () >= threshold.number ()) { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + if (source) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + pending_tree.put ("source", info.source.to_account ()); + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } } } } @@ -3520,6 +3552,7 @@ void rai::rpc_handler::wallet_pending () { uint64_t count (std::numeric_limits ::max ()); rai::uint128_union threshold (0); + bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -3538,6 +3571,11 @@ void rai::rpc_handler::wallet_pending () error_response (response, "Bad threshold number"); } } + boost::optional source_optional (request.get_optional ("source")); + if (source_optional.is_initialized ()) + { + source = source_optional.get (); + } boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -3549,7 +3587,7 @@ void rai::rpc_handler::wallet_pending () for (auto ii (node.store.pending_begin (transaction, rai::pending_key (account, 0))), nn (node.store.pending_begin (transaction, rai::pending_key (end, 0))); ii != nn && peers_l.size ()< count; ++ii) { rai::pending_key key (ii->first); - if (threshold.is_zero ()) + if (threshold.is_zero () && !source) { boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); @@ -3560,7 +3598,17 @@ void rai::rpc_handler::wallet_pending () rai::pending_info info (ii->second); if (info.amount.number () >= threshold.number ()) { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + if (source) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + pending_tree.put ("source", info.source.to_account ()); + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } } } }