diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index fb3d2ce0..dba52c8e 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -1238,14 +1238,14 @@ TEST (rpc, peers) TEST (rpc, pending) { - rai::system system (24000, 1); + rai::system system (24000, 1); rai::keypair key1; system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); auto block1 (system.wallet (0)->send_action (rai::test_genesis_key.pub, key1.pub, 100)); - rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); rpc.start (); - boost::property_tree::ptree request; - request.put ("action", "pending"); + boost::property_tree::ptree request; + request.put ("action", "pending"); request.put ("account", key1.pub.to_account ()); request.put ("count", "100"); test_response response (request, rpc, system.service); @@ -1253,11 +1253,39 @@ TEST (rpc, pending) { system.poll (); } - ASSERT_EQ (200, response.status); - auto & blocks_node (response.json.get_child ("blocks")); + ASSERT_EQ (200, response.status); + auto & blocks_node (response.json.get_child ("blocks")); ASSERT_EQ (1, blocks_node.size ()); rai::block_hash hash1 (blocks_node.begin ()->second.get ("")); ASSERT_EQ (block1->hash (), hash1); + request.put ("threshold", "100"); // Threshold test + test_response response0 (request, rpc, system.service); + while (response0.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response0.status); + blocks_node = response0.json.get_child ("blocks"); + ASSERT_EQ (1, blocks_node.size ()); + std::unordered_map blocks; + for (auto i (blocks_node.begin ()), j (blocks_node.end ()); i != j; ++i) + { + rai::block_hash hash; + hash.decode_hex (i->first); + rai::uint128_union amount; + amount.decode_dec (i->second.get ("")); + blocks [hash] = amount; + } + ASSERT_EQ (blocks[block1->hash ()], 100); + request.put ("threshold", "101"); + test_response response1 (request, rpc, system.service); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + blocks_node = response1.json.get_child ("blocks"); + ASSERT_EQ (0, blocks_node.size ()); } TEST (rpc_config, serialization) @@ -1934,23 +1962,26 @@ TEST (rpc, bootstrap_any) TEST (rpc, republish) { - rai::system system (24000, 2); + rai::system system (24000, 2); rai::keypair key; + rai::genesis genesis; 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)); system.nodes [0]->process (send); - rai::rpc rpc (system.service, node1, rai::rpc_config (true)); + rai::open_block open (send.hash (), 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); + rai::rpc rpc (system.service, node1, rai::rpc_config (true)); rpc.start (); - boost::property_tree::ptree request; - request.put ("action", "republish"); + boost::property_tree::ptree request; + request.put ("action", "republish"); request.put ("hash", send.hash ().to_string ()); test_response response (request, rpc, system.service); while (response.status == 0) { system.poll (); } - ASSERT_EQ (200, response.status); + ASSERT_EQ (200, response.status); auto iterations (0); while (system.nodes[1]->balance (rai::test_genesis_key.pub) == rai::genesis_amount) { @@ -1958,6 +1989,50 @@ TEST (rpc, republish) ++iterations; ASSERT_GT (200, iterations); } + auto & blocks_node (response.json.get_child ("blocks")); + std::vector blocks; + for (auto i (blocks_node.begin ()), n (blocks_node.end ()); i != n; ++i) + { + blocks.push_back (rai::block_hash (i->second.get (""))); + } + ASSERT_EQ (1, blocks.size ()); + ASSERT_EQ (send.hash (), blocks [0]); + + request.put ("hash", genesis.hash ().to_string ()); + request.put ("count", 1); + test_response response1 (request, rpc, system.service); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + blocks_node = response1.json.get_child ("blocks"); + blocks.clear(); + for (auto i (blocks_node.begin ()), n (blocks_node.end ()); i != n; ++i) + { + blocks.push_back (rai::block_hash (i->second.get (""))); + } + ASSERT_EQ (1, blocks.size ()); + ASSERT_EQ (genesis.hash (), blocks [0]); + + request.put ("hash", open.hash ().to_string ()); + request.put ("sources", 2); + 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"); + blocks.clear(); + for (auto i (blocks_node.begin ()), n (blocks_node.end ()); i != n; ++i) + { + blocks.push_back (rai::block_hash (i->second.get (""))); + } + ASSERT_EQ (3, blocks.size ()); + ASSERT_EQ (genesis.hash (), blocks [0]); + ASSERT_EQ (send.hash (), blocks [1]); + ASSERT_EQ (open.hash (), blocks [2]); } TEST (rpc, deterministic_key) @@ -2083,6 +2158,28 @@ TEST (rpc, accounts_pending) rai::block_hash hash1 (blocks.second.begin ()->second.get ("")); ASSERT_EQ (block1->hash (), hash1); } + request.put ("threshold", "100"); // Threshold test + test_response response1 (request, rpc, system.service); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + 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; + hash.decode_hex (i->first); + rai::uint128_union amount; + amount.decode_dec (i->second.get ("")); + blocks [hash] = amount; + } + ASSERT_EQ (blocks[block1->hash ()], 100); + } } TEST (rpc, blocks) @@ -2151,7 +2248,6 @@ TEST (rpc, wallet_balances) system0.poll (); } ASSERT_EQ (200, response.status); - std::vector balances; for (auto & balances : response.json.get_child("balances")) { std::string account_text (balances.first); @@ -2162,3 +2258,274 @@ TEST (rpc, wallet_balances) ASSERT_EQ ("0", pending_text); } } + +TEST (rpc, pending_exists) +{ + rai::system system (24000, 1); + rai::keypair key1; + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + auto hash0 (system.nodes [0]->latest (rai::genesis_account)); + auto block1 (system.wallet (0)->send_action (rai::test_genesis_key.pub, key1.pub, 100)); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "pending_exists"); + request.put ("hash", hash0.to_string ()); + test_response response0 (request, rpc, system.service); + while (response0.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response0.status); + std::string exists_text (response0.json.get ("exists")); + ASSERT_EQ ("0", exists_text); + request.put ("hash", block1->hash ().to_string ()); + test_response response1 (request, rpc, system.service); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + std::string exists_text1 (response1.json.get ("exists")); + ASSERT_EQ ("1", exists_text1); +} + +TEST (rpc, wallet_pending) +{ + rai::system system0 (24000, 1); + rai::keypair key1; + system0.wallet (0)->insert_adhoc (rai::test_genesis_key.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 (); + boost::property_tree::ptree request; + request.put ("action", "wallet_pending"); + request.put ("wallet", system0.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("count", "100"); + test_response response (request, rpc, system0.service); + while (response.status == 0) + { + system0.poll (); + } + ASSERT_EQ (200, response.status); + 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 (block1->hash (), hash1); + } + request.put ("threshold", "100"); // Threshold test + test_response response0 (request, rpc, system0.service); + while (response0.status == 0) + { + system0.poll (); + } + ASSERT_EQ (200, response0.status); + 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; + for (auto i (pending.second.begin ()), j (pending.second.end ()); i != j; ++i) + { + rai::block_hash hash; + hash.decode_hex (i->first); + rai::uint128_union amount; + amount.decode_dec (i->second.get ("")); + blocks [hash] = amount; + } + ASSERT_EQ (blocks[block1->hash ()], 100); + } + request.put ("threshold", "101"); + test_response response1 (request, rpc, system0.service); + while (response1.status == 0) + { + system0.poll (); + } + ASSERT_EQ (200, response1.status); + auto & pending1 (response1.json.get_child ("blocks")); + ASSERT_EQ (0, pending1.size ()); +} + +TEST (rpc, receive_minimum) +{ + 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", "receive_minimum"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string amount (response.json.get ("amount")); + ASSERT_EQ (system.nodes [0]->config.receive_minimum.to_string_dec (), amount); +} + +TEST (rpc, receive_minimum_set) +{ + 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", "receive_minimum_set"); + request.put ("amount", "100"); + ASSERT_NE (system.nodes [0]->config.receive_minimum.to_string_dec (), "100"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string success (response.json.get ("success")); + ASSERT_TRUE (success.empty()); + ASSERT_EQ (system.nodes [0]->config.receive_minimum.to_string_dec (), "100"); +} + +TEST (rpc, work_get) +{ + rai::system system (24000, 1); + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "work_get"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("account", rai::test_genesis_key.pub.to_account ()); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string work_text (response.json.get ("work")); + uint64_t work (1); + rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false); + system.nodes [0]->wallets.items.begin ()->second->store.work_get (transaction, rai::genesis_account, work); + ASSERT_EQ (rai::to_string_hex (work), work_text); +} + +TEST (rpc, wallet_work_get) +{ + rai::system system (24000, 1); + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "wallet_work_get"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false); + for (auto & works : response.json.get_child("works")) + { + std::string account_text (works.first); + ASSERT_EQ (rai::test_genesis_key.pub.to_account (), account_text); + std::string work_text (works.second.get ("")); + uint64_t work (1); + system.nodes [0]->wallets.items.begin ()->second->store.work_get (transaction, rai::genesis_account, work); + ASSERT_EQ (rai::to_string_hex (work), work_text); + } +} + +TEST (rpc, work_set) +{ + rai::system system (24000, 1); + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + uint64_t work0 (100); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "work_set"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("account", rai::test_genesis_key.pub.to_account ()); + request.put ("work", rai::to_string_hex (work0)); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string success (response.json.get ("success")); + ASSERT_TRUE (success.empty()); + uint64_t work1 (1); + rai::transaction transaction (system.nodes [0]->store.environment, nullptr, false); + system.nodes [0]->wallets.items.begin ()->second->store.work_get (transaction, rai::genesis_account, work1); + ASSERT_EQ (work1, work0); +} + +TEST (rpc, search_pending_all) +{ + rai::system system (24000, 1); + system.wallet (0)->insert_adhoc (rai::test_genesis_key.prv); + rai::send_block block (system.nodes [0]->latest (rai::test_genesis_key.pub), rai::test_genesis_key.pub, rai::genesis_amount - system.nodes [0]->config.receive_minimum.number (), rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, system.nodes [0]->ledger.process (rai::transaction (system.nodes [0]->store.environment, nullptr, true), block).code); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "search_pending_all"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + auto iterations (0); + while (system.nodes [0]->balance (rai::test_genesis_key.pub) != rai::genesis_amount) + { + system.poll (); + ++iterations; + ASSERT_LT (iterations, 200); + } +} + +TEST (rpc, wallet_republish) +{ + rai::system system (24000, 1); + rai::genesis genesis; + rai::keypair key; + while (key.pub < rai::test_genesis_key.pub) + { + rai::keypair key1; + key.pub = key1.pub; + key.prv.data = key1.prv.data; + } + 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 (), 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); + rai::rpc rpc (system.service, *system.nodes [0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "wallet_republish"); + request.put ("wallet", system.nodes [0]->wallets.items.begin ()->first.to_string ()); + request.put ("count", 1); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + auto & blocks_node (response.json.get_child ("blocks")); + std::vector blocks; + for (auto i (blocks_node.begin ()), n (blocks_node.end ()); i != n; ++i) + { + blocks.push_back (rai::block_hash (i->second.get (""))); + } + ASSERT_EQ (2, blocks.size ()); + ASSERT_EQ (send.hash (), blocks [0]); + ASSERT_EQ (open.hash (), blocks [1]); +} diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 7764a4b9..3155848d 100755 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -588,42 +588,72 @@ void rai::rpc_handler::accounts_frontiers () void rai::rpc_handler::accounts_pending () { - std::string count_text (request.get ("count")); - uint64_t count; - if (!decode_unsigned (count_text, count)) + uint64_t count (std::numeric_limits ::max ()); + rai::uint128_union threshold (0); + try { - boost::property_tree::ptree response_l; - boost::property_tree::ptree pending; - rai::transaction transaction (node.store.environment, nullptr, false); - for (auto &accounts : request.get_child("accounts")) + std::string count_text (request.get ("count")); + auto error (decode_unsigned (count_text, count)); + if (error) { - std::string account_text = accounts.second.data (); - rai::uint256_union account; - if (!account.decode_account (account_text)) + error_response (response, "Invalid count"); + } + } + catch (std::runtime_error &) + { + // If there is no "count" in request + } + try + { + std::string threshold_text (request.get ("threshold")); + auto error_threshold (threshold.decode_dec (threshold_text)); + 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); + for (auto &accounts : request.get_child("accounts")) + { + std::string account_text = accounts.second.data (); + rai::uint256_union account; + if (!account.decode_account (account_text)) + { + boost::property_tree::ptree peers_l; + rai::account end (account.number () + 1); + 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) { - boost::property_tree::ptree peers_l; - rai::account end (account.number () + 1); - 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 ()) { - rai::pending_key key (i->first); boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); peers_l.push_back (std::make_pair ("", entry)); } - pending.add_child (account.to_account (), peers_l); - } - else - { - error_response (response, "Bad account number"); + else + { + rai::pending_info info (i->second); + if (info.amount.number () >= threshold.number ()) + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } + } } + pending.add_child (account.to_account (), peers_l); + } + else + { + error_response (response, "Bad account number"); } - response_l.add_child ("blocks", pending); - response (response_l); - } - else - { - error_response (response, "Invalid count"); } + response_l.add_child ("blocks", pending); + response (response_l); } void rai::rpc_handler::available_supply () @@ -1317,26 +1347,60 @@ void rai::rpc_handler::pending () rai::account account; if (!account.decode_account(account_text)) { - std::string count_text (request.get ("count")); - uint64_t count; - if (!decode_unsigned (count_text, count)) + uint64_t count (std::numeric_limits ::max ()); + rai::uint128_union threshold (0); + try { - boost::property_tree::ptree response_l; - boost::property_tree::ptree peers_l; + std::string count_text (request.get ("count")); + auto error (decode_unsigned (count_text, count)); + if (error) { - rai::transaction transaction (node.store.environment, nullptr, false); - rai::account end (account.number () + 1); - 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) + error_response (response, "Invalid count"); + } + } + catch (std::runtime_error &) + { + // If there is no "count" in request + } + try + { + std::string threshold_text (request.get ("threshold")); + auto error_threshold (threshold.decode_dec (threshold_text)); + 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; + { + rai::transaction transaction (node.store.environment, nullptr, false); + rai::account end (account.number () + 1); + 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 ()) { - rai::pending_key key (i->first); boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); peers_l.push_back (std::make_pair ("", entry)); } + else + { + rai::pending_info info (i->second); + if (info.amount.number () >= threshold.number ()) + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } + } } - response_l.add_child ("blocks", peers_l); - response (response_l); } + response_l.add_child ("blocks", peers_l); + response (response_l); } else { @@ -1344,6 +1408,35 @@ void rai::rpc_handler::pending () } } +void rai::rpc_handler::pending_exists () +{ + std::string hash_text (request.get ("hash")); + rai::uint256_union hash; + auto error (hash.decode_hex (hash_text)); + if (!error) + { + rai::transaction transaction (node.store.environment, nullptr, false); + auto block (node.store.block_get (transaction, hash)); + if (block != nullptr) + { + auto block_l (static_cast (block.release ())); + auto account (block_l->hashables.destination); + boost::property_tree::ptree response_l; + auto exists (node.store.pending_exists (transaction, rai::pending_key (account, hash))); + response_l.put ("exists", exists ? "1" : "0"); + response (response_l); + } + else + { + error_response (response, "Block not found"); + } + } + else + { + error_response (response, "Bad hash number"); + } +} + void rai::rpc_handler::payment_begin () { std::string id_text (request.get ("wallet")); @@ -1691,6 +1784,44 @@ void rai::rpc_handler::receive () } } +void rai::rpc_handler::receive_minimum () +{ + if (rpc.config.enable_control) + { + boost::property_tree::ptree response_l; + response_l.put ("amount", node.config.receive_minimum.to_string_dec ()); + response (response_l); + } + else + { + error_response (response, "RPC control is disabled"); + } +} + +void rai::rpc_handler::receive_minimum_set () +{ + if (rpc.config.enable_control) + { + std::string amount_text (request.get ("amount")); + rai::uint128_union amount; + if (!amount.decode_dec (amount_text)) + { + node.config.receive_minimum = amount; + boost::property_tree::ptree response_l; + response_l.put ("success", ""); + response (response_l); + } + else + { + error_response (response, "Bad amount number"); + } + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::representatives () { boost::property_tree::ptree response_l; @@ -1708,23 +1839,77 @@ void rai::rpc_handler::representatives () void rai::rpc_handler::republish () { + uint64_t count (2048U); + uint64_t sources (0); + try + { + std::string count_text (request.get ("count")); + auto error (decode_unsigned (count_text, count)); + if (error) + { + error_response (response, "Invalid count"); + } + } + catch (std::runtime_error &) + { + // If there is no "count" in request + } + try + { + std::string sources_text (request.get ("sources")); + auto error (decode_unsigned (sources_text, sources)); + if (error) + { + error_response (response, "Invalid sources number"); + } + } + catch (std::runtime_error &) + { + // If there is no "sources" in request + } std::string hash_text (request.get ("hash")); rai::uint256_union hash; auto error (hash.decode_hex (hash_text)); if (!error) { + boost::property_tree::ptree response_l; + boost::property_tree::ptree blocks; rai::transaction transaction (node.store.environment, nullptr, false); auto block (node.store.block_get (transaction, hash)); if (block != nullptr) { - while (!hash.is_zero ()) + for (auto i (0); !hash.is_zero () && i < count; ++i) { block = node.store.block_get (transaction, hash); - node.network.republish_block (std::move (block)); + if (sources != 0) // Republish source chain + { + std::unique_ptr block_a; + rai::block_hash source (block->source ()); + std::vector hashes; + while (!source.is_zero () && hashes.size () < sources) + { + hashes.push_back (source); + block_a = node.store.block_get (transaction, source); + source = block_a->previous (); + } + std::reverse (hashes.begin (), hashes.end ()); + for (auto & hash_l : hashes) + { + block_a = node.store.block_get (transaction, hash_l); + node.network.republish_block (std::move (block_a)); + boost::property_tree::ptree entry_l; + entry_l.put ("", hash_l.to_string ()); + blocks.push_back (std::make_pair ("", entry_l)); + } + } + node.network.republish_block (std::move (block)); // Republish block + boost::property_tree::ptree entry; + entry.put ("", hash.to_string ()); + blocks.push_back (std::make_pair ("", entry)); hash = node.store.block_successor (transaction, hash); } - boost::property_tree::ptree response_l; - response_l.put ("success", ""); + response_l.put ("success", ""); // obsolete + response_l.add_child ("blocks", blocks); response (response_l); } else @@ -1767,6 +1952,21 @@ void rai::rpc_handler::search_pending () } } +void rai::rpc_handler::search_pending_all () +{ + if (rpc.config.enable_control) + { + node.wallets.search_pending_all (); + boost::property_tree::ptree response_l; + response_l.put ("success", ""); + response (response_l); + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::send () { if (rpc.config.enable_control) @@ -2220,6 +2420,89 @@ void rai::rpc_handler::wallet_key_valid () } } +void rai::rpc_handler::wallet_pending () +{ + 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 ()) + { + uint64_t count (std::numeric_limits ::max ()); + rai::uint128_union threshold (0); + try + { + std::string count_text (request.get ("count")); + auto error_count (decode_unsigned (count_text, count)); + if (error_count) + { + error_response (response, "Invalid count"); + } + } + catch (std::runtime_error &) + { + // If there is no "count" in request + } + try + { + std::string threshold_text (request.get ("threshold")); + auto error_threshold (threshold.decode_dec (threshold_text)); + 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); + 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 peers_l; + rai::account end (account.number () + 1); + 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 ()) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + } + else + { + 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 (!peers_l.empty ()) + { + pending.add_child (account.to_account (), peers_l); + } + } + response_l.add_child ("blocks", pending); + response (response_l); + } + else + { + error_response (response, "Wallet not found"); + } + } + else + { + error_response (response, "Bad wallet number"); + } +} + void rai::rpc_handler::wallet_representative () { std::string wallet_text (request.get ("wallet")); @@ -2290,6 +2573,113 @@ void rai::rpc_handler::wallet_representative_set () } } +void rai::rpc_handler::wallet_republish () +{ + 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 ()) + { + uint64_t count; + std::string count_text (request.get ("count")); + auto error (decode_unsigned (count_text, count)); + if (!error) + { + boost::property_tree::ptree response_l; + boost::property_tree::ptree blocks; + rai::transaction transaction (node.store.environment, nullptr, false); + for (auto i (existing->second->store.begin (transaction)), n (existing->second->store.end ()); i != n; ++i) + { + rai::account account(i->first); + auto latest (node.ledger.latest (transaction, account)); + std::unique_ptr block; + std::vector hashes; + while (!latest.is_zero () && hashes.size () < count) + { + hashes.push_back (latest); + block = node.store.block_get (transaction, latest); + latest = block->previous (); + } + std::reverse (hashes.begin (), hashes.end ()); + for (auto & hash : hashes) + { + block = node.store.block_get (transaction, hash); + node.network.republish_block (std::move (block));; + boost::property_tree::ptree entry; + entry.put ("", hash.to_string ()); + blocks.push_back (std::make_pair ("", entry)); + } + } + response_l.add_child ("blocks", blocks); + response (response_l); + } + else + { + error_response (response, "Invalid count"); + } + } + 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_work_get () +{ + 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; + boost::property_tree::ptree works; + rai::transaction transaction (node.store.environment, nullptr, false); + for (auto i (existing->second->store.begin (transaction)), n (existing->second->store.end ()); i != n; ++i) + { + rai::account account(i->first); + uint64_t work (0); + auto error_work (existing->second->store.work_get (transaction, account, work)); + works.put (account.to_account (), rai::to_string_hex (work)); + } + response_l.add_child ("works", works); + 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::work_generate () { if (rpc.config.enable_control) @@ -2349,6 +2739,121 @@ void rai::rpc_handler::work_cancel () } } +void rai::rpc_handler::work_get () +{ + 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 ()) + { + std::string account_text (request.get ("account")); + rai::account account; + auto error (account.decode_account (account_text)); + if (!error) + { + rai::transaction transaction (node.store.environment, nullptr, false); + auto account_check (existing->second->store.find (transaction, account)); + if (account_check != existing->second->store.end ()) + { + uint64_t work (0); + auto error_work (existing->second->store.work_get (transaction, account, work)); + boost::property_tree::ptree response_l; + response_l.put ("work", rai::to_string_hex (work)); + response (response_l); + } + else + { + error_response (response, "Account not found in wallet"); + } + } + else + { + error_response (response, "Bad account number"); + } + } + 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::work_set () +{ + 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 ()) + { + std::string account_text (request.get ("account")); + rai::account account; + auto error (account.decode_account (account_text)); + if (!error) + { + rai::transaction transaction (node.store.environment, nullptr, true); + auto account_check (existing->second->store.find (transaction, account)); + if (account_check != existing->second->store.end ()) + { + std::string work_text (request.get ("work")); + uint64_t work; + auto work_error (rai::from_string_hex (work_text, work)); + if (!work_error) + { + existing->second->store.work_put (transaction, account, work); + boost::property_tree::ptree response_l; + response_l.put ("success", ""); + response (response_l); + } + else + { + error_response (response, "Bad work"); + } + } + else + { + error_response (response, "Account not found in wallet"); + } + } + else + { + error_response (response, "Bad account number"); + } + } + 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::work_validate () { std::string hash_text (request.get ("hash")); @@ -2638,6 +3143,10 @@ void rai::rpc_handler::process_request () { pending (); } + else if (action == "pending_exists") + { + pending_exists (); + } else if (action == "process") { process (); @@ -2654,6 +3163,14 @@ void rai::rpc_handler::process_request () { receive (); } + else if (action == "receive_minimum") + { + receive_minimum (); + } + else if (action == "receive_minimum_set") + { + receive_minimum_set (); + } else if (action == "representatives") { representatives (); @@ -2666,6 +3183,10 @@ void rai::rpc_handler::process_request () { search_pending (); } + else if (action == "search_pending_all") + { + search_pending_all (); + } else if (action == "send") { send (); @@ -2722,6 +3243,10 @@ void rai::rpc_handler::process_request () { wallet_key_valid (); } + else if (action == "wallet_pending") + { + wallet_pending (); + } else if (action == "wallet_representative") { wallet_representative (); @@ -2730,6 +3255,14 @@ void rai::rpc_handler::process_request () { wallet_representative_set (); } + else if (action == "wallet_republish") + { + wallet_republish (); + } + else if (action == "wallet_work_get") + { + wallet_work_get (); + } else if (action == "work_generate") { work_generate (); @@ -2738,6 +3271,14 @@ void rai::rpc_handler::process_request () { work_cancel (); } + else if (action == "work_get") + { + work_get (); + } + else if (action == "work_set") + { + work_set (); + } else if (action == "work_validate") { work_validate (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index 7cc8d98d..24b85d42 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -132,13 +132,17 @@ public: void payment_wait (); void peers (); void pending (); + void pending_exists (); void process (); void rai_to_raw (); void rai_from_raw (); void receive (); + void receive_minimum (); + void receive_minimum_set (); void representatives (); void republish (); void search_pending (); + void search_pending_all (); void send (); void stop (); void successors (); @@ -154,10 +158,15 @@ public: void wallet_export (); void wallet_frontiers (); void wallet_key_valid (); + void wallet_pending (); void wallet_representative (); void wallet_representative_set (); + void wallet_republish (); + void wallet_work_get (); void work_generate (); void work_cancel (); + void work_get (); + void work_set (); void work_validate (); std::string body; rai::node & node;