1818 */
1919#include < bitcoin/server/protocols/protocol_electrum.hpp>
2020
21+ #include < utility>
2122#include < bitcoin/server/define.hpp>
2223
2324namespace libbitcoin {
@@ -29,6 +30,8 @@ using namespace system;
2930using namespace network ::rpc;
3031using namespace std ::placeholders;
3132
33+ BC_PUSH_WARNING (NO_THROW_IN_NOEXCEPT)
34+
3235void protocol_electrum::handle_blockchain_utxo_get_address (const code& ec,
3336 rpc_interface::blockchain_utxo_get_address, const std::string& tx_hash,
3437 double index) NOEXCEPT
@@ -91,48 +94,32 @@ void protocol_electrum::handle_blockchain_outpoint_get_status(const code& ec,
9194 return ;
9295 }
9396
94- uint32_t index{};
95- hash_digest hash{};
96- if (!to_integer (index, txout_idx) ||
97- !decode_hash (hash, tx_hash))
98- {
99- send_code (error::invalid_argument);
97+ send_get_status (tx_hash, txout_idx, spk_hint);
98+ }
99+
100+ void protocol_electrum::handle_blockchain_outpoint_subscribe (const code& ec,
101+ rpc_interface::blockchain_outpoint_subscribe, const std::string& tx_hash,
102+ double txout_idx, const std::string& spk_hint) NOEXCEPT
103+ {
104+ if (stopped (ec))
100105 return ;
101- }
102106
103- // script is advisory, and should match prevout script.
104- if (!spk_hint.empty ())
107+ if (!at_least (electrum::version::v1_7))
105108 {
106- data_chunk bytes{};
107- if (!decode_base16 (bytes, spk_hint))
108- {
109- send_code (error::invalid_argument);
110- return ;
111- }
112-
113- chain::script script{ std::move (bytes), false };
114- if (!script.is_valid ())
115- {
116- send_code (error::invalid_argument);
117- return ;
118- }
109+ send_code (error::wrong_version);
110+ return ;
119111 }
120112
121- // TODO: implement outpoint status query.
122- chain::point prevout{ hash, index };
123- send_result (object_t
124- {
125- { " height" , 24 },
126- { " spender_txhash" , " <hash>" },
127- { " spender_height" , 42 }
113+ if (!send_get_status (tx_hash, txout_idx, spk_hint))
114+ return ;
128115
129- }, 16 , BIND (complete, _1));
116+ // TODO: collect the outpoint into a limited notification set.
117+ subscribed_outpoint_.store (false , std::memory_order_relaxed);
130118}
131119
132- // TODO: implement.
133- void protocol_electrum::handle_blockchain_outpoint_subscribe (const code& ec,
134- rpc_interface::blockchain_outpoint_subscribe, const std::string& tx_hash,
135- double txout_idx, const std::string& spk_hint) NOEXCEPT
120+ void protocol_electrum::handle_blockchain_outpoint_unsubscribe (const code& ec,
121+ rpc_interface::blockchain_outpoint_unsubscribe, const std::string& tx_hash,
122+ double txout_idx) NOEXCEPT
136123{
137124 if (stopped (ec))
138125 return ;
@@ -152,67 +139,87 @@ void protocol_electrum::handle_blockchain_outpoint_subscribe(const code& ec,
152139 return ;
153140 }
154141
155- // script is advisory, but should match prevout script.
142+ // TODO: remove outpoint subscription from notification set.
143+ chain::point prevout{ hash, index };
144+ const auto previous = subscribed_scriptpubkey_.load (
145+ std::memory_order_relaxed);
146+
147+ send_result (previous, 16 , BIND (complete, _1));
148+ }
149+
150+ // utility.
151+ // ----------------------------------------------------------------------------
152+
153+ bool protocol_electrum::send_get_status (const std::string& tx_hash,
154+ double txout_idx, const std::string& spk_hint) NOEXCEPT
155+ {
156+ uint32_t index{};
157+ hash_digest hash{};
158+ if (!to_integer (index, txout_idx) || !decode_hash (hash, tx_hash))
159+ {
160+ send_code (error::invalid_argument);
161+ return false ;
162+ }
163+
164+ // This is parsed for correctness but is not used.
165+ // Script is advisory, and should match output script.
156166 if (!spk_hint.empty ())
157167 {
158168 data_chunk bytes{};
159169 if (!decode_base16 (bytes, spk_hint))
160170 {
161171 send_code (error::invalid_argument);
162- return ;
172+ return false ;
163173 }
164174
165175 chain::script script{ std::move (bytes), false };
166176 if (!script.is_valid ())
167177 {
168178 send_code (error::invalid_argument);
169- return ;
179+ return false ;
170180 }
171181 }
172182
173- // TODO: collect the outpoint into a limited notification set.
174- subscribed_outpoint_.store (false , std::memory_order_relaxed);
175-
176- // TODO: implement outpoint status query.
177- chain::point prevout{ hash, index };
178- send_result (object_t
183+ const auto & query = archive ();
184+ const auto tx = query.to_tx (hash);
185+ const auto output = query.to_output (tx, index);
186+ if (output.is_terminal ())
179187 {
180- { " height" , 24 },
181- { " spender_txhash" , " <hash>" },
182- { " spender_height" , 42 }
183-
184- }, 16 , BIND (complete, _1));
185- }
186-
187- void protocol_electrum::handle_blockchain_outpoint_unsubscribe (const code& ec,
188- rpc_interface::blockchain_outpoint_unsubscribe, const std::string& tx_hash,
189- double txout_idx) NOEXCEPT
190- {
191- if (stopped (ec))
192- return ;
188+ send_code (error::not_found);
189+ return false ;
190+ }
193191
194- if (!at_least (electrum::version::v1_7))
192+ // TODO: database query.///////////////////////////////////////////////////
193+ size_t height{ database::history::rooted_height };
194+ if (const auto block = query.find_confirmed_block (tx); block.is_terminal ())
195195 {
196- send_code (error::wrong_version);
197- return ;
196+ if (!query. is_confirmed_all_prevouts (tx))
197+ height = database::history::unrooted_height ;
198198 }
199-
200- uint32_t index{};
201- hash_digest hash{};
202- if (!to_integer (index, txout_idx) ||
203- !decode_hash (hash, tx_hash))
199+ else if (!query.get_height (height, block))
204200 {
205- send_code (error::invalid_argument );
206- return ;
201+ send_code (error::server_error );
202+ return false ;
207203 }
204+ // /////////////////////////////////////////////////////////////////////////
208205
209- // TODO: remove outpoint subscription from notification set.
210- chain::point prevout{ hash, index };
211- const auto previous = subscribed_scriptpubkey_.load (
212- std::memory_order_relaxed);
206+ // TODO: query tx spenders sorted history./////////////////////////////////
207+ const database::histories spenders{};
208+ // /////////////////////////////////////////////////////////////////////////
213209
214- send_result (previous, 16 , BIND (complete, _1));
210+ auto result = object_t { { " height" , to_unsigned (height) } };
211+ if (!spenders.empty ())
212+ {
213+ const auto & spender = spenders.front ().tx ;
214+ result[" spender_txhash" ] = encode_hash (spender.hash ());
215+ result[" spender_height" ] = to_unsigned (spender.height ());
216+ }
217+
218+ send_result (std::move (result), 128 , BIND (complete, _1));
219+ return true ;
215220}
216221
222+ BC_POP_WARNING ()
223+
217224} // namespace server
218225} // namespace libbitcoin
0 commit comments