hive-ruby 1.0.5 → 1.0.6
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/.gitignore +4 -0
- data/CLAUDE.md +148 -0
- data/README.md +26 -0
- data/Rakefile +37 -24
- data/hive-ruby.gemspec +3 -3
- data/lib/hive/api.rb +16 -1
- data/lib/hive/broadcast.rb +4 -0
- data/lib/hive/compact_signer.rb +54 -0
- data/lib/hive/rpc/http_client.rb +7 -0
- data/lib/hive/rpc/thread_safe_http_client.rb +5 -12
- data/lib/hive/signing_key.rb +29 -0
- data/lib/hive/transaction_builder.rb +71 -31
- data/lib/hive/version.rb +1 -1
- data/lib/hive.rb +2 -0
- metadata +60 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4efaff9ab4047a72ffdb8c232fac4ae19c33bf09f0e9b8a4319f57f06f95f9f
|
|
4
|
+
data.tar.gz: 3b0c7290c5ffceab0c05436bd7d87ae272e057ae54e90dfc7134a9591b7c94ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d32d534b7ecb4e9072c6293f7f1de0d20f29be63358bb05d5abec5e26c414d8572225b7d770f6c8161c448a649c85522e7b47a5bd1ba3517960b988b2907ffbb
|
|
7
|
+
data.tar.gz: ccc1d25b6bf658df0d604c049e7b20b182412b38210e6ee36915e66c7647ee3788af810cb546b56f85b49a1652875ae4478a414078ad38b4ee34e3c24c82bffb
|
data/.gitignore
CHANGED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# hive-ruby
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
Ruby API client library for the Hive blockchain. Complete implementation supporting transaction broadcasting, streaming, and all blockchain operations. Version 1.0.5 (HF27 compatible).
|
|
6
|
+
|
|
7
|
+
Key features:
|
|
8
|
+
- Full Ruby API for Hive blockchain interaction
|
|
9
|
+
- Transaction broadcasting with multisignature support
|
|
10
|
+
- Streaming for blocks, transactions, and operations (including virtual ops)
|
|
11
|
+
- JSON-RPC batch request support
|
|
12
|
+
- Thread-safe HTTP client for concurrent requests
|
|
13
|
+
- Dynamic API discovery from Hive nodes
|
|
14
|
+
|
|
15
|
+
## Tech Stack
|
|
16
|
+
|
|
17
|
+
- **Ruby**: 2.2.5+ (tested with 2.7+ and 3.0+)
|
|
18
|
+
- **Core Dependencies**:
|
|
19
|
+
- `json` (~> 2.1) - JSON serialization
|
|
20
|
+
- `logging` (~> 2.2) - Logging framework
|
|
21
|
+
- `hashie` (>= 3.5) - Hash manipulation
|
|
22
|
+
- `bitcoin-ruby` (0.0.20) - Cryptographic signing
|
|
23
|
+
- `ffi` (~> 1.9) - Foreign Function Interface
|
|
24
|
+
- `bindata` (~> 2.4) - Binary data parsing
|
|
25
|
+
- `base58` (~> 0.2) - Base58 encoding
|
|
26
|
+
- **Testing**: minitest, webmock, vcr, simplecov
|
|
27
|
+
- **Documentation**: yard
|
|
28
|
+
|
|
29
|
+
## Directory Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
lib/hive/
|
|
33
|
+
├── hive.rb # Main entry, requires all modules
|
|
34
|
+
├── version.rb # Version constant
|
|
35
|
+
├── api.rb # Base API with dynamic namespace handling
|
|
36
|
+
├── jsonrpc.rb # JSON-RPC client wrapper
|
|
37
|
+
├── broadcast.rb # Transaction broadcasting (largest file)
|
|
38
|
+
├── transaction_builder.rb # Transaction creation and signing
|
|
39
|
+
├── stream.rb # Streaming blocks/transactions/operations
|
|
40
|
+
├── operation.rb # Operation type definitions + virtual ops
|
|
41
|
+
├── operation/ # Individual operation classes (35 files)
|
|
42
|
+
├── mixins/ # Shared functionality (jsonable, serializable, retriable)
|
|
43
|
+
├── rpc/ # HTTP/RPC client implementations
|
|
44
|
+
└── type/ # Custom type classes (amount, etc.)
|
|
45
|
+
|
|
46
|
+
test/
|
|
47
|
+
├── test_helper.rb # Test configuration
|
|
48
|
+
├── hive/ # Test files (20+ files)
|
|
49
|
+
└── fixtures/vcr_cassettes/ # Recorded HTTP interactions
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Development Commands
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Install dependencies
|
|
56
|
+
bundle install
|
|
57
|
+
|
|
58
|
+
# Run tests
|
|
59
|
+
bundle exec rake test # All tests (default)
|
|
60
|
+
bundle exec rake test:static # Static tests (read-only, mocked)
|
|
61
|
+
bundle exec rake test:broadcast # Broadcast tests (signing validation)
|
|
62
|
+
bundle exec rake test:testnet # Real testnet broadcasts
|
|
63
|
+
bundle exec rake test:threads # Thread safety verification
|
|
64
|
+
|
|
65
|
+
# Streaming tasks
|
|
66
|
+
bundle exec rake stream:block_range
|
|
67
|
+
bundle exec rake stream:op_range
|
|
68
|
+
bundle exec rake stream:vop_range
|
|
69
|
+
|
|
70
|
+
# Utilities
|
|
71
|
+
bundle exec rake console # IRB with hive loaded
|
|
72
|
+
bundle exec rake yard # Generate documentation
|
|
73
|
+
bundle exec rake clean:vcr # Remove VCR cassettes
|
|
74
|
+
bundle exec rake show:apis # Display available APIs
|
|
75
|
+
bundle exec rake show:methods[api] # Show methods for specific API
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Environment Variables**:
|
|
79
|
+
- `TEST_NODE=<url>` - Custom node (default: mainnet)
|
|
80
|
+
- `VCR_RECORD_MODE=<mode>` - VCR recording mode
|
|
81
|
+
- `TEST_ACCOUNT_NAME=<name>` - Test account (default: `social`)
|
|
82
|
+
- `TEST_WIF=<key>` - Test account private key
|
|
83
|
+
- `HIVE_CHAIN_ID=<id>` - Override chain ID
|
|
84
|
+
|
|
85
|
+
## Key Files
|
|
86
|
+
|
|
87
|
+
- `lib/hive.rb` - Main entry point
|
|
88
|
+
- `lib/hive/api.rb` - Base API class with dynamic method generation
|
|
89
|
+
- `lib/hive/broadcast.rb` - High-level operation helpers
|
|
90
|
+
- `lib/hive/transaction_builder.rb` - Transaction creation/signing
|
|
91
|
+
- `lib/hive/stream.rb` - Block/transaction streaming
|
|
92
|
+
- `lib/hive/operation.rb` - Operation type definitions
|
|
93
|
+
- `hive-ruby.gemspec` - Gem specification
|
|
94
|
+
- `Rakefile` - Task definitions
|
|
95
|
+
- `test/test_helper.rb` - Test configuration
|
|
96
|
+
|
|
97
|
+
## Coding Conventions
|
|
98
|
+
|
|
99
|
+
- All code in `module Hive` namespace
|
|
100
|
+
- Class names: CamelCase (`TransactionBuilder`, `DatabaseApi`)
|
|
101
|
+
- Method names: snake_case
|
|
102
|
+
- Constants: SCREAMING_SNAKE_CASE
|
|
103
|
+
- 2-space indentation
|
|
104
|
+
- Symbol-based hash keys for internal APIs
|
|
105
|
+
- Block-based callbacks for responses: `api.method { |result| ... }`
|
|
106
|
+
|
|
107
|
+
**Error Handling**:
|
|
108
|
+
- Custom exception hierarchy from `BaseError`
|
|
109
|
+
- Specific types: `RemoteDatabaseLockError`, `RemoteServerError`, `PluginNotEnabledError`
|
|
110
|
+
|
|
111
|
+
**Testing**:
|
|
112
|
+
- Minitest with VCR for HTTP recording/replay
|
|
113
|
+
- Cassettes in `test/fixtures/vcr_cassettes/`
|
|
114
|
+
- Thread safety tests via `minitest-hell`
|
|
115
|
+
|
|
116
|
+
## CI/CD Notes
|
|
117
|
+
|
|
118
|
+
**GitLab CI** (`.gitlab-ci.yml`):
|
|
119
|
+
- Image: `ruby:2.6.1-alpine`
|
|
120
|
+
- Single stage: `pages` (documentation)
|
|
121
|
+
- Generates YARD docs, publishes to GitLab Pages
|
|
122
|
+
- Only runs on `master` branch
|
|
123
|
+
- Caches `vendor/` directory
|
|
124
|
+
|
|
125
|
+
**No automated test pipeline** - tests run locally. CI only handles documentation deployment.
|
|
126
|
+
|
|
127
|
+
## API Usage Examples
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Basic API call
|
|
131
|
+
api = Hive::DatabaseApi.new
|
|
132
|
+
api.get_dynamic_global_properties { |result| puts result }
|
|
133
|
+
|
|
134
|
+
# Streaming
|
|
135
|
+
stream = Hive::Stream.new
|
|
136
|
+
stream.blocks { |block| process(block) }
|
|
137
|
+
stream.operations(:vote) { |op| handle_vote(op) }
|
|
138
|
+
|
|
139
|
+
# Broadcasting
|
|
140
|
+
Hive::Broadcast.vote(wif: 'WIF_KEY', voter: 'user', author: 'author', permlink: 'post', weight: 10000)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Notes
|
|
144
|
+
|
|
145
|
+
- Uses `const_missing` for dynamic API class generation
|
|
146
|
+
- 60+ operation types supported
|
|
147
|
+
- VCR cassettes excluded from gem package but committed to repo
|
|
148
|
+
- Thread-safe by default (`ThreadSafeHttpClient`)
|
data/README.md
CHANGED
|
@@ -49,6 +49,32 @@ To add the gem as a dependency to your project with [Bundler](http://bundler.io/
|
|
|
49
49
|
gem 'hive-ruby', require: 'hive'
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Signing backend notes
|
|
53
|
+
|
|
54
|
+
`hive-ruby` signs transactions with a `libsecp256k1` backend through `rbsecp256k1`. This avoids Ruby/OpenSSL EC mutation behavior that can break older `bitcoin-ruby` signing paths on Ruby/OpenSSL 3 platforms.
|
|
55
|
+
|
|
56
|
+
If signing fails while installing or deploying, check the native build prerequisites first:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
bundle install
|
|
60
|
+
bundle exec ruby -rrbsecp256k1 -e 'puts Secp256k1.have_recovery?'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The command should print `true`. If the `rbsecp256k1` native extension cannot build, make sure normal native Ruby build tools are available. On macOS, the bundled `libsecp256k1` build may need Automake/Autoconf tooling, e.g.:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
brew install automake autoconf
|
|
67
|
+
bundle pristine rbsecp256k1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For downstream projects comparing deployment strategies:
|
|
71
|
+
|
|
72
|
+
* **Default path:** use the bundled `rbsecp256k1` signer. This is the expected path for modern Ruby/OpenSSL environments.
|
|
73
|
+
* **Legacy fallback:** set `HIVE_USE_LEGACY_BITCOIN_RUBY_SIGNER=1` to try the old `bitcoin-ruby` signer path. This is mainly useful for comparison or troubleshooting and may still fail on OpenSSL 3.
|
|
74
|
+
* **Fork strategy:** if a downstream project carries a patched `bitcoin-ruby` fork, test it behind the legacy fallback first. If it proves reliable, compare its output against the default `rbsecp256k1` path before relying on it in production.
|
|
75
|
+
|
|
76
|
+
`bitcoin-ruby` is still present as a dependency in v1, so projects should not assume this release removes all of its transitive dependencies. The important change is that normal transaction signing no longer depends on `bitcoin-ruby`'s OpenSSL EC signing internals.
|
|
77
|
+
|
|
52
78
|
## Examples
|
|
53
79
|
|
|
54
80
|
### Broadcast Vote
|
data/Rakefile
CHANGED
|
@@ -3,19 +3,28 @@ require 'rake/testtask'
|
|
|
3
3
|
require 'yard'
|
|
4
4
|
require 'hive'
|
|
5
5
|
|
|
6
|
-
Rake::TestTask.new(
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.description = 'Run tests with hell-mode disabled by default; opt in with HELL_ENABLED=1 or rake test:hell'
|
|
7
8
|
t.libs << 'test'
|
|
8
9
|
t.libs << 'lib'
|
|
9
10
|
t.test_files = FileList['test/**/*_test.rb']
|
|
10
|
-
t.ruby_opts <<
|
|
11
|
-
'-W2'
|
|
12
|
-
else
|
|
13
|
-
'-W1'
|
|
14
|
-
end
|
|
11
|
+
t.ruby_opts << '-W1'
|
|
15
12
|
end
|
|
16
13
|
|
|
17
14
|
namespace :test do
|
|
18
|
-
Rake::TestTask.new(
|
|
15
|
+
Rake::TestTask.new(:hell) do |t|
|
|
16
|
+
t.description = <<-EOD
|
|
17
|
+
Run the full suite with hell-mode enabled (parallelized/minitest-hell stress mode).
|
|
18
|
+
This is opt-in during v1 while OpenSSL 3 signing remains quarantined.
|
|
19
|
+
EOD
|
|
20
|
+
t.libs << 'test'
|
|
21
|
+
t.libs << 'lib'
|
|
22
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
23
|
+
t.ruby_opts << '-eHELL_ENABLED=1'
|
|
24
|
+
t.ruby_opts << '-W2'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Rake::TestTask.new(:static) do |t|
|
|
19
28
|
t.description = <<-EOD
|
|
20
29
|
Run static tests, which are those that have static request/responses.
|
|
21
30
|
These are tests that are typically read-only and do not require heavy
|
|
@@ -35,14 +44,10 @@ namespace :test do
|
|
|
35
44
|
'test/hive/tags_api_test.rb',
|
|
36
45
|
'test/hive/witness_api_test.rb'
|
|
37
46
|
]
|
|
38
|
-
t.ruby_opts <<
|
|
39
|
-
'-W2'
|
|
40
|
-
else
|
|
41
|
-
'-W1'
|
|
42
|
-
end
|
|
47
|
+
t.ruby_opts << '-W1'
|
|
43
48
|
end
|
|
44
49
|
|
|
45
|
-
Rake::TestTask.new(broadcast
|
|
50
|
+
Rake::TestTask.new(:broadcast) do |t|
|
|
46
51
|
t.description = <<-EOD
|
|
47
52
|
Run broadcast tests, which are those that only use network_broadcast_api
|
|
48
53
|
and/or database_api.verify_authority (pretend: true).
|
|
@@ -53,14 +58,26 @@ namespace :test do
|
|
|
53
58
|
'test/hive/broadcast_test.rb',
|
|
54
59
|
'test/hive/transaction_builder_test.rb'
|
|
55
60
|
]
|
|
56
|
-
t.ruby_opts <<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
t.ruby_opts << '-W1'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
Rake::TestTask.new(:broadcast_openssl3) do |t|
|
|
65
|
+
t.description = <<-EOD
|
|
66
|
+
Run broadcast/signing tests with OpenSSL 3 signing failures explicitly
|
|
67
|
+
quarantined behind SKIP_OPENSSL3_SIGNING=1. This is a deliberate fake-
|
|
68
|
+
green v1 containment task, not the default truth of the suite.
|
|
69
|
+
EOD
|
|
70
|
+
t.libs << 'test'
|
|
71
|
+
t.libs << 'lib'
|
|
72
|
+
t.test_files = [
|
|
73
|
+
'test/hive/broadcast_test.rb',
|
|
74
|
+
'test/hive/transaction_builder_test.rb'
|
|
75
|
+
]
|
|
76
|
+
t.ruby_opts << '-eSKIP_OPENSSL3_SIGNING=1'
|
|
77
|
+
t.ruby_opts << '-W1'
|
|
61
78
|
end
|
|
62
79
|
|
|
63
|
-
Rake::TestTask.new(testnet
|
|
80
|
+
Rake::TestTask.new(:testnet) do |t|
|
|
64
81
|
t.description = <<-EOD
|
|
65
82
|
Run testnet tests, which are those that use network_broadcast_api to do
|
|
66
83
|
actual broadcast operations, on a specified (or default) testnet.
|
|
@@ -70,11 +87,7 @@ namespace :test do
|
|
|
70
87
|
t.test_files = [
|
|
71
88
|
'test/hive/testnet_test.rb'
|
|
72
89
|
]
|
|
73
|
-
t.ruby_opts <<
|
|
74
|
-
'-W2'
|
|
75
|
-
else
|
|
76
|
-
'-W1'
|
|
77
|
-
end
|
|
90
|
+
t.ruby_opts << '-W1'
|
|
78
91
|
end
|
|
79
92
|
|
|
80
93
|
desc 'Tests the API using multiple threads.'
|
data/hive-ruby.gemspec
CHANGED
|
@@ -18,8 +18,8 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.require_paths = ['lib']
|
|
19
19
|
|
|
20
20
|
spec.add_development_dependency 'bundler', '~> 2.1', '>= 2.1.4'
|
|
21
|
-
spec.add_development_dependency 'rake', '~> 13.0
|
|
22
|
-
spec.add_development_dependency 'minitest', '
|
|
21
|
+
spec.add_development_dependency 'rake', '~> 13.0', '>= 12.3.0'
|
|
22
|
+
spec.add_development_dependency 'minitest', '>= 5.10.3', '< 7'
|
|
23
23
|
spec.add_development_dependency 'minitest-line', '~> 0.6', '>= 0.6.4'
|
|
24
24
|
spec.add_development_dependency 'minitest-proveit', '~> 1.0', '>= 1.0.0'
|
|
25
25
|
spec.add_development_dependency 'webmock', '~> 3.16', '>= 3.16.0'
|
|
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
spec.add_dependency 'logging', '~> 2.2', '>= 2.2.0'
|
|
35
35
|
spec.add_dependency 'hashie', '>= 3.5'
|
|
36
36
|
spec.add_dependency 'bitcoin-ruby', '~> 0.0', '0.0.20'
|
|
37
|
-
spec.add_dependency '
|
|
37
|
+
spec.add_dependency 'rbsecp256k1', '~> 6.0', '>= 6.0.0'
|
|
38
38
|
spec.add_dependency 'bindata', '~> 2.4', '>= 2.4.4'
|
|
39
39
|
spec.add_dependency 'base58', '~> 0.2', '>= 0.2.3'
|
|
40
40
|
end
|
data/lib/hive/api.rb
CHANGED
|
@@ -162,7 +162,9 @@ module Hive
|
|
|
162
162
|
@@signatures[url][rpc_method_name] ||= begin
|
|
163
163
|
Api::jsonrpc(url).get_signature(method: rpc_method_name).result
|
|
164
164
|
rescue => e
|
|
165
|
-
|
|
165
|
+
fallback_signatures = Fallback::API_METHOD_SIGNATURES[@api_name] || {}
|
|
166
|
+
fallback_args = fallback_signatures[rpc_method_name.split('.').last.to_sym] || {}
|
|
167
|
+
Hashie::Mash.new({args: fallback_args})
|
|
166
168
|
end
|
|
167
169
|
end
|
|
168
170
|
|
|
@@ -219,6 +221,19 @@ module Hive
|
|
|
219
221
|
response = rpc_client.rpc_execute(@api_name, m, rpc_args)
|
|
220
222
|
|
|
221
223
|
if !!block
|
|
224
|
+
if ENV['HIVE_DEBUG_RPC_FLOW'] == 'true'
|
|
225
|
+
debug_result = case response
|
|
226
|
+
when Hashie::Mash then response.result
|
|
227
|
+
else response
|
|
228
|
+
end
|
|
229
|
+
preview = begin
|
|
230
|
+
debug_result.inspect
|
|
231
|
+
rescue
|
|
232
|
+
debug_result.to_s
|
|
233
|
+
end
|
|
234
|
+
@error_pipe.puts "RPC #{@api_name}.#{m} -> #{debug_result.class}: #{preview[0,200]}"
|
|
235
|
+
end
|
|
236
|
+
|
|
222
237
|
case response
|
|
223
238
|
when Hashie::Mash then yield response.result, response.error, response.id
|
|
224
239
|
when Hashie::Array
|
data/lib/hive/broadcast.rb
CHANGED
|
@@ -1371,6 +1371,10 @@ module Hive
|
|
|
1371
1371
|
tx.operations = ops
|
|
1372
1372
|
trx = tx.transaction
|
|
1373
1373
|
|
|
1374
|
+
unless trx.is_a?(Transaction)
|
|
1375
|
+
raise Hive::ArgumentError, "Expected Hive::Transaction from TransactionBuilder, got #{trx.class}"
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1374
1378
|
response = if !!options[:pretend]
|
|
1375
1379
|
if !!options[:app_base]
|
|
1376
1380
|
database_api(options).verify_authority(trx: trx)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Hive
|
|
2
|
+
class CompactSigner
|
|
3
|
+
class Rbsecp256k1
|
|
4
|
+
HEADER_BASE = 27
|
|
5
|
+
COMPRESSED_FLAG = 4
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
require 'rbsecp256k1'
|
|
9
|
+
|
|
10
|
+
unless Secp256k1.have_recovery?
|
|
11
|
+
raise Hive::BaseError, 'rbsecp256k1 was built without recoverable signature support'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@context = Secp256k1::Context.create
|
|
15
|
+
rescue LoadError => e
|
|
16
|
+
raise Hive::BaseError, "rbsecp256k1 is not available: #{e.message}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def sign_compact(digest32, private_key_hex, public_key_hex = nil, compressed = false)
|
|
20
|
+
private_key = Secp256k1::PrivateKey.from_data([private_key_hex].pack('H*'))
|
|
21
|
+
signature = @context.sign_recoverable(private_key, digest32)
|
|
22
|
+
compact_signature, recovery_id = signature.compact
|
|
23
|
+
header = HEADER_BASE + recovery_id + (compressed ? COMPRESSED_FLAG : 0)
|
|
24
|
+
compact = [header].pack('C') + compact_signature
|
|
25
|
+
|
|
26
|
+
if public_key_hex && public_key_hex != recover_compact(digest32, compact)
|
|
27
|
+
raise Hive::BaseError, 'Compact signature recovered unexpected public key'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
compact
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def recover_compact(digest32, compact_signature)
|
|
34
|
+
header = compact_signature.bytes.first
|
|
35
|
+
recovery_id = header - HEADER_BASE
|
|
36
|
+
recovery_id -= COMPRESSED_FLAG if recovery_id >= COMPRESSED_FLAG
|
|
37
|
+
signature = @context.recoverable_signature_from_compact(compact_signature.byteslice(1, 64), recovery_id)
|
|
38
|
+
|
|
39
|
+
signature.recover_public_key(digest32).uncompressed.unpack1('H*')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def public_key(private_key_hex, compressed = false)
|
|
43
|
+
keypair = @context.key_pair_from_private_key([private_key_hex].pack('H*'))
|
|
44
|
+
public_key = keypair.public_key
|
|
45
|
+
|
|
46
|
+
(compressed ? public_key.compressed : public_key.uncompressed).unpack1('H*')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.default
|
|
51
|
+
@default ||= Rbsecp256k1.new
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/hive/rpc/http_client.rb
CHANGED
|
@@ -82,6 +82,10 @@ module Hive
|
|
|
82
82
|
else
|
|
83
83
|
request_object
|
|
84
84
|
end.to_json
|
|
85
|
+
|
|
86
|
+
if ENV['HIVE_DEBUG_RPC_HTTP'] == 'true'
|
|
87
|
+
@error_pipe.puts "HTTP RPC request #{api_name}.#{api_method}: #{request.body[0,300]}"
|
|
88
|
+
end
|
|
85
89
|
|
|
86
90
|
response = catch :http_request do; begin; http_request(request)
|
|
87
91
|
rescue *TIMEOUT_ERRORS => e
|
|
@@ -94,6 +98,9 @@ module Hive
|
|
|
94
98
|
|
|
95
99
|
case response.code
|
|
96
100
|
when '200'
|
|
101
|
+
if ENV['HIVE_DEBUG_RPC_HTTP'] == 'true'
|
|
102
|
+
@error_pipe.puts "HTTP RPC response #{api_name}.#{api_method}: #{response.body[0,300]}"
|
|
103
|
+
end
|
|
97
104
|
response = catch :parse_json do; begin; JSON[response.body]
|
|
98
105
|
rescue *TIMEOUT_ERRORS => e
|
|
99
106
|
throw retry_timeout(:parse_json, e)
|
|
@@ -10,20 +10,13 @@ module Hive
|
|
|
10
10
|
class ThreadSafeHttpClient < HttpClient
|
|
11
11
|
SEMAPHORE = Mutex.new.freeze
|
|
12
12
|
|
|
13
|
-
# Same as #{HttpClient#http_post}, but
|
|
14
|
-
#
|
|
13
|
+
# Same as #{HttpClient#http_post}, but returns a fresh request object.
|
|
14
|
+
# Reusing mutable Net::HTTP::Post instances across nested/retried calls can
|
|
15
|
+
# leak request bodies between rpc executions.
|
|
15
16
|
def http_post(api_name)
|
|
16
17
|
raise "Namespace required." if api_name.nil?
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
http_posts = thread.thread_variable_get(:http_posts) || {}
|
|
20
|
-
|
|
21
|
-
SEMAPHORE.synchronize do
|
|
22
|
-
http_posts[[uri, api_name]] ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
|
|
23
|
-
thread.thread_variable_set(:http_posts, http_posts)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
http_posts[[uri, api_name]]
|
|
18
|
+
|
|
19
|
+
Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
|
|
27
20
|
end
|
|
28
21
|
|
|
29
22
|
def http_request(request); SEMAPHORE.synchronize{super}; end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Hive
|
|
2
|
+
class SigningKey
|
|
3
|
+
PRIVATE_KEY_VERSION = '80'
|
|
4
|
+
COMPRESSED_FLAG = '01'
|
|
5
|
+
|
|
6
|
+
attr_reader :private_key_hex, :compressed
|
|
7
|
+
|
|
8
|
+
def self.from_base58(wif)
|
|
9
|
+
new(wif)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(wif)
|
|
13
|
+
hex = Bitcoin.decode_base58(wif)
|
|
14
|
+
compressed = hex.size == 76
|
|
15
|
+
version, private_key_hex, flag, checksum = hex.unpack("a2a64a#{compressed ? 2 : 0}a8")
|
|
16
|
+
|
|
17
|
+
raise Hive::ArgumentError, 'Invalid private key version' unless version == PRIVATE_KEY_VERSION
|
|
18
|
+
raise Hive::ArgumentError, 'Invalid compressed private key flag' if compressed && flag != COMPRESSED_FLAG
|
|
19
|
+
raise Hive::ArgumentError, 'Invalid private key checksum' unless Bitcoin.checksum(version + private_key_hex + flag) == checksum
|
|
20
|
+
|
|
21
|
+
@private_key_hex = private_key_hex
|
|
22
|
+
@compressed = compressed
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def pub
|
|
26
|
+
CompactSigner.default.public_key(private_key_hex, compressed)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -26,7 +26,9 @@ module Hive
|
|
|
26
26
|
include ChainConfig
|
|
27
27
|
include Utils
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
MAX_CANONICAL_SIGNATURE_ATTEMPTS = 100
|
|
30
|
+
|
|
31
|
+
attr_accessor :app_base, :database_api, :block_api, :operations
|
|
30
32
|
attr_writer :wif
|
|
31
33
|
attr_reader :signed, :testnet, :force_serialize
|
|
32
34
|
|
|
@@ -99,6 +101,15 @@ module Hive
|
|
|
99
101
|
|
|
100
102
|
self
|
|
101
103
|
end
|
|
104
|
+
|
|
105
|
+
def expiration
|
|
106
|
+
@trx.expiration
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def expiration=(value)
|
|
110
|
+
@trx.expiration = value
|
|
111
|
+
@signed = false
|
|
112
|
+
end
|
|
102
113
|
|
|
103
114
|
# If the transaction can be prepared, this method will do so and set the
|
|
104
115
|
# expiration. Once the expiration is set, it will not re-prepare. If you
|
|
@@ -109,37 +120,48 @@ module Hive
|
|
|
109
120
|
# @return {TransactionBuilder}
|
|
110
121
|
def prepare
|
|
111
122
|
if @trx.expired?
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
loop do
|
|
124
|
+
begin
|
|
125
|
+
properties = nil
|
|
126
|
+
header = nil
|
|
127
|
+
block_number = nil
|
|
128
|
+
|
|
129
|
+
@database_api.get_dynamic_global_properties do |result|
|
|
130
|
+
properties = result
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
|
|
114
134
|
block_number = properties.last_irreversible_block_num
|
|
115
135
|
block_header_args = if app_base?
|
|
116
136
|
{block_num: block_number}
|
|
117
137
|
else
|
|
118
138
|
block_number
|
|
119
139
|
end
|
|
120
|
-
|
|
140
|
+
|
|
121
141
|
@block_api.get_block_header(block_header_args) do |result|
|
|
122
142
|
header = if app_base?
|
|
123
143
|
result.header
|
|
124
144
|
else
|
|
125
145
|
result
|
|
126
146
|
end
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
@trx.ref_block_num = (block_number - 1) & 0xFFFF
|
|
151
|
+
@trx.ref_block_prefix = unhexlify(header.previous[8..-1]).unpack('V*')[0]
|
|
152
|
+
@trx.expiration = (Time.parse(properties.time + 'Z') + EXPIRE_IN_SECS).utc
|
|
153
|
+
break
|
|
154
|
+
rescue => e
|
|
155
|
+
if can_retry? e
|
|
156
|
+
@error_pipe.puts "#{e} ... retrying."
|
|
157
|
+
next
|
|
158
|
+
else
|
|
159
|
+
raise e
|
|
131
160
|
end
|
|
132
161
|
end
|
|
133
|
-
|
|
134
|
-
if can_retry? e
|
|
135
|
-
@error_pipe.puts "#{e} ... retrying."
|
|
136
|
-
throw :prepare_header
|
|
137
|
-
else
|
|
138
|
-
raise e
|
|
139
|
-
end
|
|
140
|
-
end; end
|
|
162
|
+
end
|
|
141
163
|
end
|
|
142
|
-
|
|
164
|
+
|
|
143
165
|
self
|
|
144
166
|
end
|
|
145
167
|
|
|
@@ -213,10 +235,12 @@ module Hive
|
|
|
213
235
|
# Appends to the `signatures` array of the transaction, built from a
|
|
214
236
|
# serialized digest.
|
|
215
237
|
#
|
|
216
|
-
# @return
|
|
238
|
+
# @return [Transaction] The transaction payload. Even when signing is skipped
|
|
239
|
+
# (for example due to missing wif or an expired transaction), callers expect
|
|
240
|
+
# a concrete transaction object rather than the builder instance itself.
|
|
217
241
|
def sign
|
|
218
|
-
return
|
|
219
|
-
return
|
|
242
|
+
return @trx if @wif.empty?
|
|
243
|
+
return @trx if @trx.expired?
|
|
220
244
|
|
|
221
245
|
unless @signed
|
|
222
246
|
catch :serialize do; begin
|
|
@@ -255,21 +279,37 @@ module Hive
|
|
|
255
279
|
hex = @chain_id + hex
|
|
256
280
|
digest = unhexlify(hex)
|
|
257
281
|
digest_hex = Digest::SHA256.digest(digest)
|
|
258
|
-
|
|
259
|
-
|
|
282
|
+
legacy_bitcoin_ruby_signer = ENV['HIVE_USE_LEGACY_BITCOIN_RUBY_SIGNER'] == '1'
|
|
283
|
+
private_keys = @wif.map do |wif|
|
|
284
|
+
if legacy_bitcoin_ruby_signer
|
|
285
|
+
Bitcoin::Key.from_base58(wif)
|
|
286
|
+
else
|
|
287
|
+
SigningKey.from_base58(wif)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
ec = legacy_bitcoin_ruby_signer ? Bitcoin::OpenSSL_EC : CompactSigner.default
|
|
260
291
|
count = 0
|
|
261
292
|
|
|
262
293
|
private_keys.each do |private_key|
|
|
263
294
|
sig = nil
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
sig = ec.sign_compact(digest_hex,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
295
|
+
public_key_hex = private_key.pub
|
|
296
|
+
private_key_hex = private_key.respond_to?(:private_key_hex) ? private_key.private_key_hex : private_key.priv
|
|
297
|
+
compressed = private_key.respond_to?(:compressed) ? private_key.compressed : false
|
|
298
|
+
|
|
299
|
+
unless legacy_bitcoin_ruby_signer
|
|
300
|
+
sig = ec.sign_compact(digest_hex, private_key_hex, public_key_hex, compressed)
|
|
301
|
+
else
|
|
302
|
+
loop do
|
|
303
|
+
count += 1
|
|
304
|
+
@error_pipe.puts "#{count} attempts to find canonical signature" if count % 40 == 0
|
|
305
|
+
sig = ec.sign_compact(digest_hex, private_key_hex, public_key_hex, compressed)
|
|
306
|
+
|
|
307
|
+
break if public_key_hex == ec.recover_compact(digest_hex, sig) && canonical?(sig)
|
|
308
|
+
|
|
309
|
+
if count >= MAX_CANONICAL_SIGNATURE_ATTEMPTS
|
|
310
|
+
raise Hive::BaseError, "Unable to find canonical signature after #{MAX_CANONICAL_SIGNATURE_ATTEMPTS} attempts"
|
|
311
|
+
end
|
|
312
|
+
end
|
|
273
313
|
end
|
|
274
314
|
|
|
275
315
|
@trx.signatures << hexlify(sig)
|
data/lib/hive/version.rb
CHANGED
data/lib/hive.rb
CHANGED
|
@@ -62,6 +62,8 @@ require 'hive/operation/witness_update.rb'
|
|
|
62
62
|
require 'hive/operation/witness_set_properties.rb'
|
|
63
63
|
require 'hive/marshal'
|
|
64
64
|
require 'hive/transaction'
|
|
65
|
+
require 'hive/signing_key'
|
|
66
|
+
require 'hive/compact_signer'
|
|
65
67
|
require 'hive/transaction_builder'
|
|
66
68
|
require 'hive/rpc/base_client'
|
|
67
69
|
require 'hive/rpc/http_client'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hive-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anthony Martin
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -34,22 +34,22 @@ dependencies:
|
|
|
34
34
|
name: rake
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
37
40
|
- - ">="
|
|
38
41
|
- !ruby/object:Gem::Version
|
|
39
42
|
version: 12.3.0
|
|
40
|
-
- - "~>"
|
|
41
|
-
- !ruby/object:Gem::Version
|
|
42
|
-
version: 13.0.1
|
|
43
43
|
type: :development
|
|
44
44
|
prerelease: false
|
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
46
|
requirements:
|
|
47
|
+
- - "~>"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '13.0'
|
|
47
50
|
- - ">="
|
|
48
51
|
- !ruby/object:Gem::Version
|
|
49
52
|
version: 12.3.0
|
|
50
|
-
- - "~>"
|
|
51
|
-
- !ruby/object:Gem::Version
|
|
52
|
-
version: 13.0.1
|
|
53
53
|
- !ruby/object:Gem::Dependency
|
|
54
54
|
name: minitest
|
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,9 +57,9 @@ dependencies:
|
|
|
57
57
|
- - ">="
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
59
|
version: 5.10.3
|
|
60
|
-
- - "
|
|
60
|
+
- - "<"
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '
|
|
62
|
+
version: '7'
|
|
63
63
|
type: :development
|
|
64
64
|
prerelease: false
|
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -67,9 +67,9 @@ dependencies:
|
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
69
|
version: 5.10.3
|
|
70
|
-
- - "
|
|
70
|
+
- - "<"
|
|
71
71
|
- !ruby/object:Gem::Version
|
|
72
|
-
version: '
|
|
72
|
+
version: '7'
|
|
73
73
|
- !ruby/object:Gem::Dependency
|
|
74
74
|
name: minitest-line
|
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,42 +94,42 @@ dependencies:
|
|
|
94
94
|
name: minitest-proveit
|
|
95
95
|
requirement: !ruby/object:Gem::Requirement
|
|
96
96
|
requirements:
|
|
97
|
-
- - ">="
|
|
98
|
-
- !ruby/object:Gem::Version
|
|
99
|
-
version: 1.0.0
|
|
100
97
|
- - "~>"
|
|
101
98
|
- !ruby/object:Gem::Version
|
|
102
99
|
version: '1.0'
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 1.0.0
|
|
103
103
|
type: :development
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
|
-
- - ">="
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: 1.0.0
|
|
110
107
|
- - "~>"
|
|
111
108
|
- !ruby/object:Gem::Version
|
|
112
109
|
version: '1.0'
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: 1.0.0
|
|
113
113
|
- !ruby/object:Gem::Dependency
|
|
114
114
|
name: webmock
|
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
|
116
116
|
requirements:
|
|
117
|
-
- - ">="
|
|
118
|
-
- !ruby/object:Gem::Version
|
|
119
|
-
version: 3.16.0
|
|
120
117
|
- - "~>"
|
|
121
118
|
- !ruby/object:Gem::Version
|
|
122
119
|
version: '3.16'
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: 3.16.0
|
|
123
123
|
type: :development
|
|
124
124
|
prerelease: false
|
|
125
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements:
|
|
127
|
-
- - ">="
|
|
128
|
-
- !ruby/object:Gem::Version
|
|
129
|
-
version: 3.16.0
|
|
130
127
|
- - "~>"
|
|
131
128
|
- !ruby/object:Gem::Version
|
|
132
129
|
version: '3.16'
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: 3.16.0
|
|
133
133
|
- !ruby/object:Gem::Dependency
|
|
134
134
|
name: simplecov
|
|
135
135
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -154,22 +154,22 @@ dependencies:
|
|
|
154
154
|
name: vcr
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
156
156
|
requirements:
|
|
157
|
-
- - ">="
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: 4.0.0
|
|
160
157
|
- - "~>"
|
|
161
158
|
- !ruby/object:Gem::Version
|
|
162
159
|
version: '6.0'
|
|
160
|
+
- - ">="
|
|
161
|
+
- !ruby/object:Gem::Version
|
|
162
|
+
version: 4.0.0
|
|
163
163
|
type: :development
|
|
164
164
|
prerelease: false
|
|
165
165
|
version_requirements: !ruby/object:Gem::Requirement
|
|
166
166
|
requirements:
|
|
167
|
-
- - ">="
|
|
168
|
-
- !ruby/object:Gem::Version
|
|
169
|
-
version: 4.0.0
|
|
170
167
|
- - "~>"
|
|
171
168
|
- !ruby/object:Gem::Version
|
|
172
169
|
version: '6.0'
|
|
170
|
+
- - ">="
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: 4.0.0
|
|
173
173
|
- !ruby/object:Gem::Dependency
|
|
174
174
|
name: yard
|
|
175
175
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -214,22 +214,22 @@ dependencies:
|
|
|
214
214
|
name: awesome_print
|
|
215
215
|
requirement: !ruby/object:Gem::Requirement
|
|
216
216
|
requirements:
|
|
217
|
-
- - ">="
|
|
218
|
-
- !ruby/object:Gem::Version
|
|
219
|
-
version: 1.8.0
|
|
220
217
|
- - "~>"
|
|
221
218
|
- !ruby/object:Gem::Version
|
|
222
219
|
version: '1.8'
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: 1.8.0
|
|
223
223
|
type: :development
|
|
224
224
|
prerelease: false
|
|
225
225
|
version_requirements: !ruby/object:Gem::Requirement
|
|
226
226
|
requirements:
|
|
227
|
-
- - ">="
|
|
228
|
-
- !ruby/object:Gem::Version
|
|
229
|
-
version: 1.8.0
|
|
230
227
|
- - "~>"
|
|
231
228
|
- !ruby/object:Gem::Version
|
|
232
229
|
version: '1.8'
|
|
230
|
+
- - ">="
|
|
231
|
+
- !ruby/object:Gem::Version
|
|
232
|
+
version: 1.8.0
|
|
233
233
|
- !ruby/object:Gem::Dependency
|
|
234
234
|
name: irb
|
|
235
235
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -254,42 +254,42 @@ dependencies:
|
|
|
254
254
|
name: json
|
|
255
255
|
requirement: !ruby/object:Gem::Requirement
|
|
256
256
|
requirements:
|
|
257
|
-
- - ">="
|
|
258
|
-
- !ruby/object:Gem::Version
|
|
259
|
-
version: 2.1.0
|
|
260
257
|
- - "~>"
|
|
261
258
|
- !ruby/object:Gem::Version
|
|
262
259
|
version: '2.1'
|
|
260
|
+
- - ">="
|
|
261
|
+
- !ruby/object:Gem::Version
|
|
262
|
+
version: 2.1.0
|
|
263
263
|
type: :runtime
|
|
264
264
|
prerelease: false
|
|
265
265
|
version_requirements: !ruby/object:Gem::Requirement
|
|
266
266
|
requirements:
|
|
267
|
-
- - ">="
|
|
268
|
-
- !ruby/object:Gem::Version
|
|
269
|
-
version: 2.1.0
|
|
270
267
|
- - "~>"
|
|
271
268
|
- !ruby/object:Gem::Version
|
|
272
269
|
version: '2.1'
|
|
270
|
+
- - ">="
|
|
271
|
+
- !ruby/object:Gem::Version
|
|
272
|
+
version: 2.1.0
|
|
273
273
|
- !ruby/object:Gem::Dependency
|
|
274
274
|
name: logging
|
|
275
275
|
requirement: !ruby/object:Gem::Requirement
|
|
276
276
|
requirements:
|
|
277
|
-
- - ">="
|
|
278
|
-
- !ruby/object:Gem::Version
|
|
279
|
-
version: 2.2.0
|
|
280
277
|
- - "~>"
|
|
281
278
|
- !ruby/object:Gem::Version
|
|
282
279
|
version: '2.2'
|
|
280
|
+
- - ">="
|
|
281
|
+
- !ruby/object:Gem::Version
|
|
282
|
+
version: 2.2.0
|
|
283
283
|
type: :runtime
|
|
284
284
|
prerelease: false
|
|
285
285
|
version_requirements: !ruby/object:Gem::Requirement
|
|
286
286
|
requirements:
|
|
287
|
-
- - ">="
|
|
288
|
-
- !ruby/object:Gem::Version
|
|
289
|
-
version: 2.2.0
|
|
290
287
|
- - "~>"
|
|
291
288
|
- !ruby/object:Gem::Version
|
|
292
289
|
version: '2.2'
|
|
290
|
+
- - ">="
|
|
291
|
+
- !ruby/object:Gem::Version
|
|
292
|
+
version: 2.2.0
|
|
293
293
|
- !ruby/object:Gem::Dependency
|
|
294
294
|
name: hashie
|
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -325,25 +325,25 @@ dependencies:
|
|
|
325
325
|
- !ruby/object:Gem::Version
|
|
326
326
|
version: 0.0.20
|
|
327
327
|
- !ruby/object:Gem::Dependency
|
|
328
|
-
name:
|
|
328
|
+
name: rbsecp256k1
|
|
329
329
|
requirement: !ruby/object:Gem::Requirement
|
|
330
330
|
requirements:
|
|
331
331
|
- - "~>"
|
|
332
332
|
- !ruby/object:Gem::Version
|
|
333
|
-
version: '
|
|
333
|
+
version: '6.0'
|
|
334
334
|
- - ">="
|
|
335
335
|
- !ruby/object:Gem::Version
|
|
336
|
-
version:
|
|
336
|
+
version: 6.0.0
|
|
337
337
|
type: :runtime
|
|
338
338
|
prerelease: false
|
|
339
339
|
version_requirements: !ruby/object:Gem::Requirement
|
|
340
340
|
requirements:
|
|
341
341
|
- - "~>"
|
|
342
342
|
- !ruby/object:Gem::Version
|
|
343
|
-
version: '
|
|
343
|
+
version: '6.0'
|
|
344
344
|
- - ">="
|
|
345
345
|
- !ruby/object:Gem::Version
|
|
346
|
-
version:
|
|
346
|
+
version: 6.0.0
|
|
347
347
|
- !ruby/object:Gem::Dependency
|
|
348
348
|
name: bindata
|
|
349
349
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -393,6 +393,7 @@ extra_rdoc_files: []
|
|
|
393
393
|
files:
|
|
394
394
|
- ".gitignore"
|
|
395
395
|
- ".gitlab-ci.yml"
|
|
396
|
+
- CLAUDE.md
|
|
396
397
|
- CONTRIBUTING.md
|
|
397
398
|
- Gemfile
|
|
398
399
|
- LICENSE
|
|
@@ -411,6 +412,7 @@ files:
|
|
|
411
412
|
- lib/hive/bridge.rb
|
|
412
413
|
- lib/hive/broadcast.rb
|
|
413
414
|
- lib/hive/chain_config.rb
|
|
415
|
+
- lib/hive/compact_signer.rb
|
|
414
416
|
- lib/hive/fallback.rb
|
|
415
417
|
- lib/hive/formatter.rb
|
|
416
418
|
- lib/hive/jsonrpc.rb
|
|
@@ -469,6 +471,7 @@ files:
|
|
|
469
471
|
- lib/hive/rpc/base_client.rb
|
|
470
472
|
- lib/hive/rpc/http_client.rb
|
|
471
473
|
- lib/hive/rpc/thread_safe_http_client.rb
|
|
474
|
+
- lib/hive/signing_key.rb
|
|
472
475
|
- lib/hive/stream.rb
|
|
473
476
|
- lib/hive/transaction.rb
|
|
474
477
|
- lib/hive/transaction_builder.rb
|
|
@@ -481,7 +484,7 @@ homepage: https://gitlab.syncad.com/hive/hive-ruby
|
|
|
481
484
|
licenses:
|
|
482
485
|
- MIT
|
|
483
486
|
metadata: {}
|
|
484
|
-
post_install_message:
|
|
487
|
+
post_install_message:
|
|
485
488
|
rdoc_options: []
|
|
486
489
|
require_paths:
|
|
487
490
|
- lib
|
|
@@ -496,8 +499,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
496
499
|
- !ruby/object:Gem::Version
|
|
497
500
|
version: '0'
|
|
498
501
|
requirements: []
|
|
499
|
-
rubygems_version: 3.
|
|
500
|
-
signing_key:
|
|
502
|
+
rubygems_version: 3.4.19
|
|
503
|
+
signing_key:
|
|
501
504
|
specification_version: 4
|
|
502
505
|
summary: Hive Ruby Client
|
|
503
506
|
test_files: []
|