Skip to content

Commit a5508aa

Browse files
committed
Add electrum transaction.id_from_pos tests.
1 parent 3c618fc commit a5508aa

3 files changed

Lines changed: 127 additions & 14 deletions

File tree

include/bitcoin/server/interfaces/electrum.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct electrum_methods
4646
method<"blockchain.transaction.broadcast", string_t>{ "raw_tx" },
4747
method<"blockchain.transaction.get", string_t, boolean_t>{ "tx_hash", "verbose" },
4848
method<"blockchain.transaction.get_merkle", string_t, number_t>{ "tx_hash", "height" },
49-
method<"blockchain.transaction.id_from_pos", number_t, number_t, boolean_t>{ "height", "tx_pos", "merkle" },
49+
method<"blockchain.transaction.id_from_pos", number_t, number_t, optional<false>>{ "height", "tx_pos", "merkle" },
5050

5151
/// Server methods.
5252
method<"server.add_peer", object_t>{ "features" },

src/protocols/protocol_electrum.cpp

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ void protocol_electrum::blockchain_block_headers(size_t starting,
270270

271271
array_t branch(proof.size());
272272
std::ranges::transform(proof, branch.begin(),
273-
[](const auto& hash) { return encode_hash(hash); });
273+
[](const auto& hash) NOEXCEPT { return encode_hash(hash); });
274274

275275
result["branch"] = std::move(branch);
276276
result["root"] = encode_hash(root);
@@ -526,11 +526,73 @@ void protocol_electrum::handle_blockchain_transaction_get_merkle(const code& ec,
526526
}
527527

528528
void protocol_electrum::handle_blockchain_transaction_id_from_pos(const code& ec,
529-
rpc_interface::blockchain_transaction_id_from_pos, double ,
530-
double , bool ) NOEXCEPT
529+
rpc_interface::blockchain_transaction_id_from_pos, double height,
530+
double tx_pos, bool merkle) NOEXCEPT
531531
{
532-
if (stopped(ec)) return;
533-
send_code(error::not_implemented);
532+
if (stopped(ec))
533+
return;
534+
535+
size_t position{};
536+
size_t block_height{};
537+
if (!to_integer(block_height, height) ||
538+
!to_integer(position, tx_pos))
539+
{
540+
send_code(error::invalid_argument);
541+
return;
542+
}
543+
544+
const auto& query = archive();
545+
const auto block_link = query.to_confirmed(block_height);
546+
const auto tx_link = query.get_position_tx(block_link, position);
547+
if (tx_link.is_terminal())
548+
{
549+
send_code(error::not_found);
550+
return;
551+
}
552+
553+
using namespace system;
554+
const auto hash = query.get_tx_key(tx_link);
555+
if (hash == null_hash)
556+
{
557+
send_code(error::server_error);
558+
return;
559+
}
560+
561+
if (!merkle)
562+
{
563+
send_result(encode_hash(hash), two * hash_size, BIND(complete, _1));
564+
}
565+
else
566+
{
567+
auto hashes = query.get_tx_keys(block_link);
568+
if (hashes.empty())
569+
{
570+
send_code(error::server_error);
571+
return;
572+
}
573+
574+
if (position >= hashes.size())
575+
{
576+
send_code(error::not_found);
577+
return;
578+
}
579+
580+
using namespace chain;
581+
const auto proof = block::merkle_branch(position, std::move(hashes));
582+
583+
array_t branch(proof.size());
584+
std::ranges::transform(proof, branch.begin(),
585+
[](const auto& hash) NOEXCEPT { return encode_hash(hash); });
586+
587+
send_result(
588+
{
589+
object_t
590+
{
591+
{ "tx_hash", encode_hash(hash) },
592+
{ "merkle", std::move(branch) }
593+
}
594+
}, two * hash_size * add1(branch.size()), BIND(complete, _1));
595+
}
534596
}
535597

536598
// Handlers (server).

test/protocols/electrum/electrum_transactions.cpp

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__missing_param__droppe
9898
BOOST_CHECK(handshake());
9999

100100
const auto& coinbase = *genesis.transactions_ptr()->front();
101-
const auto tx_hash = encode_hash(coinbase.hash(false));
101+
const auto tx0_hash = encode_hash(coinbase.hash(false));
102102
const auto request = R"({"id":80,"method":"blockchain.transaction.get","params":["%1%"]})" "\n";
103-
const auto response = get((boost::format(request) % tx_hash).str());
103+
const auto response = get((boost::format(request) % tx0_hash).str());
104104
BOOST_CHECK(response.at("dropped").as_bool());
105105
}
106106

@@ -109,9 +109,9 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__extra_param__dropped)
109109
BOOST_CHECK(handshake());
110110

