-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathwhats_on_chain.rb
More file actions
95 lines (82 loc) · 3.02 KB
/
whats_on_chain.rb
File metadata and controls
95 lines (82 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# frozen_string_literal: true
require 'net/http'
require 'json'
require 'uri'
module BSV
module Transaction
module ChainTrackers
# Chain tracker that verifies merkle roots using the WhatsOnChain API.
#
# Queries the WoC block header endpoint to retrieve the merkle root for a
# given block height and compares it with the provided root.
#
# @example
# tracker = BSV::Transaction::ChainTrackers::WhatsOnChain.new
# tracker.valid_root_for_height?('abcd...', 800_000)
class WhatsOnChain < ChainTracker
BASE_URL = 'https://api.whatsonchain.com'
NETWORKS = {
main: 'main',
mainnet: 'main',
test: 'test',
testnet: 'test',
stn: 'stn'
}.freeze
# @param network [Symbol] :main, :mainnet, :test, :testnet, or :stn
# @param api_key [String, nil] optional WoC API key
# @param http_client [#request, nil] injectable HTTP client for testing
def initialize(network: :main, api_key: nil, http_client: nil)
super()
@network = NETWORKS.fetch(network) { raise ArgumentError, "unknown network: #{network}" }
@api_key = api_key
@http_client = http_client
end
# Verify that a merkle root is valid for the given block height.
#
# @param root [String] merkle root as a hex string
# @param height [Integer] block height
# @return [Boolean]
def valid_root_for_height?(root, height)
response = get("/v1/bsv/#{@network}/block/#{height}/header")
return false if response.nil?
data = JSON.parse(response.body)
data['merkleroot'].downcase == root.downcase
end
# Return the current blockchain height.
#
# @return [Integer]
def current_height
response = get("/v1/bsv/#{@network}/chain/info", not_found_returns_nil: false)
data = JSON.parse(response.body)
data['blocks']
end
private
# @param path [String] API path
# @param not_found_returns_nil [Boolean] if true, return nil on 404 instead of raising
# @return [Net::HTTPResponse, nil]
def get(path, not_found_returns_nil: true)
uri = URI("#{BASE_URL}#{path}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = @api_key if @api_key
response = execute(uri, request)
code = response.code.to_i
return nil if not_found_returns_nil && code == 404
return response if (200..299).cover?(code)
raise BSV::Network::ChainProviderError.new(
response.body || "HTTP #{code}",
status_code: code
)
end
def execute(uri, request)
if @http_client
@http_client.request(uri, request)
else
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
end
end
end
end
end
end