Skip to content

Commit 7061c6f

Browse files
committed
Implement and test handle_blockchain_transaction_get_merkle.
1 parent 5372396 commit 7061c6f

2 files changed

Lines changed: 125 additions & 4 deletions

File tree

src/protocols/protocol_electrum.cpp

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,60 @@ void protocol_electrum::handle_blockchain_transaction_get(const code& ec,
518518
}
519519

520520
void protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec,
521-
rpc_interface::blockchain_transaction_get_merkle, const std::string& ,
522-
double ) NOEXCEPT
521+
rpc_interface::blockchain_transaction_get_merkle, const std::string& tx_hash,
522+
double height) NOEXCEPT
523523
{
524-
if (stopped(ec)) return;
525-
send_code(error::not_implemented);
524+
using namespace system;
525+
if (stopped(ec))
526+
return;
527+
528+
hash_digest hash{};
529+
size_t block_height{};
530+
if (!to_integer(block_height, height) || !decode_hash(hash, tx_hash))
531+
{
532+
send_code(error::invalid_argument);
533+
return;
534+
}
535+
536+
const auto& query = archive();
537+
const auto block_link = query.to_confirmed(block_height);
538+
if (block_link.is_terminal())
539+
{
540+
send_code(error::not_found);
541+
return;
542+
}
543+
544+
auto hashes = query.get_tx_keys(block_link);
545+
if (hashes.empty())
546+
{
547+
send_code(error::server_error);
548+
return;
549+
}
550+
551+
const auto index = find_position(hashes, hash);
552+
if (is_negative(index))
553+
{
554+
send_code(error::not_found);
555+
return;
556+
}
557+
558+
using namespace chain;
559+
const auto position = to_unsigned(index);
560+
const auto proof = block::merkle_branch(index, std::move(hashes));
561+
562+
array_t branch(proof.size());
563+
std::ranges::transform(proof, branch.begin(),
564+
[](const auto& hash) NOEXCEPT{ return encode_hash(hash); });
565+
566+
send_result(
567+
{
568+
object_t
569+
{
570+
{ "merkle", std::move(branch) },
571+
{ "block_height", block_height },
572+
{ "pos", position }
573+
}
574+
}, two * hash_size * add1(branch.size()), BIND(complete, _1));
526575
}
527576

528577
void protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec,

test/protocols/electrum/electrum_transactions.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,78 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verb
151151

152152
// blockchain.transaction.get_merkle
153153

154+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__empty_hash__invalid_argument)
155+
{
156+
BOOST_CHECK(handshake());
157+
158+
const auto response = get(R"({"id":100,"method":"blockchain.transaction.get_merkle","params":["",0]})" "\n");
159+
BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value());
160+
}
161+
162+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__invalid_hash_encoding__invalid_argument)
163+
{
164+
BOOST_CHECK(handshake());
165+
166+
const auto response = get(R"({"id":101,"method":"blockchain.transaction.get_merkle","params":["deadbeef",0]})" "\n");
167+
BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), invalid_argument.value());
168+
}
169+
170+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__nonexistent_height__not_found)
171+
{
172+
BOOST_CHECK(handshake());
173+
174+
const auto bogus = "0000000000000000000000000000000000000000000000000000000000000042";
175+
const auto request = R"({"id":102,"method":"blockchain.transaction.get_merkle","params":["%1%",999]})" "\n";
176+
const auto response = get((boost::format(request) % bogus).str());
177+
BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value());
178+
}
179+
180+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__tx_not_in_block__not_found)
181+
{
182+
BOOST_CHECK(handshake());
183+
184+
const auto bogus = "0000000000000000000000000000000000000000000000000000000000000042";
185+
const auto request = R"({"id":103,"method":"blockchain.transaction.get_merkle","params":["%1%",0]})" "\n";
186+
const auto response = get((boost::format(request) % bogus).str());
187+
BOOST_CHECK_EQUAL(response.at("error").as_object().at("code").as_int64(), not_found.value());
188+
}
189+
190+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__genesis_coinbase__expected)
191+
{
192+
BOOST_CHECK(handshake());
193+
194+
const auto& coinbase = *genesis.transactions_ptr()->front();
195+
const auto tx_hash = encode_hash(coinbase.hash(false));
196+
const auto request = R"({"id":104,"method":"blockchain.transaction.get_merkle","params":["%1%",0]})" "\n";
197+
const auto response = get((boost::format(request) % tx_hash).str());
198+
const auto& result = response.at("result").as_object();
199+
BOOST_CHECK_EQUAL(result.at("block_height").as_int64(), 0);
200+
BOOST_CHECK_EQUAL(result.at("pos").as_int64(), 0);
201+
BOOST_CHECK(result.at("merkle").as_array().empty());
202+
}
203+
204+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__missing_param__dropped)
205+
{
206+
BOOST_CHECK(handshake());
207+
208+
const auto& coinbase = *genesis.transactions_ptr()->front();
209+
const auto tx_hash = encode_hash(coinbase.hash(false));
210+
const auto request = R"({"id":105,"method":"blockchain.transaction.get_merkle","params":["%1%"]})" "\n";
211+
const auto response = get((boost::format(request) % tx_hash).str());
212+
BOOST_CHECK(response.at("dropped").as_bool());
213+
}
214+
215+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get_merkle__extra_param__dropped)
216+
{
217+
BOOST_CHECK(handshake());
218+
219+
const auto& coinbase = *genesis.transactions_ptr()->front();
220+
const auto tx_hash = encode_hash(coinbase.hash(false));
221+
const auto request = R"({"id":106,"method":"blockchain.transaction.get_merkle","params":["%1%",0,"extra"]})" "\n";
222+
const auto response = get((boost::format(request) % tx_hash).str());
223+
BOOST_CHECK(response.at("dropped").as_bool());
224+
}
225+
154226
// blockchain.transaction.id_from_pos
155227

156228
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__genesis_coinbase_default__expected)

0 commit comments

Comments
 (0)