111111
const auto& coinbase = *genesis.transactions_ptr()->front();
112-
const auto tx_hash = encode_hash(coinbase.hash(false));
112+
const auto tx0_hash = encode_hash(coinbase.hash(false));
113113
const auto request = R"({"id":81,"method":"blockchain.transaction.get","params":["%1%",false,"extra"]})" "\n";
114-
const auto response = get((boost::format(request) % tx_hash).str());
114+
const auto response = get((boost::format(request) % tx0_hash).str());
115115
BOOST_CHECK(response.at("dropped").as_bool());
116116
}
117117

@@ -120,9 +120,9 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verb
120120
BOOST_CHECK(handshake());
121121

122122
const auto& coinbase = *genesis.transactions_ptr()->front();
123-
const auto tx_hash = encode_hash(coinbase.hash(false));
123+
const auto tx0_hash = encode_hash(coinbase.hash(false));
124124
const auto request = R"({"id":82,"method":"blockchain.transaction.get","params":["%1%",false]})" "\n";
125-
const auto response = get((boost::format(request) % tx_hash).str());
125+
const auto response = get((boost::format(request) % tx0_hash).str());
126126
BOOST_CHECK_EQUAL(response.at("result").as_string(), encode_base16(coinbase.to_data(true)));
127127
}
128128

@@ -131,9 +131,9 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verb
131131
BOOST_CHECK(handshake());
132132

133133
const auto& coinbase = *genesis.transactions_ptr()->front();
134-
const auto tx_hash = encode_hash(coinbase.hash(false));
134+
const auto tx0_hash = encode_hash(coinbase.hash(false));
135135
const auto request = R"({"id":83,"method":"blockchain.transaction.get","params":["%1%",true]})" "\n";
136-
const auto response = get((boost::format(request) % tx_hash).str());
136+
const auto response = get((boost::format(request) % tx0_hash).str());
137137

138138
auto expected = value_from(bitcoind(coinbase));
139139
BOOST_CHECK(expected.is_object());
@@ -150,6 +150,57 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_get__genesis_coinbase_verb
150150
}
151151

152152
// blockchain.transaction.get_merkle
153+
153154
// blockchain.transaction.id_from_pos
154155

156+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__genesis_coinbase_default__expected)
157+
{
158+
BOOST_CHECK(handshake());
159+
160+
const auto& coinbase = *genesis.transactions_ptr()->front();
161+
const auto tx0_hash = encode_hash(coinbase.hash(false));
162+
const auto request = R"({"id":90,"method":"blockchain.transaction.id_from_pos","params":[0,0]})" "\n";
163+
const auto response = get(request);
164+
BOOST_CHECK_EQUAL(response.at("result").as_string(), tx0_hash);
165+
}
166+
167+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__coinbase_false__expected)
168+
{
169+
BOOST_CHECK(handshake());
170+
171+
const auto& coinbase = *block2.transactions_ptr()->front();
172+
const auto tx0_hash = encode_hash(coinbase.hash(false));
173+
const auto request = R"({"id":91,"method":"blockchain.transaction.id_from_pos","params":[2,0,false]})" "\n";
174+
const auto response = get(request);
175+
BOOST_CHECK_EQUAL(response.at("result").as_string(), tx0_hash);
176+
}
177+
178+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__merkle_proof_one_tx__empty)
179+
{
180+
BOOST_CHECK(handshake());
181+
182+
const auto& coinbase = *block9.transactions_ptr()->front();
183+
const auto tx0_hash = encode_hash(coinbase.hash(false));
184+
const auto request = R"({"id":92,"method":"blockchain.transaction.id_from_pos","params":[9,0,true]})" "\n";
185+
const auto& object = get(request).at("result").as_object();
186+
BOOST_CHECK_EQUAL(object.at("tx_hash").as_string(), tx0_hash);
187+
BOOST_CHECK(object.at("merkle").as_array().empty());
188+
}
189+
190+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__missing_block__not_found)
191+
{
192+
BOOST_CHECK(handshake());
193+
194+
const auto request = R"({"id":93,"method":"blockchain.transaction.id_from_pos","params":[11,0]})" "\n";
195+
BOOST_CHECK_EQUAL(get(request).at("error").as_object().at("code").as_int64(), not_found.value());
196+
}
197+
198+
BOOST_AUTO_TEST_CASE(electrum__blockchain_transaction_id_from_pos__missing_position__not_found)
199+
{
200+
BOOST_CHECK(handshake());
201+
202+
const auto request = R"({"id":94,"method":"blockchain.transaction.id_from_pos","params":[0,1]})" "\n";
203+
BOOST_CHECK_EQUAL(get(request).at("error").as_object().at("code").as_int64(), not_found.value());
204+
}
205+
155206
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)