bsv-sdk 0.1.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 +7 -0
- data/CHANGELOG.md +58 -0
- data/LICENCE +86 -0
- data/README.md +155 -0
- data/lib/bsv/attest/configuration.rb +9 -0
- data/lib/bsv/attest/response.rb +19 -0
- data/lib/bsv/attest/verification_error.rb +7 -0
- data/lib/bsv/attest/version.rb +7 -0
- data/lib/bsv/attest.rb +71 -0
- data/lib/bsv/network/arc.rb +113 -0
- data/lib/bsv/network/broadcast_error.rb +15 -0
- data/lib/bsv/network/broadcast_response.rb +29 -0
- data/lib/bsv/network/chain_provider_error.rb +14 -0
- data/lib/bsv/network/utxo.rb +28 -0
- data/lib/bsv/network/whats_on_chain.rb +82 -0
- data/lib/bsv/network.rb +12 -0
- data/lib/bsv/primitives/base58.rb +117 -0
- data/lib/bsv/primitives/bsm.rb +131 -0
- data/lib/bsv/primitives/curve.rb +115 -0
- data/lib/bsv/primitives/digest.rb +99 -0
- data/lib/bsv/primitives/ecdsa.rb +224 -0
- data/lib/bsv/primitives/ecies.rb +128 -0
- data/lib/bsv/primitives/extended_key.rb +315 -0
- data/lib/bsv/primitives/mnemonic/wordlist.rb +270 -0
- data/lib/bsv/primitives/mnemonic.rb +192 -0
- data/lib/bsv/primitives/private_key.rb +139 -0
- data/lib/bsv/primitives/public_key.rb +118 -0
- data/lib/bsv/primitives/schnorr.rb +108 -0
- data/lib/bsv/primitives/signature.rb +136 -0
- data/lib/bsv/primitives.rb +23 -0
- data/lib/bsv/script/builder.rb +73 -0
- data/lib/bsv/script/chunk.rb +77 -0
- data/lib/bsv/script/interpreter/error.rb +54 -0
- data/lib/bsv/script/interpreter/interpreter.rb +281 -0
- data/lib/bsv/script/interpreter/operations/arithmetic.rb +243 -0
- data/lib/bsv/script/interpreter/operations/bitwise.rb +68 -0
- data/lib/bsv/script/interpreter/operations/crypto.rb +209 -0
- data/lib/bsv/script/interpreter/operations/data_push.rb +34 -0
- data/lib/bsv/script/interpreter/operations/flow_control.rb +94 -0
- data/lib/bsv/script/interpreter/operations/splice.rb +89 -0
- data/lib/bsv/script/interpreter/operations/stack_ops.rb +112 -0
- data/lib/bsv/script/interpreter/script_number.rb +218 -0
- data/lib/bsv/script/interpreter/stack.rb +203 -0
- data/lib/bsv/script/opcodes.rb +165 -0
- data/lib/bsv/script/script.rb +424 -0
- data/lib/bsv/script.rb +20 -0
- data/lib/bsv/transaction/beef.rb +323 -0
- data/lib/bsv/transaction/merkle_path.rb +250 -0
- data/lib/bsv/transaction/p2pkh.rb +44 -0
- data/lib/bsv/transaction/sighash.rb +48 -0
- data/lib/bsv/transaction/transaction.rb +380 -0
- data/lib/bsv/transaction/transaction_input.rb +109 -0
- data/lib/bsv/transaction/transaction_output.rb +51 -0
- data/lib/bsv/transaction/unlocking_script_template.rb +36 -0
- data/lib/bsv/transaction/var_int.rb +50 -0
- data/lib/bsv/transaction.rb +21 -0
- data/lib/bsv/version.rb +5 -0
- data/lib/bsv/wallet/insufficient_funds_error.rb +15 -0
- data/lib/bsv/wallet/wallet.rb +119 -0
- data/lib/bsv/wallet.rb +8 -0
- data/lib/bsv-attest.rb +4 -0
- data/lib/bsv-sdk.rb +11 -0
- metadata +104 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ed81ad480647a52136c32f6bd786fd8a0a590aa6d2805fb02151b583c6b8664b
|
|
4
|
+
data.tar.gz: c6eba8e1020913d59958d68b028b66ef7035d494f3c5d34dbd98c401ab30ec35
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: aa4673f314ac7dddcb94d04d8e9c6db75a2c635c298e46d9412ee65babb491fd514f1bc37e94996206a41ad7d35ff1c2f57554913201be67e532700793b187da
|
|
7
|
+
data.tar.gz: ca38730828db7c78299a8eee34a682c5781f8cdae44186d7762b12030158f0f696d78204819f832e8f2bb8f913298100f9c55fb7a6f1f89cce4453c759d5e2c1
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-02-14
|
|
9
|
+
|
|
10
|
+
Initial release of the BSV Ruby SDK.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### Primitives
|
|
15
|
+
|
|
16
|
+
- secp256k1 elliptic curve operations (point arithmetic, scalar multiplication)
|
|
17
|
+
- ECDSA signing and verification with RFC 6979 deterministic nonces
|
|
18
|
+
- Public and private key handling (WIF import/export, compressed/uncompressed formats)
|
|
19
|
+
- Base58Check encoding and decoding
|
|
20
|
+
- Hash functions: SHA-256, RIPEMD-160, Hash160 (SHA-256 + RIPEMD-160), SHA-512, HMAC
|
|
21
|
+
- BIP-32 hierarchical deterministic key derivation (extended keys, hardened/normal child paths)
|
|
22
|
+
- BIP-39 mnemonic phrase generation and seed derivation
|
|
23
|
+
- ECIES encryption and decryption (BIE1 format)
|
|
24
|
+
- Bitcoin Signed Message (BSM) signing and verification
|
|
25
|
+
|
|
26
|
+
#### Script
|
|
27
|
+
|
|
28
|
+
- Opcode constants (full set)
|
|
29
|
+
- Script chunk representation and parsing
|
|
30
|
+
- Script serialisation and deserialisation
|
|
31
|
+
- Script templates: P2PKH, P2PK, P2MS (multisig), OP_RETURN data
|
|
32
|
+
- Script type detection (including read-only recognition of P2SH and other legacy types)
|
|
33
|
+
- Script builder API for programmatic construction
|
|
34
|
+
- Script interpreter with stack operations, arithmetic, crypto, flow control, splice, and bitwise ops
|
|
35
|
+
|
|
36
|
+
#### Transaction
|
|
37
|
+
|
|
38
|
+
- Transaction construction and serialisation (raw format)
|
|
39
|
+
- BIP-143 sighash computation (all hash types with FORKID)
|
|
40
|
+
- Transaction signing with configurable sighash flags
|
|
41
|
+
- BEEF serialisation (BRC-95/BRC-96)
|
|
42
|
+
- Merkle path representation and verification
|
|
43
|
+
- Fee estimation
|
|
44
|
+
- Script verification during signing
|
|
45
|
+
- Unlocking script templates for common script types
|
|
46
|
+
|
|
47
|
+
#### Network
|
|
48
|
+
|
|
49
|
+
- ARC broadcaster for transaction submission
|
|
50
|
+
- WhatsOnChain chain data provider
|
|
51
|
+
- Basic wallet functionality
|
|
52
|
+
|
|
53
|
+
#### Testing
|
|
54
|
+
|
|
55
|
+
- Cross-SDK test vectors from Go, TypeScript, and Python reference implementations
|
|
56
|
+
- NIST and RFC hash function test vectors
|
|
57
|
+
- Bitcoin Core script interpreter test suite
|
|
58
|
+
- Protocol conformance specs for opcodes, sighash flags, and transaction templates
|
data/LICENCE
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Open BSV License Version 5 – granted by BSV Association, Grafenauweg 6, 6300
|
|
2
|
+
Zug, Switzerland (CHE-427.008.338) ("Licensor"), to you as a user (henceforth
|
|
3
|
+
"You", "User" or "Licensee").
|
|
4
|
+
|
|
5
|
+
For the purposes of this license, the definitions below have the following
|
|
6
|
+
meanings:
|
|
7
|
+
|
|
8
|
+
"Bitcoin Protocol" means the protocol implementation, cryptographic rules,
|
|
9
|
+
network protocols, and consensus mechanisms in the Bitcoin White Paper as
|
|
10
|
+
described here https://protocol.bsvblockchain.org.
|
|
11
|
+
|
|
12
|
+
"Bitcoin White Paper" means the paper entitled 'Bitcoin: A Peer-to-Peer
|
|
13
|
+
Electronic Cash System' published by 'Satoshi Nakamoto' in October 2008.
|
|
14
|
+
|
|
15
|
+
"BSV Blockchains" means:
|
|
16
|
+
(a) the Bitcoin blockchain containing block height #556767 with the hash
|
|
17
|
+
"000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b" and
|
|
18
|
+
that contains the longest honest persistent chain of blocks which has been
|
|
19
|
+
produced in a manner which is consistent with the rules set forth in the
|
|
20
|
+
Network Access Rules; and
|
|
21
|
+
(b) the test blockchains that contain the longest honest persistent chains of
|
|
22
|
+
blocks which has been produced in a manner which is consistent with the
|
|
23
|
+
rules set forth in the Network Access Rules.
|
|
24
|
+
|
|
25
|
+
"Network Access Rules" or "Rules" means the set of rules regulating the
|
|
26
|
+
relationship between BSV Association and the nodes on BSV based on the Bitcoin
|
|
27
|
+
Protocol rules and those set out in the Bitcoin White Paper, and available here
|
|
28
|
+
https://bsvblockchain.org/network-access-rules.
|
|
29
|
+
|
|
30
|
+
"Software" means the software the subject of this licence, including any/all
|
|
31
|
+
intellectual property rights therein and associated documentation files.
|
|
32
|
+
|
|
33
|
+
BSV Association grants permission, free of charge and on a non-exclusive and
|
|
34
|
+
revocable basis, to any person obtaining a copy of the Software to deal in the
|
|
35
|
+
Software without restriction, including without limitation the rights to use,
|
|
36
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
37
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
|
38
|
+
subject to and conditioned upon the following conditions:
|
|
39
|
+
|
|
40
|
+
1 - The text "© BSV Association," and this license shall be included in all
|
|
41
|
+
copies or substantial portions of the Software.
|
|
42
|
+
2 - The Software, and any software that is derived from the Software or parts
|
|
43
|
+
thereof, must only be used on the BSV Blockchains.
|
|
44
|
+
|
|
45
|
+
For the avoidance of doubt, this license is granted subject to and conditioned
|
|
46
|
+
upon your compliance with these terms only. In the event of non-compliance, the
|
|
47
|
+
license shall extinguish and you can be enjoined from violating BSV's
|
|
48
|
+
intellectual property rights (incl. damages and similar related claims).
|
|
49
|
+
|
|
50
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
51
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES REGARDING ENTITLEMENT,
|
|
52
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
53
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS THEREOF BE LIABLE FOR ANY CLAIM,
|
|
54
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
55
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
56
|
+
DEALINGS IN THE SOFTWARE.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
Version 0.1.1 of the Bitcoin SV software, and prior versions of software upon
|
|
60
|
+
which it was based, were licensed under the MIT License, which is included below.
|
|
61
|
+
|
|
62
|
+
The MIT License (MIT)
|
|
63
|
+
|
|
64
|
+
Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
65
|
+
Copyright (c) 2009-2015 Bitcoin Developers
|
|
66
|
+
Copyright (c) 2009-2017 The Bitcoin Core developers
|
|
67
|
+
Copyright (c) 2017 The Bitcoin ABC developers
|
|
68
|
+
Copyright (c) 2018 Bitcoin Association for BSV
|
|
69
|
+
Copyright (c) 2023 BSV Association
|
|
70
|
+
|
|
71
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
72
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
73
|
+
the Software without restriction, including without limitation the rights to
|
|
74
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
75
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
76
|
+
subject to the following conditions:
|
|
77
|
+
|
|
78
|
+
The above copyright notice and this permission notice shall be included in all
|
|
79
|
+
copies or substantial portions of the Software.
|
|
80
|
+
|
|
81
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
82
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
83
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
84
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
85
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
86
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# BSV Ruby SDK
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sgbett/bsv-ruby-sdk/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/sgbett/bsv-ruby-sdk)
|
|
5
|
+
[](https://rubygems.org/gems/bsv-sdk)
|
|
6
|
+
[](https://rubygems.org/gems/bsv-sdk)
|
|
7
|
+
|
|
8
|
+
Welcome to the BSV Blockchain Libraries Project, the comprehensive Ruby SDK designed to provide an updated and unified layer for developing scalable applications on the BSV Blockchain. This SDK addresses the limitations of previous tools by offering a fresh, peer-to-peer approach, adhering to SPV, and ensuring privacy and scalability.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Acknowledgements](#acknowledgements)
|
|
13
|
+
2. [Objective](#objective)
|
|
14
|
+
3. [Getting Started](#getting-started)
|
|
15
|
+
4. [Features & Deliverables](#features--deliverables)
|
|
16
|
+
5. [Documentation](#documentation)
|
|
17
|
+
6. [Contribution Guidelines](#contribution-guidelines)
|
|
18
|
+
7. [Support & Contacts](#support--contacts)
|
|
19
|
+
8. [Licence](#licence)
|
|
20
|
+
|
|
21
|
+
## Acknowledgements
|
|
22
|
+
|
|
23
|
+
This Ruby SDK is a port of the official BSV Blockchain SDKs, which serve as its reference implementations. Primitives, script handling, and transaction logic are directly translated from them, adapted for Ruby idioms and conventions.
|
|
24
|
+
|
|
25
|
+
The reference SDKs:
|
|
26
|
+
|
|
27
|
+
- [TypeScript SDK](https://github.com/bsv-blockchain/ts-sdk)
|
|
28
|
+
- [Go SDK](https://github.com/bsv-blockchain/go-sdk)
|
|
29
|
+
- [Python SDK](https://github.com/bsv-blockchain/py-sdk)
|
|
30
|
+
|
|
31
|
+
These are maintained under the BSV Blockchain organisation and backed by the Bitcoin Association. The debt to their contributors is substantial — their clear, robust code made this port both feasible and consistent.
|
|
32
|
+
|
|
33
|
+
## Objective
|
|
34
|
+
|
|
35
|
+
The BSV Blockchain Libraries Project aims to structure and maintain a middleware layer of the BSV Blockchain technology stack. By facilitating the development and maintenance of core libraries, it serves as an essential toolkit for developers looking to build on the BSV Blockchain.
|
|
36
|
+
|
|
37
|
+
This Ruby SDK brings maximum compatibility with the official SDK family to the Ruby ecosystem. It was born from a practical need: building an attestation gem ([bsv-attest](https://rubygems.org/gems/bsv-attest)) required a complete, idiomatic Ruby implementation of BSV primitives, script handling, and transaction construction. Rather than wrapping FFI bindings or shelling out to other languages, the SDK implements everything in pure Ruby using only the standard library's OpenSSL bindings.
|
|
38
|
+
|
|
39
|
+
<!-- TODO: Update bsv-attest link once gem documentation is published (see #48) -->
|
|
40
|
+
|
|
41
|
+
## Getting Started
|
|
42
|
+
|
|
43
|
+
### Requirements
|
|
44
|
+
|
|
45
|
+
- Ruby >= 2.7
|
|
46
|
+
- No external dependencies beyond Ruby's standard library (`openssl`)
|
|
47
|
+
|
|
48
|
+
### Installation
|
|
49
|
+
|
|
50
|
+
Add to your Gemfile:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
gem 'bsv-sdk'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or install directly:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gem install bsv-sdk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Basic Usage
|
|
63
|
+
|
|
64
|
+
Create and sign a P2PKH transaction:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
require 'bsv-sdk'
|
|
68
|
+
|
|
69
|
+
# Generate a new private key (or load from WIF)
|
|
70
|
+
priv_key = BSV::Primitives::PrivateKey.generate
|
|
71
|
+
|
|
72
|
+
# Derive the public key hash for locking scripts
|
|
73
|
+
pubkey_hash = priv_key.public_key.hash160
|
|
74
|
+
locking_script = BSV::Script::Script.p2pkh_lock(pubkey_hash)
|
|
75
|
+
|
|
76
|
+
# Create a transaction spending a UTXO
|
|
77
|
+
tx = BSV::Transaction::Transaction.new
|
|
78
|
+
|
|
79
|
+
# Add an input referencing a previous transaction output
|
|
80
|
+
input = BSV::Transaction::TransactionInput.new(
|
|
81
|
+
prev_tx_id: source_txid_bytes, # 32-byte binary txid of the UTXO
|
|
82
|
+
prev_tx_out_index: 0
|
|
83
|
+
)
|
|
84
|
+
input.source_satoshis = 100_000
|
|
85
|
+
input.source_locking_script = locking_script
|
|
86
|
+
tx.add_input(input)
|
|
87
|
+
|
|
88
|
+
# Add an output sending to the same address (for demonstration)
|
|
89
|
+
tx.add_output(BSV::Transaction::TransactionOutput.new(
|
|
90
|
+
satoshis: 90_000,
|
|
91
|
+
locking_script: locking_script
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
# Sign the input using the P2PKH template
|
|
95
|
+
template = BSV::Transaction::P2PKH.new(priv_key)
|
|
96
|
+
tx.inputs[0].unlocking_script = template.sign(tx, 0)
|
|
97
|
+
|
|
98
|
+
# The signed transaction is ready to broadcast
|
|
99
|
+
puts tx.to_hex
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Features & Deliverables
|
|
103
|
+
|
|
104
|
+
- **Cryptographic Primitives** — ECDSA signing with RFC 6979 deterministic nonces, Schnorr signatures, ECIES encryption/decryption, Bitcoin Signed Messages. All built on Ruby's stdlib OpenSSL.
|
|
105
|
+
- **Key Management** — BIP-32 HD key derivation, BIP-39 mnemonic generation (12/24-word phrases), WIF import/export, Base58Check encoding/decoding.
|
|
106
|
+
- **Script Layer** — Complete opcode set, script parsing and serialisation, type detection and predicates (`p2pkh?`, `p2pk?`, `p2sh?`, `multisig?`, `op_return?`), data extraction (pubkey hashes, script hashes, addresses), and a fluent builder API.
|
|
107
|
+
- **Script Templates** — Ready-made locking and unlocking script generators for P2PKH, P2PK, P2MS (multisig), and OP_RETURN.
|
|
108
|
+
- **Transaction Construction** — Input/output building, BIP-143 sighash computation (all SIGHASH types with FORKID), P2PKH signing, fee estimation.
|
|
109
|
+
- **SPV Structures** — Merkle path construction and verification, BEEF (Background Evaluation Extended Format) serialisation and deserialisation.
|
|
110
|
+
- **Network Integration** — ARC broadcaster for transaction submission, WhatsOnChain chain provider for UTXO queries and fee rates.
|
|
111
|
+
- **Wallet** — Simple wallet that sources UTXOs, estimates fees, funds and signs transactions.
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
Full documentation is available at **[sgbett.github.io/bsv-ruby-sdk](https://sgbett.github.io/bsv-ruby-sdk/)**.
|
|
116
|
+
|
|
117
|
+
**Guides:**
|
|
118
|
+
|
|
119
|
+
- [Getting Started](https://sgbett.github.io/bsv-ruby-sdk/guides/getting-started/) — installation, first transaction
|
|
120
|
+
- [Primitives](https://sgbett.github.io/bsv-ruby-sdk/guides/primitives/) — keys, signing, encryption, HD keys
|
|
121
|
+
- [Script](https://sgbett.github.io/bsv-ruby-sdk/guides/script/) — construction, templates, detection
|
|
122
|
+
- [Transaction](https://sgbett.github.io/bsv-ruby-sdk/guides/transaction/) — building, signing, BEEF
|
|
123
|
+
|
|
124
|
+
**Additional resources:**
|
|
125
|
+
|
|
126
|
+
- [API Reference](https://sgbett.github.io/bsv-ruby-sdk/reference/) — auto-generated from YARD annotations
|
|
127
|
+
- [spec/ directory](https://github.com/sgbett/bsv-ruby-sdk/tree/master/spec) — runnable usage examples
|
|
128
|
+
- [CHANGELOG](CHANGELOG.md) — release history
|
|
129
|
+
|
|
130
|
+
**Protocol reference:**
|
|
131
|
+
|
|
132
|
+
The [BSV Protocol Documentation](https://hub.bsvblockchain.org/bitcoin-protocol-documentation) on the BSV Hub is the canonical protocol reference — covering transaction format, script opcodes, sighash flags, BEEF/SPV structures, and BRC specifications. The project includes an [MCP](https://modelcontextprotocol.io/) configuration (`.mcp.json`) that connects [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to the hub's search endpoint, giving AI-assisted development sessions direct access to protocol specs during implementation.
|
|
133
|
+
|
|
134
|
+
## Contribution Guidelines
|
|
135
|
+
|
|
136
|
+
Contributions are welcome — bug reports, feature requests, and pull requests.
|
|
137
|
+
|
|
138
|
+
1. **Fork & Clone** — Fork this repository and clone it locally.
|
|
139
|
+
2. **Set Up** — Run `bundle install` to install dependencies.
|
|
140
|
+
3. **Branch** — Create a new branch for your changes.
|
|
141
|
+
4. **Test** — Ensure all specs pass with `bundle exec rake` and lint passes with `bundle exec rubocop`.
|
|
142
|
+
5. **Commit** — Follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages.
|
|
143
|
+
6. **Pull Request** — Open a pull request against `master`.
|
|
144
|
+
|
|
145
|
+
## Support & Contacts
|
|
146
|
+
|
|
147
|
+
Maintainer: Simon Bettison
|
|
148
|
+
|
|
149
|
+
For questions, bug reports, or feature requests, please [open an issue](https://github.com/sgbett/bsv-ruby-sdk/issues) on GitHub.
|
|
150
|
+
|
|
151
|
+
## Licence
|
|
152
|
+
|
|
153
|
+
[Open BSV Licence Version 5](LICENCE)
|
|
154
|
+
|
|
155
|
+
Thank you for being a part of the BSV Blockchain Libraries Project. Let's build the future of BSV Blockchain together!
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Attest
|
|
5
|
+
class Response
|
|
6
|
+
attr_reader :hash, :transaction, :txid
|
|
7
|
+
|
|
8
|
+
def initialize(hash:, transaction:, txid:)
|
|
9
|
+
@hash = hash
|
|
10
|
+
@transaction = transaction
|
|
11
|
+
@txid = txid
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def hash_hex
|
|
15
|
+
@hash.unpack1('H*')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/bsv/attest.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Attest
|
|
5
|
+
autoload :Configuration, 'bsv/attest/configuration'
|
|
6
|
+
autoload :Response, 'bsv/attest/response'
|
|
7
|
+
autoload :VerificationError, 'bsv/attest/verification_error'
|
|
8
|
+
autoload :VERSION, 'bsv/attest/version'
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def configuration
|
|
12
|
+
@configuration ||= Configuration.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def configure
|
|
16
|
+
yield(configuration)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reset_configuration!
|
|
20
|
+
@configuration = Configuration.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def hash(data)
|
|
24
|
+
BSV::Primitives::Digest.sha256(data)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def publish(data, wallet: nil, broadcaster: nil)
|
|
28
|
+
w = wallet || configuration.wallet
|
|
29
|
+
b = broadcaster || configuration.broadcaster
|
|
30
|
+
raise ArgumentError, 'wallet is required' unless w
|
|
31
|
+
raise ArgumentError, 'broadcaster is required' unless b
|
|
32
|
+
|
|
33
|
+
digest = hash(data)
|
|
34
|
+
|
|
35
|
+
script = BSV::Script::Script.op_return(digest)
|
|
36
|
+
output = BSV::Transaction::TransactionOutput.new(satoshis: 0, locking_script: script)
|
|
37
|
+
|
|
38
|
+
tx = BSV::Transaction::Transaction.new
|
|
39
|
+
tx.add_output(output)
|
|
40
|
+
|
|
41
|
+
w.fund_and_sign(tx)
|
|
42
|
+
|
|
43
|
+
broadcast_response = b.broadcast(tx)
|
|
44
|
+
|
|
45
|
+
Response.new(hash: digest, transaction: tx, txid: broadcast_response.txid)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def verify(data, txid, provider: nil)
|
|
49
|
+
p = provider || configuration.provider
|
|
50
|
+
raise ArgumentError, 'provider is required' unless p
|
|
51
|
+
|
|
52
|
+
digest = hash(data)
|
|
53
|
+
|
|
54
|
+
tx = p.fetch_transaction(txid)
|
|
55
|
+
|
|
56
|
+
tx.outputs.each do |output|
|
|
57
|
+
chunks = output.locking_script.chunks
|
|
58
|
+
next if chunks.length < 3
|
|
59
|
+
next unless chunks[0].opcode == BSV::Script::Opcodes::OP_FALSE
|
|
60
|
+
next unless chunks[1].opcode == BSV::Script::Opcodes::OP_RETURN
|
|
61
|
+
|
|
62
|
+
chunks[2..].each do |chunk|
|
|
63
|
+
return true if chunk.data == digest
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
raise VerificationError, 'hash not found in transaction outputs'
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
module BSV
|
|
8
|
+
module Network
|
|
9
|
+
# ARC broadcaster for submitting transactions to the BSV network.
|
|
10
|
+
#
|
|
11
|
+
# Any object responding to #broadcast(tx) can serve as a broadcaster;
|
|
12
|
+
# this class implements that contract using the ARC API.
|
|
13
|
+
#
|
|
14
|
+
# The HTTP client is injectable for testability. It must respond to
|
|
15
|
+
# #request(uri, request) and return an object with #code and #body.
|
|
16
|
+
class ARC
|
|
17
|
+
REJECTED_STATUSES = %w[REJECTED DOUBLE_SPEND_ATTEMPTED].freeze
|
|
18
|
+
|
|
19
|
+
def initialize(url, api_key: nil, http_client: nil)
|
|
20
|
+
@url = url.chomp('/')
|
|
21
|
+
@api_key = api_key
|
|
22
|
+
@http_client = http_client
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Submit a transaction to ARC.
|
|
26
|
+
# Returns BroadcastResponse on success, raises BroadcastError on failure.
|
|
27
|
+
def broadcast(tx)
|
|
28
|
+
uri = URI("#{@url}/v1/tx")
|
|
29
|
+
request = Net::HTTP::Post.new(uri)
|
|
30
|
+
request['Content-Type'] = 'application/octet-stream'
|
|
31
|
+
apply_auth_header(request)
|
|
32
|
+
request.body = tx.to_binary
|
|
33
|
+
|
|
34
|
+
response = execute(uri, request)
|
|
35
|
+
handle_broadcast_response(response)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Query the status of a previously submitted transaction.
|
|
39
|
+
# Returns BroadcastResponse on success, raises BroadcastError on failure.
|
|
40
|
+
def status(txid)
|
|
41
|
+
uri = URI("#{@url}/v1/tx/#{txid}")
|
|
42
|
+
request = Net::HTTP::Get.new(uri)
|
|
43
|
+
apply_auth_header(request)
|
|
44
|
+
|
|
45
|
+
response = execute(uri, request)
|
|
46
|
+
handle_broadcast_response(response)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def apply_auth_header(request)
|
|
52
|
+
request['Authorization'] = "Bearer #{@api_key}" if @api_key
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def execute(uri, request)
|
|
56
|
+
if @http_client
|
|
57
|
+
@http_client.request(uri, request)
|
|
58
|
+
else
|
|
59
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
60
|
+
http.request(request)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def handle_broadcast_response(response)
|
|
66
|
+
body = parse_json(response.body)
|
|
67
|
+
code = response.code.to_i
|
|
68
|
+
|
|
69
|
+
unless (200..299).cover?(code)
|
|
70
|
+
raise BroadcastError.new(
|
|
71
|
+
body['detail'] || body['title'] || "HTTP #{code}",
|
|
72
|
+
status_code: code,
|
|
73
|
+
txid: body['txid']
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
tx_status = body['txStatus']
|
|
78
|
+
if rejected_status?(tx_status)
|
|
79
|
+
raise BroadcastError.new(
|
|
80
|
+
body['detail'] || body['title'] || tx_status,
|
|
81
|
+
status_code: code,
|
|
82
|
+
txid: body['txid']
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
build_response(body)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def rejected_status?(tx_status)
|
|
90
|
+
REJECTED_STATUSES.include?(tx_status)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def parse_json(raw)
|
|
94
|
+
JSON.parse(raw)
|
|
95
|
+
rescue JSON::ParserError
|
|
96
|
+
{ 'detail' => raw }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def build_response(body)
|
|
100
|
+
BroadcastResponse.new(
|
|
101
|
+
txid: body['txid'],
|
|
102
|
+
tx_status: body['txStatus'],
|
|
103
|
+
message: body['title'],
|
|
104
|
+
extra_info: body['extraInfo'],
|
|
105
|
+
block_hash: body['blockHash'],
|
|
106
|
+
block_height: body['blockHeight'],
|
|
107
|
+
timestamp: body['timestamp'],
|
|
108
|
+
competing_txs: body['competingTxs']
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Network
|
|
5
|
+
class BroadcastError < StandardError
|
|
6
|
+
attr_reader :status_code, :txid
|
|
7
|
+
|
|
8
|
+
def initialize(message, status_code: nil, txid: nil)
|
|
9
|
+
@status_code = status_code
|
|
10
|
+
@txid = txid
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Network
|
|
5
|
+
class BroadcastResponse
|
|
6
|
+
attr_reader :txid, :tx_status, :message, :extra_info,
|
|
7
|
+
:block_hash, :block_height, :timestamp, :competing_txs
|
|
8
|
+
|
|
9
|
+
def initialize(attrs = {})
|
|
10
|
+
@txid = attrs[:txid]
|
|
11
|
+
@tx_status = attrs[:tx_status]
|
|
12
|
+
@message = attrs[:message]
|
|
13
|
+
@extra_info = attrs[:extra_info]
|
|
14
|
+
@block_hash = attrs[:block_hash]
|
|
15
|
+
@block_height = attrs[:block_height]
|
|
16
|
+
@timestamp = attrs[:timestamp]
|
|
17
|
+
@competing_txs = attrs[:competing_txs]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def success?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def mined?
|
|
25
|
+
tx_status == 'MINED'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BSV
|
|
4
|
+
module Network
|
|
5
|
+
class UTXO
|
|
6
|
+
attr_reader :tx_hash, :tx_pos, :satoshis, :height
|
|
7
|
+
|
|
8
|
+
def initialize(tx_hash:, tx_pos:, satoshis:, height: nil)
|
|
9
|
+
@tx_hash = tx_hash
|
|
10
|
+
@tx_pos = tx_pos
|
|
11
|
+
@satoshis = satoshis
|
|
12
|
+
@height = height
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
other.is_a?(self.class) &&
|
|
17
|
+
tx_hash == other.tx_hash &&
|
|
18
|
+
tx_pos == other.tx_pos
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias eql? ==
|
|
22
|
+
|
|
23
|
+
def hash
|
|
24
|
+
[tx_hash, tx_pos].hash
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|