bsv-sdk 0.9.0 → 0.10.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-sdk.md → CHANGELOG.md} +37 -0
- data/bin/bsv-mcp +10 -0
- data/lib/bsv/mcp/config.rb +44 -0
- data/lib/bsv/mcp/server.rb +68 -0
- data/lib/bsv/mcp/tools/broadcast_p2pkh.rb +211 -0
- data/lib/bsv/mcp/tools/check_balance.rb +96 -0
- data/lib/bsv/mcp/tools/decode_tx.rb +64 -0
- data/lib/bsv/mcp/tools/fetch_tx.rb +72 -0
- data/lib/bsv/mcp/tools/fetch_utxos.rb +72 -0
- data/lib/bsv/mcp/tools/generate_key.rb +62 -0
- data/lib/bsv/mcp/tools/helpers.rb +87 -0
- data/lib/bsv/mcp.rb +18 -0
- data/lib/bsv/network/arc.rb +107 -8
- data/lib/bsv/primitives/schnorr.rb +22 -0
- data/lib/bsv/script/interpreter/error.rb +1 -0
- data/lib/bsv/script/interpreter/interpreter.rb +16 -11
- data/lib/bsv/script/interpreter/operations/arithmetic.rb +43 -7
- data/lib/bsv/script/interpreter/operations/flow_control.rb +85 -10
- data/lib/bsv/script/interpreter/operations/splice.rb +49 -0
- data/lib/bsv/script/opcodes.rb +2 -3
- data/lib/bsv/transaction/chain_trackers/chaintracks.rb +95 -0
- data/lib/bsv/transaction/chain_trackers.rb +11 -0
- data/lib/bsv/version.rb +1 -1
- data/lib/bsv-sdk.rb +1 -0
- metadata +33 -6
|
@@ -80,6 +80,55 @@ module BSV
|
|
|
80
80
|
data = @dstack.pop_bytes
|
|
81
81
|
@dstack.push_bytes(ScriptNumber.minimally_encode(data))
|
|
82
82
|
end
|
|
83
|
+
|
|
84
|
+
# OP_SUBSTR: extract a substring by offset and length
|
|
85
|
+
# Stack: [... data offset len] -> [... data[offset, len]]
|
|
86
|
+
def op_substr
|
|
87
|
+
len = @dstack.pop_int.to_i32
|
|
88
|
+
offset = @dstack.pop_int.to_i32
|
|
89
|
+
data = @dstack.pop_bytes
|
|
90
|
+
|
|
91
|
+
unless offset >= 0 && offset < data.bytesize && len >= 0 && len <= data.bytesize - offset
|
|
92
|
+
raise ScriptError.new(
|
|
93
|
+
ScriptErrorCode::INVALID_INPUT_LENGTH,
|
|
94
|
+
"invalid OP_SUBSTR range: offset=#{offset} len=#{len} for data of length #{data.bytesize}"
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@dstack.push_bytes(data.byteslice(offset, len))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# OP_LEFT: extract the leftmost N bytes
|
|
102
|
+
# Stack: [... data len] -> [... data[0, len]]
|
|
103
|
+
def op_left
|
|
104
|
+
len = @dstack.pop_int.to_i32
|
|
105
|
+
data = @dstack.pop_bytes
|
|
106
|
+
|
|
107
|
+
unless len.between?(0, data.bytesize)
|
|
108
|
+
raise ScriptError.new(
|
|
109
|
+
ScriptErrorCode::INVALID_INPUT_LENGTH,
|
|
110
|
+
"invalid OP_LEFT length: #{len} for data of length #{data.bytesize}"
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
@dstack.push_bytes(data.byteslice(0, len))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# OP_RIGHT: extract the rightmost N bytes
|
|
118
|
+
# Stack: [... data len] -> [... data[size-len, len]]
|
|
119
|
+
def op_right
|
|
120
|
+
len = @dstack.pop_int.to_i32
|
|
121
|
+
data = @dstack.pop_bytes
|
|
122
|
+
|
|
123
|
+
unless len.between?(0, data.bytesize)
|
|
124
|
+
raise ScriptError.new(
|
|
125
|
+
ScriptErrorCode::INVALID_INPUT_LENGTH,
|
|
126
|
+
"invalid OP_RIGHT length: #{len} for data of length #{data.bytesize}"
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
@dstack.push_bytes(data.byteslice(data.bytesize - len, len))
|
|
131
|
+
end
|
|
83
132
|
end
|
|
84
133
|
end
|
|
85
134
|
end
|
data/lib/bsv/script/opcodes.rb
CHANGED
|
@@ -130,9 +130,8 @@ module BSV
|
|
|
130
130
|
OP_CHECKLOCKTIMEVERIFY = 0xb1
|
|
131
131
|
OP_CHECKSEQUENCEVERIFY = 0xb2
|
|
132
132
|
|
|
133
|
-
# Chronicle slots (0xb3–0xb7):
|
|
134
|
-
#
|
|
135
|
-
# will raise ScriptErrorCode::UNIMPLEMENTED_OPCODE at execution time.
|
|
133
|
+
# Chronicle slots (0xb3–0xb7): string-splice and numeric-shift opcodes
|
|
134
|
+
# re-enabled in BSV Chronicle. Fully implemented in the interpreter.
|
|
136
135
|
OP_SUBSTR = 0xb3
|
|
137
136
|
OP_LEFT = 0xb4
|
|
138
137
|
OP_RIGHT = 0xb5
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
module BSV
|
|
8
|
+
module Transaction
|
|
9
|
+
module ChainTrackers
|
|
10
|
+
# Chain tracker that verifies merkle roots using the Chaintracks API (Arcade/GorillaPool).
|
|
11
|
+
#
|
|
12
|
+
# Queries the Chaintracks v2 block header endpoint to retrieve the merkle root for a
|
|
13
|
+
# given block height and compares it with the provided root.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# tracker = BSV::Transaction::ChainTrackers::Chaintracks.new
|
|
17
|
+
# tracker.valid_root_for_height?('abcd...', 800_000)
|
|
18
|
+
#
|
|
19
|
+
# @example With API key
|
|
20
|
+
# tracker = BSV::Transaction::ChainTrackers::Chaintracks.new(api_key: 'my-key')
|
|
21
|
+
# tracker.current_height
|
|
22
|
+
class Chaintracks < ChainTracker
|
|
23
|
+
MAINNET_URL = 'https://arcade.gorillapool.io'
|
|
24
|
+
TESTNET_URL = 'https://testnet.arcade.gorillapool.io'
|
|
25
|
+
|
|
26
|
+
# @param url [String] base URL for the Chaintracks API
|
|
27
|
+
# @param api_key [String, nil] optional Bearer API key
|
|
28
|
+
# @param http_client [#request, nil] injectable HTTP client for testing
|
|
29
|
+
def initialize(url: MAINNET_URL, api_key: nil, http_client: nil)
|
|
30
|
+
super()
|
|
31
|
+
@url = url.chomp('/')
|
|
32
|
+
@api_key = api_key
|
|
33
|
+
@http_client = http_client
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Verify that a merkle root is valid for the given block height.
|
|
37
|
+
#
|
|
38
|
+
# @param root [String] merkle root as a hex string
|
|
39
|
+
# @param height [Integer] block height
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
def valid_root_for_height?(root, height)
|
|
42
|
+
response = get("/chaintracks/v2/header/height/#{height}")
|
|
43
|
+
return false if response.nil?
|
|
44
|
+
|
|
45
|
+
data = JSON.parse(response.body)
|
|
46
|
+
merkle_root = data['merkleRoot']
|
|
47
|
+
return false unless merkle_root
|
|
48
|
+
|
|
49
|
+
merkle_root.downcase == root.downcase
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Return the current blockchain height.
|
|
53
|
+
#
|
|
54
|
+
# @return [Integer]
|
|
55
|
+
def current_height
|
|
56
|
+
response = get('/chaintracks/v2/tip', not_found_returns_nil: false)
|
|
57
|
+
data = JSON.parse(response.body)
|
|
58
|
+
data['height']
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# @param path [String] API path
|
|
64
|
+
# @param not_found_returns_nil [Boolean] if true, return nil on 404 instead of raising
|
|
65
|
+
# @return [Net::HTTPResponse, nil]
|
|
66
|
+
def get(path, not_found_returns_nil: true)
|
|
67
|
+
uri = URI("#{@url}#{path}")
|
|
68
|
+
request = Net::HTTP::Get.new(uri)
|
|
69
|
+
request['Authorization'] = "Bearer #{@api_key}" if @api_key
|
|
70
|
+
|
|
71
|
+
response = execute(uri, request)
|
|
72
|
+
code = response.code.to_i
|
|
73
|
+
|
|
74
|
+
return nil if not_found_returns_nil && code == 404
|
|
75
|
+
return response if (200..299).cover?(code)
|
|
76
|
+
|
|
77
|
+
raise BSV::Network::ChainProviderError.new(
|
|
78
|
+
response.body || "HTTP #{code}",
|
|
79
|
+
status_code: code
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def execute(uri, request)
|
|
84
|
+
if @http_client
|
|
85
|
+
@http_client.request(uri, request)
|
|
86
|
+
else
|
|
87
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
88
|
+
http.request(request)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -5,6 +5,17 @@ module BSV
|
|
|
5
5
|
# Namespace for chain tracker implementations.
|
|
6
6
|
module ChainTrackers
|
|
7
7
|
autoload :WhatsOnChain, 'bsv/transaction/chain_trackers/whats_on_chain'
|
|
8
|
+
autoload :Chaintracks, 'bsv/transaction/chain_trackers/chaintracks'
|
|
9
|
+
|
|
10
|
+
# Return a default chain tracker backed by the Arcade/GorillaPool Chaintracks API.
|
|
11
|
+
#
|
|
12
|
+
# @param testnet [Boolean] use the testnet endpoint when true
|
|
13
|
+
# @param api_key [String, nil] optional Bearer API key
|
|
14
|
+
# @return [Chaintracks]
|
|
15
|
+
def self.default(testnet: false, api_key: nil)
|
|
16
|
+
url = testnet ? Chaintracks::TESTNET_URL : Chaintracks::MAINNET_URL
|
|
17
|
+
Chaintracks.new(url: url, api_key: api_key)
|
|
18
|
+
end
|
|
8
19
|
end
|
|
9
20
|
end
|
|
10
21
|
end
|
data/lib/bsv/version.rb
CHANGED
data/lib/bsv-sdk.rb
CHANGED
metadata
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bettison
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
11
|
-
dependencies:
|
|
10
|
+
date: 2026-04-11 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mcp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.12'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.12'
|
|
12
26
|
description: A Ruby library for interacting with the BSV Blockchain — keys, scripts,
|
|
13
27
|
transactions, and more.
|
|
14
|
-
executables:
|
|
28
|
+
executables:
|
|
29
|
+
- bsv-mcp
|
|
15
30
|
extensions: []
|
|
16
31
|
extra_rdoc_files: []
|
|
17
32
|
files:
|
|
18
|
-
- CHANGELOG
|
|
33
|
+
- CHANGELOG.md
|
|
19
34
|
- LICENSE
|
|
20
35
|
- README.md
|
|
36
|
+
- bin/bsv-mcp
|
|
21
37
|
- lib/bsv-sdk.rb
|
|
22
38
|
- lib/bsv/auth.rb
|
|
23
39
|
- lib/bsv/auth/auth_error.rb
|
|
@@ -31,6 +47,16 @@ files:
|
|
|
31
47
|
- lib/bsv/identity/constants.rb
|
|
32
48
|
- lib/bsv/identity/identity_parser.rb
|
|
33
49
|
- lib/bsv/identity/types.rb
|
|
50
|
+
- lib/bsv/mcp.rb
|
|
51
|
+
- lib/bsv/mcp/config.rb
|
|
52
|
+
- lib/bsv/mcp/server.rb
|
|
53
|
+
- lib/bsv/mcp/tools/broadcast_p2pkh.rb
|
|
54
|
+
- lib/bsv/mcp/tools/check_balance.rb
|
|
55
|
+
- lib/bsv/mcp/tools/decode_tx.rb
|
|
56
|
+
- lib/bsv/mcp/tools/fetch_tx.rb
|
|
57
|
+
- lib/bsv/mcp/tools/fetch_utxos.rb
|
|
58
|
+
- lib/bsv/mcp/tools/generate_key.rb
|
|
59
|
+
- lib/bsv/mcp/tools/helpers.rb
|
|
34
60
|
- lib/bsv/network.rb
|
|
35
61
|
- lib/bsv/network/arc.rb
|
|
36
62
|
- lib/bsv/network/broadcast_error.rb
|
|
@@ -97,6 +123,7 @@ files:
|
|
|
97
123
|
- lib/bsv/transaction/beef.rb
|
|
98
124
|
- lib/bsv/transaction/chain_tracker.rb
|
|
99
125
|
- lib/bsv/transaction/chain_trackers.rb
|
|
126
|
+
- lib/bsv/transaction/chain_trackers/chaintracks.rb
|
|
100
127
|
- lib/bsv/transaction/chain_trackers/whats_on_chain.rb
|
|
101
128
|
- lib/bsv/transaction/fee_model.rb
|
|
102
129
|
- lib/bsv/transaction/fee_models.rb
|
|
@@ -121,7 +148,7 @@ licenses:
|
|
|
121
148
|
metadata:
|
|
122
149
|
homepage_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
123
150
|
source_code_uri: https://github.com/sgbett/bsv-ruby-sdk
|
|
124
|
-
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/
|
|
151
|
+
changelog_uri: https://github.com/sgbett/bsv-ruby-sdk/blob/master/gem/bsv-sdk/CHANGELOG.md
|
|
125
152
|
rubygems_mfa_required: 'true'
|
|
126
153
|
rdoc_options: []
|
|
127
154
|
require_paths:
|