bsv-sdk 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/lib/bsv/mcp/tools/broadcast_p2pkh.rb +4 -4
- data/lib/bsv/mcp/tools/check_balance.rb +2 -2
- data/lib/bsv/mcp/tools/fetch_tx.rb +2 -2
- data/lib/bsv/mcp/tools/fetch_utxos.rb +2 -2
- data/lib/bsv/network/protocol.rb +41 -30
- data/lib/bsv/network/protocol_response.rb +86 -0
- data/lib/bsv/network/protocols/arc.rb +48 -111
- data/lib/bsv/network/protocols/ordinals.rb +10 -10
- data/lib/bsv/network/protocols/taal_binary.rb +11 -12
- data/lib/bsv/network/protocols/woc_rest.rb +25 -25
- data/lib/bsv/network/provider.rb +2 -2
- data/lib/bsv/network.rb +1 -6
- data/lib/bsv/transaction/chain_tracker.rb +30 -12
- data/lib/bsv/transaction/chain_trackers/chaintracks.rb +6 -14
- data/lib/bsv/transaction/chain_trackers/whats_on_chain.rb +6 -14
- data/lib/bsv/version.rb +1 -1
- metadata +2 -7
- data/lib/bsv/network/arc.rb +0 -26
- data/lib/bsv/network/broadcast_error.rb +0 -18
- data/lib/bsv/network/broadcast_response.rb +0 -31
- data/lib/bsv/network/chain_provider_error.rb +0 -14
- data/lib/bsv/network/result.rb +0 -119
- data/lib/bsv/network/whats_on_chain.rb +0 -26
|
@@ -19,7 +19,7 @@ module BSV
|
|
|
19
19
|
# auth: { api_key: 'mainnet_your_key_here' }
|
|
20
20
|
# )
|
|
21
21
|
# result = protocol.call(:broadcast, tx)
|
|
22
|
-
# puts result.data[:txid] if result.
|
|
22
|
+
# puts result.data[:txid] if result.http_success?
|
|
23
23
|
#
|
|
24
24
|
# @see https://docs.taal.com TAAL API documentation
|
|
25
25
|
class TAALBinary < BSV::Network::Protocol
|
|
@@ -42,7 +42,7 @@ module BSV
|
|
|
42
42
|
# TAAL-specific headers and applies TAAL-specific response quirk handling.
|
|
43
43
|
#
|
|
44
44
|
# @param tx [#to_binary, String] transaction object or raw binary string
|
|
45
|
-
# @return [
|
|
45
|
+
# @return [ProtocolResponse]
|
|
46
46
|
def call_broadcast(tx)
|
|
47
47
|
body = tx.respond_to?(:to_binary) ? tx.to_binary : tx
|
|
48
48
|
|
|
@@ -57,27 +57,26 @@ module BSV
|
|
|
57
57
|
parse_broadcast_response(response)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
# Maps the HTTP response from TAAL broadcast to a
|
|
61
|
-
# +txn-already-known+ idempotency quirk.
|
|
60
|
+
# Maps the HTTP response from TAAL broadcast to a ProtocolResponse, applying
|
|
61
|
+
# the +txn-already-known+ idempotency quirk.
|
|
62
62
|
#
|
|
63
63
|
# @param response [Net::HTTPResponse]
|
|
64
|
-
# @return [
|
|
64
|
+
# @return [ProtocolResponse]
|
|
65
65
|
def parse_broadcast_response(response)
|
|
66
66
|
code = response.code.to_i
|
|
67
67
|
body = parse_json_body(response.body)
|
|
68
68
|
|
|
69
|
-
# TAAL API boundary: display-order hex txid from the TAAL broadcast response
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
retryable = code == 429 || (500..599).cover?(code)
|
|
69
|
+
# TAAL API boundary: display-order hex txid from the TAAL broadcast response.
|
|
70
|
+
# The HTTP response class is non-2xx here, so we must pass http_success: true explicitly.
|
|
71
|
+
return ProtocolResponse.new(response, data: { txid: body['txid'] }, http_success: true) if already_known?(body) && body['txid']
|
|
73
72
|
|
|
74
73
|
if (200..299).cover?(code)
|
|
75
|
-
return
|
|
74
|
+
return ProtocolResponse.new(response, http_success: false, error_message: 'TAAL returned a malformed 2xx response') unless body['txid']
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
ProtocolResponse.new(response, data: { txid: body['txid'] })
|
|
78
77
|
else
|
|
79
78
|
message = (body.is_a?(Hash) && body['error']) || "HTTP #{code}"
|
|
80
|
-
|
|
79
|
+
ProtocolResponse.new(response, http_success: false, error_message: message)
|
|
81
80
|
end
|
|
82
81
|
end
|
|
83
82
|
|
|
@@ -21,7 +21,7 @@ module BSV
|
|
|
21
21
|
#
|
|
22
22
|
# woc = BSV::Network::Protocols::WoCREST.new(network: :main)
|
|
23
23
|
# result = woc.call(:get_tx, 'abc123...')
|
|
24
|
-
# puts result.data if result.
|
|
24
|
+
# puts result.data if result.http_success?
|
|
25
25
|
#
|
|
26
26
|
# @see https://developers.whatsonchain.com/ WhatsOnChain API documentation
|
|
27
27
|
class WoCREST < Protocol
|
|
@@ -169,18 +169,18 @@ module BSV
|
|
|
169
169
|
# @param txid [String] WoC API boundary: display-order hex transaction ID
|
|
170
170
|
# @param vout [Integer] output index
|
|
171
171
|
# @param script_hash [String, nil] ignored
|
|
172
|
-
# @return [
|
|
172
|
+
# @return [ProtocolResponse<Boolean>]
|
|
173
173
|
def call_is_utxo(txid, vout, script_hash: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
174
174
|
result = default_call(:is_utxo, txid, vout)
|
|
175
175
|
|
|
176
176
|
# 404 = no spending tx found = output is unspent
|
|
177
|
-
return
|
|
177
|
+
return result.with(data: true, http_success: true, error_message: nil) if result.http_not_found?
|
|
178
178
|
|
|
179
179
|
# Non-success, non-404 = genuine error
|
|
180
|
-
return result unless result.
|
|
180
|
+
return result unless result.http_success?
|
|
181
181
|
|
|
182
182
|
# 200 with spending tx details = output is spent
|
|
183
|
-
|
|
183
|
+
result.with(data: false)
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
# Bulk-checks whether a set of outputs are unspent.
|
|
@@ -192,15 +192,15 @@ module BSV
|
|
|
192
192
|
# Entries absent from the response (unknown outputs) are treated as spent.
|
|
193
193
|
#
|
|
194
194
|
# @param outpoints [Array<Hash>] array of +{ txid:, vout: }+ hashes
|
|
195
|
-
# @return [
|
|
195
|
+
# @return [ProtocolResponse<Hash{String => Boolean}>]
|
|
196
196
|
# On success, data is a hash mapping +"txid.vout"+ keys to booleans
|
|
197
197
|
# (+true+ = unspent, +false+ = spent).
|
|
198
198
|
def call_is_utxo_bulk(outpoints)
|
|
199
|
-
return
|
|
199
|
+
return ProtocolResponse.new(nil, data: {}, http_success: true) if outpoints.empty?
|
|
200
200
|
|
|
201
201
|
body = JSON.generate(utxos: outpoints.map { |op| { 'txid' => op[:txid].to_s, 'vout' => op[:vout].to_i } })
|
|
202
202
|
result = default_call(:is_utxo_bulk, body: body)
|
|
203
|
-
return result unless result.
|
|
203
|
+
return result unless result.http_success?
|
|
204
204
|
|
|
205
205
|
# Build a lookup from the response entries
|
|
206
206
|
# spentIn present and non-empty → spent (false); absent or empty → unspent (true)
|
|
@@ -219,7 +219,7 @@ module BSV
|
|
|
219
219
|
normalised[key] = false unless normalised.key?(key)
|
|
220
220
|
end
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
result.with(data: normalised)
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
# Broadcasts a raw transaction to WhatsOnChain.
|
|
@@ -229,17 +229,17 @@ module BSV
|
|
|
229
229
|
# stripped and wrapped in a Hash for caller convenience.
|
|
230
230
|
#
|
|
231
231
|
# @param tx [#to_hex, String] transaction object or raw hex string
|
|
232
|
-
# @return [
|
|
232
|
+
# @return [ProtocolResponse<{ txid: String }>]
|
|
233
233
|
def call_broadcast(tx)
|
|
234
234
|
hex = tx.respond_to?(:to_hex) ? tx.to_hex : tx.to_s
|
|
235
235
|
body = JSON.generate(txhex: hex)
|
|
236
236
|
|
|
237
237
|
result = default_call(:broadcast, body: body)
|
|
238
|
-
return result unless result.
|
|
238
|
+
return result unless result.http_success?
|
|
239
239
|
|
|
240
240
|
# WoC returns plain-text txid — result.data is the raw body string
|
|
241
241
|
# WoC API boundary: display-order hex txid returned as plain text
|
|
242
|
-
|
|
242
|
+
result.with(data: { txid: result.data.to_s.strip })
|
|
243
243
|
end
|
|
244
244
|
|
|
245
245
|
# Decodes a raw transaction hex by posting to the WoC decode endpoint.
|
|
@@ -248,7 +248,7 @@ module BSV
|
|
|
248
248
|
# full decoded transaction object.
|
|
249
249
|
#
|
|
250
250
|
# @param txhex [String] raw transaction hex string
|
|
251
|
-
# @return [
|
|
251
|
+
# @return [ProtocolResponse]
|
|
252
252
|
def call_decode_tx(txhex)
|
|
253
253
|
body = JSON.generate(txhex: txhex.to_s)
|
|
254
254
|
default_call(:decode_tx, body: body)
|
|
@@ -259,7 +259,7 @@ module BSV
|
|
|
259
259
|
# WoC expects +{ "txids": [...] }+ as the request body.
|
|
260
260
|
#
|
|
261
261
|
# @param txids [Array<String>] list of transaction IDs
|
|
262
|
-
# @return [
|
|
262
|
+
# @return [ProtocolResponse]
|
|
263
263
|
def call_get_tx_hex_bulk(txids)
|
|
264
264
|
body = JSON.generate(txids: txids)
|
|
265
265
|
default_call(:get_tx_hex_bulk, body: body)
|
|
@@ -270,7 +270,7 @@ module BSV
|
|
|
270
270
|
# WoC expects +{ "scripts": [...] }+ as the request body.
|
|
271
271
|
#
|
|
272
272
|
# @param script_hashes [Array<String>] list of script hashes
|
|
273
|
-
# @return [
|
|
273
|
+
# @return [ProtocolResponse]
|
|
274
274
|
def call_get_script_unspent_bulk(script_hashes)
|
|
275
275
|
body = JSON.generate(scripts: script_hashes)
|
|
276
276
|
default_call(:get_script_unspent_bulk, body: body)
|
|
@@ -281,7 +281,7 @@ module BSV
|
|
|
281
281
|
# WoC expects +{ "txids": [...] }+ as the request body.
|
|
282
282
|
#
|
|
283
283
|
# @param txids [Array<String>] list of transaction IDs
|
|
284
|
-
# @return [
|
|
284
|
+
# @return [ProtocolResponse]
|
|
285
285
|
def call_get_bulk_tx_details(txids)
|
|
286
286
|
body = JSON.generate(txids: txids)
|
|
287
287
|
default_call(:get_bulk_tx_details, body: body)
|
|
@@ -292,7 +292,7 @@ module BSV
|
|
|
292
292
|
# WoC expects +{ "txids": [{ "txid": "...", "vouts": [0, 1] }, ...] }+ as the request body.
|
|
293
293
|
#
|
|
294
294
|
# @param tx_vouts [Array<Hash>] array of +{ txid:, vouts: [Integer] }+ hashes
|
|
295
|
-
# @return [
|
|
295
|
+
# @return [ProtocolResponse]
|
|
296
296
|
def call_get_bulk_output_scripts(tx_vouts)
|
|
297
297
|
body = JSON.generate(txids: tx_vouts.map { |tv| { 'txid' => tv[:txid].to_s, 'vouts' => tv[:vouts] } })
|
|
298
298
|
default_call(:get_bulk_output_scripts, body: body)
|
|
@@ -303,7 +303,7 @@ module BSV
|
|
|
303
303
|
# WoC expects +{ "addresses": [...] }+ as the request body.
|
|
304
304
|
#
|
|
305
305
|
# @param addresses [Array<String>] list of addresses
|
|
306
|
-
# @return [
|
|
306
|
+
# @return [ProtocolResponse]
|
|
307
307
|
def call_get_bulk_address_utxos(addresses)
|
|
308
308
|
body = JSON.generate(addresses: addresses)
|
|
309
309
|
default_call(:get_bulk_address_utxos, body: body)
|
|
@@ -314,7 +314,7 @@ module BSV
|
|
|
314
314
|
# WoC expects +{ "addresses": [...] }+ as the request body.
|
|
315
315
|
#
|
|
316
316
|
# @param addresses [Array<String>] list of addresses
|
|
317
|
-
# @return [
|
|
317
|
+
# @return [ProtocolResponse]
|
|
318
318
|
def call_get_bulk_address_unconfirmed_utxos(addresses)
|
|
319
319
|
body = JSON.generate(addresses: addresses)
|
|
320
320
|
default_call(:get_bulk_address_unconfirmed_utxos, body: body)
|
|
@@ -325,7 +325,7 @@ module BSV
|
|
|
325
325
|
# WoC expects +{ "scripts": [...] }+ as the request body.
|
|
326
326
|
#
|
|
327
327
|
# @param script_hashes [Array<String>] list of script hashes
|
|
328
|
-
# @return [
|
|
328
|
+
# @return [ProtocolResponse]
|
|
329
329
|
def call_get_bulk_script_unconfirmed_unspent(script_hashes)
|
|
330
330
|
body = JSON.generate(scripts: script_hashes)
|
|
331
331
|
default_call(:get_bulk_script_unconfirmed_unspent, body: body)
|
|
@@ -336,7 +336,7 @@ module BSV
|
|
|
336
336
|
# WoC expects +{ "query": "..." }+ as the request body.
|
|
337
337
|
#
|
|
338
338
|
# @param query [String] search term
|
|
339
|
-
# @return [
|
|
339
|
+
# @return [ProtocolResponse]
|
|
340
340
|
def call_search_links(query)
|
|
341
341
|
body = JSON.generate(query: query)
|
|
342
342
|
default_call(:search_links, body: body)
|
|
@@ -351,7 +351,7 @@ module BSV
|
|
|
351
351
|
#
|
|
352
352
|
# @param txids [Array<String>, nil] list of transaction IDs (positional)
|
|
353
353
|
# @param body [String, nil] pre-serialised request body (keyword)
|
|
354
|
-
# @return [
|
|
354
|
+
# @return [ProtocolResponse]
|
|
355
355
|
# @raise [ArgumentError] when neither txids nor body is provided
|
|
356
356
|
def call_get_tx_status(txids = nil, body: nil)
|
|
357
357
|
raise ArgumentError, 'provide txids array or body: keyword' if txids.nil? && body.nil?
|
|
@@ -368,13 +368,13 @@ module BSV
|
|
|
368
368
|
#
|
|
369
369
|
# @param root [String] expected merkle root as hex
|
|
370
370
|
# @param height [Integer] block height
|
|
371
|
-
# @return [
|
|
371
|
+
# @return [ProtocolResponse<Boolean>]
|
|
372
372
|
def call_valid_root(root, height)
|
|
373
373
|
result = default_call(:valid_root, height)
|
|
374
|
-
return result unless result.
|
|
374
|
+
return result unless result.http_success?
|
|
375
375
|
|
|
376
376
|
actual = result.data['merkleroot']
|
|
377
|
-
|
|
377
|
+
result.with(data: actual.to_s.downcase == root.to_s.downcase)
|
|
378
378
|
end
|
|
379
379
|
end
|
|
380
380
|
end
|
data/lib/bsv/network/provider.rb
CHANGED
|
@@ -20,7 +20,7 @@ module BSV
|
|
|
20
20
|
# end
|
|
21
21
|
#
|
|
22
22
|
# result = gorillapool.call(:broadcast, tx)
|
|
23
|
-
# result.
|
|
23
|
+
# result.http_success? # => true
|
|
24
24
|
class Provider
|
|
25
25
|
attr_reader :name, :auth, :rate_limit
|
|
26
26
|
|
|
@@ -124,7 +124,7 @@ module BSV
|
|
|
124
124
|
# @param command_name [Symbol, String] command to invoke
|
|
125
125
|
# @param args [Array] positional arguments forwarded to the protocol
|
|
126
126
|
# @param kwargs [Hash] keyword arguments forwarded to the protocol
|
|
127
|
-
# @return [
|
|
127
|
+
# @return [ProtocolResponse]
|
|
128
128
|
# @raise [ArgumentError] when no registered protocol serves the command
|
|
129
129
|
def call(command_name, *args, **kwargs)
|
|
130
130
|
sym = command_name.to_sym
|
data/lib/bsv/network.rb
CHANGED
|
@@ -2,16 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module BSV
|
|
4
4
|
module Network
|
|
5
|
-
autoload :
|
|
5
|
+
autoload :ProtocolResponse, 'bsv/network/protocol_response'
|
|
6
6
|
autoload :Protocol, 'bsv/network/protocol'
|
|
7
7
|
autoload :Protocols, 'bsv/network/protocols'
|
|
8
8
|
autoload :Providers, 'bsv/network/providers'
|
|
9
9
|
autoload :Provider, 'bsv/network/provider'
|
|
10
|
-
autoload :BroadcastError, 'bsv/network/broadcast_error'
|
|
11
|
-
autoload :BroadcastResponse, 'bsv/network/broadcast_response'
|
|
12
|
-
autoload :ChainProviderError, 'bsv/network/chain_provider_error'
|
|
13
10
|
autoload :UTXO, 'bsv/network/utxo'
|
|
14
|
-
autoload :ARC, 'bsv/network/arc'
|
|
15
|
-
autoload :WhatsOnChain, 'bsv/network/whats_on_chain'
|
|
16
11
|
end
|
|
17
12
|
end
|
|
@@ -2,22 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module BSV
|
|
4
4
|
module Transaction
|
|
5
|
-
#
|
|
5
|
+
# Duck type for block header lookups used by the SDK's verify methods.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# {Beef#verify}, {MerklePath#verify}, and {Transaction#verify} define
|
|
8
|
+
# what a valid structure *is* — they walk trees, check proofs, and
|
|
9
|
+
# compare roots. But they have no data source of their own. The
|
|
10
|
+
# chain tracker is the data source: an object the consumer provides
|
|
11
|
+
# that can answer "is this merkle root valid for this block height?"
|
|
10
12
|
#
|
|
11
|
-
#
|
|
13
|
+
# The SDK is deliberately unopinionated about where that answer comes
|
|
14
|
+
# from. A chain tracker backed by an in-memory hash is declarative.
|
|
15
|
+
# One that fetches from the network on cache miss and writes to a
|
|
16
|
+
# database is imperative. The verify methods don't care — they just
|
|
17
|
+
# ask the question. All imperative behaviour (fetching, caching,
|
|
18
|
+
# persisting) lives in the consumer's chain tracker implementation,
|
|
19
|
+
# not in the SDK.
|
|
12
20
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# end
|
|
21
|
+
# Any object responding to +valid_root_for_height?+ and
|
|
22
|
+
# +current_height+ satisfies this interface. Inheriting from this
|
|
23
|
+
# class is optional — it exists to document the contract and provide
|
|
24
|
+
# clear error messages when methods are missing.
|
|
18
25
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
26
|
+
# @example In-memory chain tracker (test / declarative)
|
|
27
|
+
# class HashTracker < BSV::Transaction::ChainTracker
|
|
28
|
+
# def initialize(headers) @headers = headers end
|
|
29
|
+
# def valid_root_for_height?(root, h) @headers[h] == root end
|
|
30
|
+
# def current_height() @headers.keys.max end
|
|
31
|
+
# end
|
|
32
|
+
# tracker = HashTracker.new(800_000 => 'abcd...')
|
|
33
|
+
#
|
|
34
|
+
# @example Cache-aware chain tracker (production / imperative)
|
|
35
|
+
# class WalletChainTracker < BSV::Transaction::ChainTracker
|
|
36
|
+
# def valid_root_for_height?(root, height)
|
|
37
|
+
# header = @db.find_header(height) || fetch_and_store(height)
|
|
38
|
+
# header.merkle_root == root
|
|
21
39
|
# end
|
|
22
40
|
# end
|
|
23
41
|
class ChainTracker
|
|
@@ -54,17 +54,12 @@ module BSV
|
|
|
54
54
|
# @param root [String] merkle root as a hex string
|
|
55
55
|
# @param height [Integer] block height
|
|
56
56
|
# @return [Boolean]
|
|
57
|
-
# @raise [
|
|
57
|
+
# @raise [StandardError] on network or API error
|
|
58
58
|
def valid_root_for_height?(root, height)
|
|
59
59
|
result = @protocol.call(:get_block_header, height)
|
|
60
|
-
return false if result.
|
|
60
|
+
return false if result.http_not_found?
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
raise BSV::Network::ChainProviderError.new(
|
|
64
|
-
result.message.to_s,
|
|
65
|
-
status_code: result.metadata[:status_code]
|
|
66
|
-
)
|
|
67
|
-
end
|
|
62
|
+
raise result.message.to_s unless result.http_success?
|
|
68
63
|
|
|
69
64
|
merkle_root = result.data['merkleRoot']
|
|
70
65
|
return false unless merkle_root
|
|
@@ -75,15 +70,12 @@ module BSV
|
|
|
75
70
|
# Return the current blockchain height.
|
|
76
71
|
#
|
|
77
72
|
# @return [Integer]
|
|
78
|
-
# @raise [
|
|
73
|
+
# @raise [StandardError] on network or API error
|
|
79
74
|
def current_height
|
|
80
75
|
result = @protocol.call(:current_height)
|
|
81
|
-
return result.data if result.
|
|
76
|
+
return result.data if result.http_success?
|
|
82
77
|
|
|
83
|
-
raise
|
|
84
|
-
result.message.to_s,
|
|
85
|
-
status_code: result.metadata[:status_code]
|
|
86
|
-
)
|
|
78
|
+
raise result.message.to_s
|
|
87
79
|
end
|
|
88
80
|
end
|
|
89
81
|
end
|
|
@@ -47,17 +47,12 @@ module BSV
|
|
|
47
47
|
# @param root [String] merkle root as a hex string
|
|
48
48
|
# @param height [Integer] block height
|
|
49
49
|
# @return [Boolean]
|
|
50
|
-
# @raise [
|
|
50
|
+
# @raise [StandardError] on network or API error
|
|
51
51
|
def valid_root_for_height?(root, height)
|
|
52
52
|
result = @protocol.call(:valid_root, root, height)
|
|
53
|
-
return false if result.
|
|
53
|
+
return false if result.http_not_found?
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
raise BSV::Network::ChainProviderError.new(
|
|
57
|
-
result.message.to_s,
|
|
58
|
-
status_code: result.metadata[:status_code]
|
|
59
|
-
)
|
|
60
|
-
end
|
|
55
|
+
raise result.message.to_s unless result.http_success?
|
|
61
56
|
|
|
62
57
|
result.data == true
|
|
63
58
|
end
|
|
@@ -65,15 +60,12 @@ module BSV
|
|
|
65
60
|
# Return the current blockchain height.
|
|
66
61
|
#
|
|
67
62
|
# @return [Integer]
|
|
68
|
-
# @raise [
|
|
63
|
+
# @raise [StandardError] on network or API error
|
|
69
64
|
def current_height
|
|
70
65
|
result = @protocol.call(:current_height)
|
|
71
|
-
return result.data if result.
|
|
66
|
+
return result.data if result.http_success?
|
|
72
67
|
|
|
73
|
-
raise
|
|
74
|
-
result.message.to_s,
|
|
75
|
-
status_code: result.metadata[:status_code]
|
|
76
|
-
)
|
|
68
|
+
raise result.message.to_s
|
|
77
69
|
end
|
|
78
70
|
|
|
79
71
|
# Wraps an injectable HTTP client to set a raw Authorization header value
|
data/lib/bsv/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bettison
|
|
@@ -96,11 +96,8 @@ files:
|
|
|
96
96
|
- lib/bsv/mcp/tools/generate_key.rb
|
|
97
97
|
- lib/bsv/mcp/tools/helpers.rb
|
|
98
98
|
- lib/bsv/network.rb
|
|
99
|
-
- lib/bsv/network/arc.rb
|
|
100
|
-
- lib/bsv/network/broadcast_error.rb
|
|
101
|
-
- lib/bsv/network/broadcast_response.rb
|
|
102
|
-
- lib/bsv/network/chain_provider_error.rb
|
|
103
99
|
- lib/bsv/network/protocol.rb
|
|
100
|
+
- lib/bsv/network/protocol_response.rb
|
|
104
101
|
- lib/bsv/network/protocols.rb
|
|
105
102
|
- lib/bsv/network/protocols/arc.rb
|
|
106
103
|
- lib/bsv/network/protocols/chaintracks.rb
|
|
@@ -113,9 +110,7 @@ files:
|
|
|
113
110
|
- lib/bsv/network/providers/gorilla_pool.rb
|
|
114
111
|
- lib/bsv/network/providers/taal.rb
|
|
115
112
|
- lib/bsv/network/providers/whats_on_chain.rb
|
|
116
|
-
- lib/bsv/network/result.rb
|
|
117
113
|
- lib/bsv/network/utxo.rb
|
|
118
|
-
- lib/bsv/network/whats_on_chain.rb
|
|
119
114
|
- lib/bsv/overlay.rb
|
|
120
115
|
- lib/bsv/overlay/admin_token_template.rb
|
|
121
116
|
- lib/bsv/overlay/broadcast_facilitator.rb
|
data/lib/bsv/network/arc.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Network
|
|
5
|
-
# @deprecated Use {BSV::Network::Protocols::ARC} directly instead.
|
|
6
|
-
# The facade converted clean Result objects into exceptions — every
|
|
7
|
-
# consumer immediately caught them and converted back to data.
|
|
8
|
-
# Use the protocol layer, which returns Result objects natively.
|
|
9
|
-
class ARC
|
|
10
|
-
# Raised when deprecated facade classes are instantiated.
|
|
11
|
-
class DeprecationError < StandardError; end
|
|
12
|
-
|
|
13
|
-
MESSAGE = 'BSV::Network::ARC is deprecated. ' \
|
|
14
|
-
'Use BSV::Network::Protocols::ARC directly — it returns Result objects ' \
|
|
15
|
-
'instead of raising exceptions. See BSV::Network::Protocols::ARC for usage.'
|
|
16
|
-
|
|
17
|
-
def self.default(**)
|
|
18
|
-
raise DeprecationError, MESSAGE
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def initialize(*)
|
|
22
|
-
raise DeprecationError, MESSAGE
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Network
|
|
5
|
-
class BroadcastError < StandardError
|
|
6
|
-
# ARC API boundary: display-order hex txid as returned by the ARC error response.
|
|
7
|
-
attr_reader :status_code, :txid, :arc_status
|
|
8
|
-
|
|
9
|
-
def initialize(message, status_code: nil, txid: nil, arc_status: nil)
|
|
10
|
-
@status_code = status_code
|
|
11
|
-
BSV::Primitives::Hex.validate_dtxid_hex!(txid, name: 'ARC error txid') if txid
|
|
12
|
-
@txid = txid
|
|
13
|
-
@arc_status = arc_status
|
|
14
|
-
super(message)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Network
|
|
5
|
-
class BroadcastResponse
|
|
6
|
-
# ARC API boundary: display-order hex txid as returned by the ARC broadcast endpoint.
|
|
7
|
-
attr_reader :txid, :tx_status, :message, :extra_info, :block_hash, :block_height, :timestamp, :competing_txs
|
|
8
|
-
|
|
9
|
-
def initialize(attrs = {})
|
|
10
|
-
txid = attrs[:txid]
|
|
11
|
-
BSV::Primitives::Hex.validate_dtxid_hex!(txid, name: 'ARC broadcast txid') if txid
|
|
12
|
-
@txid = txid
|
|
13
|
-
@tx_status = attrs[:tx_status]
|
|
14
|
-
@message = attrs[:message]
|
|
15
|
-
@extra_info = attrs[:extra_info]
|
|
16
|
-
@block_hash = attrs[:block_hash]
|
|
17
|
-
@block_height = attrs[:block_height]
|
|
18
|
-
@timestamp = attrs[:timestamp]
|
|
19
|
-
@competing_txs = attrs[:competing_txs]
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def success?
|
|
23
|
-
true
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def mined?
|
|
27
|
-
tx_status == 'MINED'
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
data/lib/bsv/network/result.rb
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module BSV
|
|
4
|
-
module Network
|
|
5
|
-
# Result module provides three immutable value types for Protocol dispatch outcomes.
|
|
6
|
-
#
|
|
7
|
-
# All three types share a Predicates mixin with default false implementations.
|
|
8
|
-
# Each type overrides only the predicate that returns true for that type.
|
|
9
|
-
module Result
|
|
10
|
-
# Mixin providing default false implementations for all query predicates.
|
|
11
|
-
module Predicates
|
|
12
|
-
def success?
|
|
13
|
-
false
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def error?
|
|
17
|
-
false
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def not_found?
|
|
21
|
-
false
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Represents a successful outcome. Carries the response payload in +data+
|
|
26
|
-
# and optional protocol-specific extras in +metadata+.
|
|
27
|
-
class Success
|
|
28
|
-
include Predicates
|
|
29
|
-
|
|
30
|
-
attr_reader :data, :metadata
|
|
31
|
-
|
|
32
|
-
def initialize(data:, metadata: {})
|
|
33
|
-
@data = data
|
|
34
|
-
@metadata = metadata.freeze
|
|
35
|
-
freeze
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def success?
|
|
39
|
-
true
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def ==(other)
|
|
43
|
-
other.is_a?(Success) && data == other.data && metadata == other.metadata
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
alias eql? ==
|
|
47
|
-
|
|
48
|
-
def hash
|
|
49
|
-
[self.class, data, metadata].hash
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Represents a failed outcome. Carries a human-readable +message+, a boolean
|
|
54
|
-
# +retryable+ flag indicating whether the caller should retry, and optional
|
|
55
|
-
# +metadata+ for structured protocol-specific details (e.g. +arc_status+).
|
|
56
|
-
class Error
|
|
57
|
-
include Predicates
|
|
58
|
-
|
|
59
|
-
attr_reader :message, :retryable, :metadata
|
|
60
|
-
|
|
61
|
-
def initialize(message:, retryable: false, metadata: {})
|
|
62
|
-
@message = message
|
|
63
|
-
@retryable = retryable
|
|
64
|
-
@metadata = metadata.freeze
|
|
65
|
-
freeze
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def error?
|
|
69
|
-
true
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def retryable?
|
|
73
|
-
@retryable
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def ==(other)
|
|
77
|
-
other.is_a?(Error) &&
|
|
78
|
-
message == other.message &&
|
|
79
|
-
retryable == other.retryable &&
|
|
80
|
-
metadata == other.metadata
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
alias eql? ==
|
|
84
|
-
|
|
85
|
-
def hash
|
|
86
|
-
[self.class, message, retryable, metadata].hash
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Represents a resource-not-found outcome. Carries an optional human-readable
|
|
91
|
-
# +message+ and optional +metadata+.
|
|
92
|
-
class NotFound
|
|
93
|
-
include Predicates
|
|
94
|
-
|
|
95
|
-
attr_reader :message, :metadata
|
|
96
|
-
|
|
97
|
-
def initialize(message: nil, metadata: {})
|
|
98
|
-
@message = message
|
|
99
|
-
@metadata = metadata.freeze
|
|
100
|
-
freeze
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def not_found?
|
|
104
|
-
true
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def ==(other)
|
|
108
|
-
other.is_a?(NotFound) && message == other.message && metadata == other.metadata
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
alias eql? ==
|
|
112
|
-
|
|
113
|
-
def hash
|
|
114
|
-
[self.class, message, metadata].hash
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|