bsv-sdk 0.10.1 → 0.11.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 +21 -0
- data/lib/bsv/messages.rb +16 -0
- data/lib/bsv/primitives/base58.rb +23 -8
- data/lib/bsv/primitives/digest.rb +3 -0
- data/lib/bsv/primitives/ecies.rb +51 -11
- data/lib/bsv/primitives/point_in_finite_field.rb +4 -1
- data/lib/bsv/script/script.rb +46 -4
- data/lib/bsv/transaction/merkle_path.rb +19 -0
- data/lib/bsv/transaction/transaction.rb +1 -1
- data/lib/bsv/transaction/transaction_input.rb +1 -1
- data/lib/bsv/transaction/transaction_output.rb +1 -1
- data/lib/bsv/version.rb +1 -1
- data/lib/bsv-sdk.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 392488d3baa5c87f80eda541ca38b55ef0c4c55aa0848acdee995e6f4d1b79b1
|
|
4
|
+
data.tar.gz: 10bc2f546ec61020eac37158792de00328db64150d1f394375e4287cf32c5ab2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 973064e412dba805c930504c065fac014bfda41ef0306129e7035516b8c4f3f4450b1935667531ef2711a44ef8aad5bddecd146d36f3d35cb6fd2505bbc5be17
|
|
7
|
+
data.tar.gz: 46f59effd056e78c6a93d3fadcd4858550fc405a97032201e7d8daa2fdbcef60eb13fd87e15597b0250c4a7acebfa1773d76f095ea349baadf56ef27f2a3fb44
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to the `bsv-sdk` gem are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 0.11.0 — 2026-04-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `Base58Check.check_encode` accepts `prefix:` parameter; `check_decode` accepts `prefix_length:` (F1.1)
|
|
12
|
+
- `Base58.decode("")` raises `ArgumentError` instead of returning empty bytes (F1.2)
|
|
13
|
+
- `Digest.hash256` alias for `sha256d` (F1.7)
|
|
14
|
+
- `Script.p2pkh_lock` accepts Base58Check address strings as well as raw 20-byte hashes (F3.18)
|
|
15
|
+
- `TransactionInput#sequence` and `TransactionOutput#locking_script` now writable via `attr_accessor` (F4.10/F4.11)
|
|
16
|
+
- Coinbase 100-block maturity check in `MerklePath#verify` — intentionally diverges from TS SDK bug (F5.11)
|
|
17
|
+
- ECIES Electrum `no_key:` encrypt and `sender_public_key:` decrypt with uncompressed key detection (F6.7)
|
|
18
|
+
- `BSV::Messages` namespace re-exporting `SignedMessage` and `EncryptedMessage` (F6.16)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- `PointInFiniteField` zero-coordinate Base58 round-trip (BN(0) now encodes as `"\x00"`)
|
|
22
|
+
- `p2pkh_lock` encoding check uses bytesize only — accepts 20-byte hashes regardless of string encoding
|
|
23
|
+
- `Base58Check.check_decode` raises on `prefix_length` exceeding payload
|
|
24
|
+
- ECIES key-format ambiguity documented (inherited TS SDK design)
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Numeric fee `.ceil` behaviour documented with inline comment (F4.6)
|
|
28
|
+
|
|
8
29
|
## 0.10.1 — 2026-04-12
|
|
9
30
|
|
|
10
31
|
### Added
|
data/lib/bsv/messages.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
# Namespace providing TS SDK naming parity for messaging primitives.
|
|
5
|
+
#
|
|
6
|
+
# Re-exports {BSV::Primitives::SignedMessage} and {BSV::Primitives::EncryptedMessage}
|
|
7
|
+
# under the +BSV::Messages+ namespace, matching the structure of the TypeScript SDK
|
|
8
|
+
# (ts-sdk/src/messages/index.ts).
|
|
9
|
+
#
|
|
10
|
+
# The canonical implementations remain in +BSV::Primitives+; this module is a
|
|
11
|
+
# lightweight re-export only.
|
|
12
|
+
module Messages
|
|
13
|
+
SignedMessage = BSV::Primitives::SignedMessage
|
|
14
|
+
EncryptedMessage = BSV::Primitives::EncryptedMessage
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -61,9 +61,9 @@ module BSV
|
|
|
61
61
|
#
|
|
62
62
|
# @param string [String] Base58-encoded string
|
|
63
63
|
# @return [String] decoded binary data
|
|
64
|
-
# @raise [ArgumentError] if the string contains invalid Base58 characters
|
|
64
|
+
# @raise [ArgumentError] if the string is empty or contains invalid Base58 characters
|
|
65
65
|
def decode(string)
|
|
66
|
-
|
|
66
|
+
raise ArgumentError, 'cannot decode empty string' if string.empty?
|
|
67
67
|
|
|
68
68
|
# Count leading '1' characters (representing zero bytes)
|
|
69
69
|
leading_ones = 0
|
|
@@ -89,19 +89,30 @@ module BSV
|
|
|
89
89
|
|
|
90
90
|
# Encode binary data with a 4-byte double-SHA-256 checksum appended.
|
|
91
91
|
#
|
|
92
|
+
# When +prefix+ is given, it is prepended to the payload before checksumming.
|
|
93
|
+
# The checksum covers the full +prefix + payload+ concatenation.
|
|
94
|
+
#
|
|
92
95
|
# @param payload [String] binary data to encode
|
|
96
|
+
# @param prefix [String, nil] optional version prefix to prepend (binary string)
|
|
93
97
|
# @return [String] Base58Check-encoded string
|
|
94
|
-
def check_encode(payload)
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
def check_encode(payload, prefix: nil)
|
|
99
|
+
full = (prefix || ''.b) + payload
|
|
100
|
+
checksum = Digest.sha256d(full)[0, 4]
|
|
101
|
+
encode(full + checksum)
|
|
97
102
|
end
|
|
98
103
|
|
|
99
104
|
# Decode a Base58Check string and verify its checksum.
|
|
100
105
|
#
|
|
106
|
+
# When +prefix_length+ is greater than zero, the decoded payload is split
|
|
107
|
+
# into a prefix and data portion. The returned value is then a Hash with
|
|
108
|
+
# +:prefix+ and +:data+ keys. When +prefix_length+ is zero (default), the
|
|
109
|
+
# raw payload is returned unchanged for backwards compatibility.
|
|
110
|
+
#
|
|
101
111
|
# @param string [String] Base58Check-encoded string
|
|
102
|
-
# @
|
|
112
|
+
# @param prefix_length [Integer] number of leading bytes to treat as a prefix (default: 0)
|
|
113
|
+
# @return [String, Hash] decoded payload, or +{ prefix:, data: }+ when prefix_length > 0
|
|
103
114
|
# @raise [ChecksumError] if the checksum does not match or input is too short
|
|
104
|
-
def check_decode(string)
|
|
115
|
+
def check_decode(string, prefix_length: 0)
|
|
105
116
|
data = decode(string)
|
|
106
117
|
raise ChecksumError, 'input too short for checksum' if data.length < 4
|
|
107
118
|
|
|
@@ -110,7 +121,11 @@ module BSV
|
|
|
110
121
|
expected = Digest.sha256d(payload)[0, 4]
|
|
111
122
|
raise ChecksumError, 'checksum mismatch' unless checksum == expected
|
|
112
123
|
|
|
113
|
-
payload
|
|
124
|
+
return payload if prefix_length.zero?
|
|
125
|
+
|
|
126
|
+
raise ArgumentError, 'prefix_length exceeds payload' if prefix_length > payload.length
|
|
127
|
+
|
|
128
|
+
{ prefix: payload[0, prefix_length], data: payload[prefix_length..] }
|
|
114
129
|
end
|
|
115
130
|
end
|
|
116
131
|
end
|
data/lib/bsv/primitives/ecies.rb
CHANGED
|
@@ -33,8 +33,9 @@ module BSV
|
|
|
33
33
|
# @param message [String] the plaintext message
|
|
34
34
|
# @param public_key [PublicKey] the recipient's public key
|
|
35
35
|
# @param private_key [PrivateKey, nil] optional ephemeral key (random if omitted)
|
|
36
|
-
# @
|
|
37
|
-
|
|
36
|
+
# @param no_key [Boolean] when +true+, omit the ephemeral public key from the payload
|
|
37
|
+
# @return [String] encrypted payload: BIE1 magic + [ephemeral pubkey] + ciphertext + HMAC
|
|
38
|
+
def encrypt(message, public_key, private_key: nil, no_key: false)
|
|
38
39
|
message = message.b if message.encoding != Encoding::ASCII_8BIT
|
|
39
40
|
|
|
40
41
|
ephemeral = private_key || PrivateKey.generate
|
|
@@ -48,7 +49,11 @@ module BSV
|
|
|
48
49
|
cipher.iv = iv
|
|
49
50
|
ciphertext = message.empty? ? cipher.final : cipher.update(message) + cipher.final
|
|
50
51
|
|
|
51
|
-
payload =
|
|
52
|
+
payload = if no_key
|
|
53
|
+
MAGIC + ciphertext
|
|
54
|
+
else
|
|
55
|
+
MAGIC + ephemeral_pub.compressed + ciphertext
|
|
56
|
+
end
|
|
52
57
|
mac = Digest.hmac_sha256(key_m, payload)
|
|
53
58
|
|
|
54
59
|
payload + mac
|
|
@@ -58,29 +63,64 @@ module BSV
|
|
|
58
63
|
#
|
|
59
64
|
# Verifies the HMAC before attempting decryption (encrypt-then-MAC).
|
|
60
65
|
#
|
|
66
|
+
# The ephemeral public key may be embedded in the payload (compressed or
|
|
67
|
+
# uncompressed), or absent entirely (when the payload was encrypted with
|
|
68
|
+
# +no_key: true+). When absent, +sender_public_key+ must be provided.
|
|
69
|
+
#
|
|
70
|
+
# If a key is found in the payload and +sender_public_key+ is also given,
|
|
71
|
+
# the payload key takes precedence (matching TS SDK behaviour).
|
|
72
|
+
#
|
|
61
73
|
# @param data [String] the encrypted payload (BIE1 format)
|
|
62
74
|
# @param private_key [PrivateKey] the recipient's private key
|
|
75
|
+
# @param sender_public_key [PublicKey, nil] sender's public key (required when no key in payload)
|
|
63
76
|
# @return [String] the decrypted plaintext
|
|
64
|
-
# @raise [ArgumentError] if the data is too short
|
|
77
|
+
# @raise [ArgumentError] if the data is too short, has invalid magic, or has no key and none provided
|
|
65
78
|
# @raise [DecryptionError] if HMAC verification or AES decryption fails
|
|
66
|
-
def decrypt(data, private_key)
|
|
79
|
+
def decrypt(data, private_key, sender_public_key: nil)
|
|
67
80
|
data = data.b if data.encoding != Encoding::ASCII_8BIT
|
|
68
81
|
|
|
69
|
-
|
|
82
|
+
# Minimum: magic(4) + ciphertext(16) + HMAC(32) = 52 (no-key case)
|
|
83
|
+
raise ArgumentError, 'data too short' if data.bytesize < 52
|
|
70
84
|
|
|
71
85
|
magic = data[0, 4]
|
|
72
86
|
raise ArgumentError, 'invalid magic: expected BIE1' unless magic == MAGIC
|
|
73
87
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
# Determine ephemeral key presence and format by inspecting byte at offset 4.
|
|
89
|
+
# Ambiguity note: a no-key payload whose ciphertext starts with 0x02/0x03/0x04
|
|
90
|
+
# could be misinterpreted as containing an embedded key. The HMAC check below
|
|
91
|
+
# will catch this (wrong shared secret → HMAC mismatch), but the resulting
|
|
92
|
+
# error message will be misleading. This is a TS SDK design inheritance —
|
|
93
|
+
# the wire format has no explicit key-presence flag.
|
|
94
|
+
# Guard: only attempt to read a key if sufficient bytes remain beyond HMAC.
|
|
95
|
+
tag_length = 32
|
|
96
|
+
offset = 4
|
|
97
|
+
ephemeral_pub = nil
|
|
98
|
+
|
|
99
|
+
remaining_after_offset = data.bytesize - offset - tag_length
|
|
100
|
+
if remaining_after_offset >= 33
|
|
101
|
+
first_byte = data.getbyte(offset)
|
|
102
|
+
if [0x02, 0x03].include?(first_byte)
|
|
103
|
+
# Compressed key: 33 bytes
|
|
104
|
+
ephemeral_pub = PublicKey.from_bytes(data[offset, 33])
|
|
105
|
+
offset += 33
|
|
106
|
+
elsif first_byte == 0x04 && remaining_after_offset >= 65
|
|
107
|
+
# Uncompressed key: 65 bytes
|
|
108
|
+
ephemeral_pub = PublicKey.from_bytes(data[offset, 65])
|
|
109
|
+
offset += 65
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# If no key found in payload, fall back to provided sender_public_key
|
|
114
|
+
ephemeral_pub ||= sender_public_key
|
|
115
|
+
raise ArgumentError, 'sender_public_key required when no key in payload' if ephemeral_pub.nil?
|
|
77
116
|
|
|
78
|
-
|
|
117
|
+
mac = data[-tag_length, tag_length]
|
|
118
|
+
ciphertext = data[offset...-tag_length]
|
|
79
119
|
|
|
80
120
|
iv, key_e, key_m = derive_keys(private_key, ephemeral_pub)
|
|
81
121
|
|
|
82
122
|
# Verify HMAC before decryption (encrypt-then-MAC)
|
|
83
|
-
payload = data[0...-
|
|
123
|
+
payload = data[0...-tag_length]
|
|
84
124
|
expected_mac = Digest.hmac_sha256(key_m, payload)
|
|
85
125
|
|
|
86
126
|
raise DecryptionError, 'HMAC verification failed' unless secure_compare(mac, expected_mac)
|
|
@@ -65,7 +65,10 @@ module BSV
|
|
|
65
65
|
# Convert an OpenSSL::BN to a big-endian binary string, stripping the
|
|
66
66
|
# sign byte that OpenSSL::BN#to_s(2) sometimes prepends.
|
|
67
67
|
def bn_to_bytes(bn)
|
|
68
|
-
bn.to_s(2)
|
|
68
|
+
bytes = bn.to_s(2)
|
|
69
|
+
# BN(0).to_s(2) returns "" — encode as a single zero byte so
|
|
70
|
+
# Base58 round-trip works (Base58.decode raises on empty strings).
|
|
71
|
+
bytes.empty? ? "\x00".b : bytes
|
|
69
72
|
end
|
|
70
73
|
end
|
|
71
74
|
end
|
data/lib/bsv/script/script.rb
CHANGED
|
@@ -144,11 +144,19 @@ module BSV
|
|
|
144
144
|
|
|
145
145
|
# Construct a Pay-to-Public-Key-Hash (P2PKH) locking script.
|
|
146
146
|
#
|
|
147
|
-
#
|
|
147
|
+
# Accepts either a raw 20-byte binary hash or a Base58Check address string.
|
|
148
|
+
# When given an address string, the version prefix is validated: +0x00+
|
|
149
|
+
# (mainnet) and +0x6f+ (testnet) are accepted; +0x05+ (P2SH) is rejected
|
|
150
|
+
# with a clear error message.
|
|
151
|
+
#
|
|
152
|
+
# @param pubkey_hash_or_address [String] 20-byte binary pubkey hash, or a
|
|
153
|
+
# Base58Check address string
|
|
148
154
|
# @return [Script]
|
|
149
|
-
# @raise [ArgumentError] if
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
# @raise [ArgumentError] if the argument is not a valid 20-byte hash, if the
|
|
156
|
+
# address has an unrecognised prefix, or if a P2SH address is supplied
|
|
157
|
+
# @raise [BSV::Primitives::Base58::ChecksumError] if the address checksum is invalid
|
|
158
|
+
def self.p2pkh_lock(pubkey_hash_or_address)
|
|
159
|
+
pubkey_hash = resolve_pubkey_hash(pubkey_hash_or_address)
|
|
152
160
|
|
|
153
161
|
buf = [
|
|
154
162
|
Opcodes::OP_DUP,
|
|
@@ -159,6 +167,40 @@ module BSV
|
|
|
159
167
|
new(buf)
|
|
160
168
|
end
|
|
161
169
|
|
|
170
|
+
# @api private
|
|
171
|
+
# Resolve a pubkey_hash argument that may be a raw binary hash or a
|
|
172
|
+
# Base58Check address string.
|
|
173
|
+
def self.resolve_pubkey_hash(arg)
|
|
174
|
+
# A 20-byte string is treated as a raw binary hash regardless of
|
|
175
|
+
# encoding — callers commonly pass `"\x00" * 20` which is UTF-8.
|
|
176
|
+
# A valid Base58Check address is always longer than 20 characters
|
|
177
|
+
# (25 raw bytes → ~34 Base58 characters), so this is unambiguous.
|
|
178
|
+
return arg.b if arg.bytesize == 20
|
|
179
|
+
|
|
180
|
+
# Otherwise treat as a Base58Check address string.
|
|
181
|
+
decoded = BSV::Primitives::Base58.check_decode(arg, prefix_length: 1)
|
|
182
|
+
prefix = decoded[:prefix]
|
|
183
|
+
data = decoded[:data]
|
|
184
|
+
|
|
185
|
+
p2sh_prefix = "\x05".b
|
|
186
|
+
mainnet_prefix = "\x00".b
|
|
187
|
+
testnet_prefix = "\x6f".b # accepted for testnet interop; no mainnet-only restriction
|
|
188
|
+
|
|
189
|
+
if prefix == p2sh_prefix
|
|
190
|
+
raise ArgumentError,
|
|
191
|
+
'P2SH addresses are not supported on BSV; ' \
|
|
192
|
+
'use p2pkh_lock with a P2PKH address or 20-byte hash'
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
raise ArgumentError, "unrecognised address prefix: 0x#{prefix.unpack1('H*')}" \
|
|
196
|
+
unless prefix == mainnet_prefix || prefix == testnet_prefix
|
|
197
|
+
|
|
198
|
+
raise ArgumentError, 'decoded hash must be 20 bytes' unless data.bytesize == 20
|
|
199
|
+
|
|
200
|
+
data
|
|
201
|
+
end
|
|
202
|
+
private_class_method :resolve_pubkey_hash
|
|
203
|
+
|
|
162
204
|
# Construct a P2PKH unlocking script.
|
|
163
205
|
#
|
|
164
206
|
# @param signature_der [String] DER-encoded signature with sighash byte appended
|
|
@@ -303,10 +303,29 @@ module BSV
|
|
|
303
303
|
# Computes the merkle root from the path and txid, then checks it
|
|
304
304
|
# against the blockchain via the provided chain tracker.
|
|
305
305
|
#
|
|
306
|
+
# For coinbase transactions (offset 0 in the merkle tree), an additional
|
|
307
|
+
# maturity check is performed: the coinbase must have at least 100
|
|
308
|
+
# confirmations before it is considered spendable/valid.
|
|
309
|
+
#
|
|
310
|
+
# NOTE: The TS SDK has an inverted coinbase maturity check at MerklePath.ts:378
|
|
311
|
+
# (`this.blockHeight + 100 < height`), which rejects mature coinbase transactions
|
|
312
|
+
# and accepts immature ones — the opposite of the intended behaviour. The correct
|
|
313
|
+
# logic is: reject when `current_height - block_height < 100` (immature).
|
|
314
|
+
#
|
|
306
315
|
# @param txid_hex [String] hex-encoded transaction ID (display order)
|
|
307
316
|
# @param chain_tracker [ChainTracker] chain tracker to verify the root against
|
|
308
317
|
# @return [Boolean] true if the computed root matches the block at this height
|
|
309
318
|
def verify(txid_hex, chain_tracker)
|
|
319
|
+
txid_bytes = [txid_hex].pack('H*').reverse
|
|
320
|
+
txid_leaf = @path[0].find { |l| l.hash == txid_bytes }
|
|
321
|
+
|
|
322
|
+
# Offset 0 in a block's merkle tree is always the coinbase transaction —
|
|
323
|
+
# a Bitcoin protocol invariant. Apply the 100-block maturity check.
|
|
324
|
+
if txid_leaf&.offset&.zero?
|
|
325
|
+
current = chain_tracker.current_height
|
|
326
|
+
return false if current - @block_height < 100
|
|
327
|
+
end
|
|
328
|
+
|
|
310
329
|
root_hex = compute_root_hex(txid_hex)
|
|
311
330
|
chain_tracker.valid_root_for_height?(root_hex, @block_height)
|
|
312
331
|
end
|
|
@@ -801,7 +801,7 @@ module BSV
|
|
|
801
801
|
when FeeModel
|
|
802
802
|
model_or_fee.compute_fee(self)
|
|
803
803
|
when Numeric
|
|
804
|
-
model_or_fee.ceil
|
|
804
|
+
model_or_fee.ceil # round up — fractional satoshis from callers are not valid; rounding up prevents underpayment
|
|
805
805
|
else
|
|
806
806
|
raise ArgumentError, "expected FeeModel, Numeric, or nil; got #{model_or_fee.class}"
|
|
807
807
|
end
|
|
@@ -15,7 +15,7 @@ module BSV
|
|
|
15
15
|
attr_reader :prev_tx_out_index
|
|
16
16
|
|
|
17
17
|
# @return [Integer] sequence number (default: 0xFFFFFFFF)
|
|
18
|
-
|
|
18
|
+
attr_accessor :sequence
|
|
19
19
|
|
|
20
20
|
# @return [Script::Script, nil] the unlocking script (set after signing)
|
|
21
21
|
attr_accessor :unlocking_script
|
|
@@ -12,7 +12,7 @@ module BSV
|
|
|
12
12
|
attr_accessor :satoshis
|
|
13
13
|
|
|
14
14
|
# @return [Script::Script] the locking script (spending conditions)
|
|
15
|
-
|
|
15
|
+
attr_accessor :locking_script
|
|
16
16
|
|
|
17
17
|
# @return [Boolean] whether this output receives change
|
|
18
18
|
attr_accessor :change
|
data/lib/bsv/version.rb
CHANGED
data/lib/bsv-sdk.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsv-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Bettison
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: mcp
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/bsv/mcp/tools/fetch_utxos.rb
|
|
58
58
|
- lib/bsv/mcp/tools/generate_key.rb
|
|
59
59
|
- lib/bsv/mcp/tools/helpers.rb
|
|
60
|
+
- lib/bsv/messages.rb
|
|
60
61
|
- lib/bsv/network.rb
|
|
61
62
|
- lib/bsv/network/arc.rb
|
|
62
63
|
- lib/bsv/network/broadcast_error.rb
|
|
@@ -164,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
164
165
|
- !ruby/object:Gem::Version
|
|
165
166
|
version: '0'
|
|
166
167
|
requirements: []
|
|
167
|
-
rubygems_version:
|
|
168
|
+
rubygems_version: 4.0.10
|
|
168
169
|
specification_version: 4
|
|
169
170
|
summary: Ruby SDK for the BSV Blockchain
|
|
170
171
|
test_files: []
|