bitcoinrb-grpc 0.1.4 → 0.1.5
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 +1 -0
- data/bin/bitcoinrbd +4 -1
- data/bin/grpc-healthcheck +15 -0
- data/lib/bitcoin/grpc.rb +2 -0
- data/lib/bitcoin/grpc/grpc_pb.rb +97 -0
- data/lib/bitcoin/grpc/grpc_services_pb.rb +3 -0
- data/lib/bitcoin/grpc/server.rb +108 -6
- data/lib/bitcoin/grpc/version.rb +1 -1
- data/lib/bitcoin/wallet/publisher.rb +2 -1
- data/lib/bitcoin/wallet/utxo_handler.rb +43 -7
- data/lib/extensions/string.rb +14 -0
- data/proto/README.md +4 -0
- data/proto/bitcoin/grpc/grpc.proto +101 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e14a674b10187c411a04fffc871add50169f06db7670812c7e8ea81df0208e16
|
4
|
+
data.tar.gz: 7259ab262c29de7acc1ec01798731635c63247c3faeb69eeee24fe0d37ded098
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26b335c3419c163165071696690e167d995fc53bac47c52da777180bddb48e80bff831d39f9bdc75988aff1c0faaae51f78692252ce51ee31737cb63581de665
|
7
|
+
data.tar.gz: c01904de80b687ccb4460e362b83edd10cfcf939dcaf716e6c9b34f6a3756b82ae23ebb177e005404d37edd78bcdbbb95d626ce4bdc48b851770dc9e50ee09fc
|
data/.gitignore
CHANGED
data/bin/bitcoinrbd
CHANGED
@@ -15,7 +15,10 @@ class BitcoinDaemon < DaemonSpawn::Base
|
|
15
15
|
|
16
16
|
refresh_wallet
|
17
17
|
node = Bitcoin::Node::SPV.new(conf)
|
18
|
-
|
18
|
+
publisher = Bitcoin::Wallet::Publisher.spawn(:publisher)
|
19
|
+
utxo_handler = Bitcoin::Wallet::UtxoHandler.spawn(:utxo_handler, node, publisher)
|
20
|
+
asset_handler = Bitcoin::Wallet::AssetHandler.spawn(:asset_handler, node, publisher)
|
21
|
+
Thread.new { Bitcoin::Grpc::Server.run(node, publisher, utxo_handler, asset_handler) }
|
19
22
|
node.run
|
20
23
|
end
|
21
24
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'bitcoin'
|
5
|
+
require 'bitcoin/grpc'
|
6
|
+
|
7
|
+
begin
|
8
|
+
stub = Bitcoin::Grpc::Blockchain::Stub.new('localhost:8080', :this_channel_is_insecure)
|
9
|
+
request = Bitcoin::Grpc::GetBlockchainInfoRequest.new
|
10
|
+
response = stub.get_blockchain_info(request)
|
11
|
+
exit 0
|
12
|
+
rescue => e
|
13
|
+
puts e.message
|
14
|
+
exit 1
|
15
|
+
end
|
data/lib/bitcoin/grpc.rb
CHANGED
data/lib/bitcoin/grpc/grpc_pb.rb
CHANGED
@@ -4,6 +4,60 @@
|
|
4
4
|
require 'google/protobuf'
|
5
5
|
|
6
6
|
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
+
add_message "bitcoin.grpc.EventsRequest" do
|
8
|
+
optional :operation, :enum, 1, "bitcoin.grpc.Operation"
|
9
|
+
optional :event_type, :string, 2
|
10
|
+
end
|
11
|
+
add_message "bitcoin.grpc.EventsResponse" do
|
12
|
+
oneof :event do
|
13
|
+
optional :connect, :message, 1, "bitcoin.grpc.Connect"
|
14
|
+
optional :disconnect, :message, 2, "bitcoin.grpc.Disconnect"
|
15
|
+
optional :block_created, :message, 3, "bitcoin.grpc.BlockCreated"
|
16
|
+
optional :reorganized, :message, 4, "bitcoin.grpc.Reorganized"
|
17
|
+
optional :tx_received, :message, 5, "bitcoin.grpc.TxReceived"
|
18
|
+
optional :tx_broadcasted, :message, 6, "bitcoin.grpc.TxBroadcasted"
|
19
|
+
optional :utxo_registered, :message, 7, "bitcoin.grpc.UtxoRegistered"
|
20
|
+
optional :utxo_spent, :message, 8, "bitcoin.grpc.UtxoSpent"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
add_message "bitcoin.grpc.Connect" do
|
24
|
+
optional :host, :string, 1
|
25
|
+
optional :port, :uint32, 2
|
26
|
+
optional :local_version, :string, 3
|
27
|
+
optional :remote_version, :string, 4
|
28
|
+
end
|
29
|
+
add_message "bitcoin.grpc.Disconnect" do
|
30
|
+
optional :host, :string, 1
|
31
|
+
optional :port, :uint32, 2
|
32
|
+
end
|
33
|
+
add_message "bitcoin.grpc.BlockCreated" do
|
34
|
+
optional :hash, :string, 1
|
35
|
+
optional :height, :uint32, 2
|
36
|
+
end
|
37
|
+
add_message "bitcoin.grpc.Reorganized" do
|
38
|
+
optional :hash, :string, 1
|
39
|
+
optional :height, :uint32, 2
|
40
|
+
optional :orphan_block_hash, :string, 3
|
41
|
+
end
|
42
|
+
add_message "bitcoin.grpc.TxReceived" do
|
43
|
+
optional :tx_hash, :string, 1
|
44
|
+
optional :tx_payload, :string, 2
|
45
|
+
end
|
46
|
+
add_message "bitcoin.grpc.TxBroadcasted" do
|
47
|
+
optional :tx_hash, :string, 1
|
48
|
+
optional :tx_payload, :string, 2
|
49
|
+
end
|
50
|
+
add_message "bitcoin.grpc.UtxoRegistered" do
|
51
|
+
optional :tx_hash, :string, 1
|
52
|
+
optional :tx_payload, :string, 2
|
53
|
+
optional :utxo, :message, 3, "bitcoin.grpc.Utxo"
|
54
|
+
end
|
55
|
+
add_message "bitcoin.grpc.UtxoSpent" do
|
56
|
+
optional :tx_hash, :string, 1
|
57
|
+
optional :tx_payload, :string, 2
|
58
|
+
optional :utxo, :message, 3, "bitcoin.grpc.Utxo"
|
59
|
+
optional :out_point, :message, 4, "bitcoin.grpc.OutPoint"
|
60
|
+
end
|
7
61
|
add_message "bitcoin.grpc.WatchTxConfirmedRequest" do
|
8
62
|
optional :id, :uint32, 1
|
9
63
|
optional :tx_hash, :string, 2
|
@@ -25,6 +79,15 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
25
79
|
optional :spent, :message, 3, "bitcoin.grpc.EventUtxoSpent"
|
26
80
|
end
|
27
81
|
end
|
82
|
+
add_message "bitcoin.grpc.WatchUtxoSpentRequest" do
|
83
|
+
optional :id, :uint32, 1
|
84
|
+
optional :tx_hash, :string, 2
|
85
|
+
optional :output_index, :uint32, 3
|
86
|
+
end
|
87
|
+
add_message "bitcoin.grpc.WatchUtxoSpentResponse" do
|
88
|
+
optional :id, :uint32, 1
|
89
|
+
optional :spent, :message, 2, "bitcoin.grpc.EventUtxoSpent"
|
90
|
+
end
|
28
91
|
add_message "bitcoin.grpc.WatchTokenRequest" do
|
29
92
|
optional :id, :uint32, 1
|
30
93
|
optional :asset_type, :bytes, 2
|
@@ -41,6 +104,15 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
41
104
|
optional :tx_hash, :string, 1
|
42
105
|
optional :tx_payload, :string, 2
|
43
106
|
end
|
107
|
+
add_message "bitcoin.grpc.GetBlockchainInfoRequest" do
|
108
|
+
end
|
109
|
+
add_message "bitcoin.grpc.GetBlockchainInfoResponse" do
|
110
|
+
optional :chain, :string, 1
|
111
|
+
optional :headers, :uint32, 2
|
112
|
+
optional :bestblockhash, :string, 3
|
113
|
+
optional :chainwork, :uint32, 4
|
114
|
+
optional :mediantime, :uint32, 5
|
115
|
+
end
|
44
116
|
add_message "bitcoin.grpc.EventTxConfirmed" do
|
45
117
|
optional :request_id, :uint32, 1
|
46
118
|
optional :tx_hash, :string, 2
|
@@ -60,6 +132,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
60
132
|
optional :tx_hash, :string, 2
|
61
133
|
optional :tx_payload, :string, 3
|
62
134
|
optional :utxo, :message, 4, "bitcoin.grpc.Utxo"
|
135
|
+
optional :out_point, :message, 5, "bitcoin.grpc.OutPoint"
|
63
136
|
end
|
64
137
|
add_message "bitcoin.grpc.EventTokenIssued" do
|
65
138
|
optional :request_id, :uint32, 1
|
@@ -73,6 +146,10 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
73
146
|
optional :request_id, :uint32, 1
|
74
147
|
optional :asset, :message, 4, "bitcoin.grpc.AssetOutput"
|
75
148
|
end
|
149
|
+
add_message "bitcoin.grpc.OutPoint" do
|
150
|
+
optional :tx_hash, :string, 1
|
151
|
+
optional :index, :uint32, 2
|
152
|
+
end
|
76
153
|
add_message "bitcoin.grpc.Utxo" do
|
77
154
|
optional :tx_hash, :string, 1
|
78
155
|
optional :index, :uint32, 2
|
@@ -88,24 +165,44 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
88
165
|
optional :index, :uint32, 5
|
89
166
|
optional :block_height, :uint32, 6
|
90
167
|
end
|
168
|
+
add_enum "bitcoin.grpc.Operation" do
|
169
|
+
value :SUBSCRIBE, 0
|
170
|
+
value :UNSUBSCRIBE, 1
|
171
|
+
end
|
91
172
|
end
|
92
173
|
|
93
174
|
module Bitcoin
|
94
175
|
module Grpc
|
176
|
+
EventsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventsRequest").msgclass
|
177
|
+
EventsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventsResponse").msgclass
|
178
|
+
Connect = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.Connect").msgclass
|
179
|
+
Disconnect = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.Disconnect").msgclass
|
180
|
+
BlockCreated = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.BlockCreated").msgclass
|
181
|
+
Reorganized = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.Reorganized").msgclass
|
182
|
+
TxReceived = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.TxReceived").msgclass
|
183
|
+
TxBroadcasted = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.TxBroadcasted").msgclass
|
184
|
+
UtxoRegistered = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.UtxoRegistered").msgclass
|
185
|
+
UtxoSpent = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.UtxoSpent").msgclass
|
95
186
|
WatchTxConfirmedRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchTxConfirmedRequest").msgclass
|
96
187
|
WatchTxConfirmedResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchTxConfirmedResponse").msgclass
|
97
188
|
WatchUtxoRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchUtxoRequest").msgclass
|
98
189
|
WatchUtxoResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchUtxoResponse").msgclass
|
190
|
+
WatchUtxoSpentRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchUtxoSpentRequest").msgclass
|
191
|
+
WatchUtxoSpentResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchUtxoSpentResponse").msgclass
|
99
192
|
WatchTokenRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchTokenRequest").msgclass
|
100
193
|
WatchTokenResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchTokenResponse").msgclass
|
101
194
|
WatchAssetIdAssignedRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.WatchAssetIdAssignedRequest").msgclass
|
195
|
+
GetBlockchainInfoRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.GetBlockchainInfoRequest").msgclass
|
196
|
+
GetBlockchainInfoResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.GetBlockchainInfoResponse").msgclass
|
102
197
|
EventTxConfirmed = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventTxConfirmed").msgclass
|
103
198
|
EventUtxoRegistered = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventUtxoRegistered").msgclass
|
104
199
|
EventUtxoSpent = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventUtxoSpent").msgclass
|
105
200
|
EventTokenIssued = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventTokenIssued").msgclass
|
106
201
|
EventTokenTransfered = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventTokenTransfered").msgclass
|
107
202
|
EventTokenBurned = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.EventTokenBurned").msgclass
|
203
|
+
OutPoint = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.OutPoint").msgclass
|
108
204
|
Utxo = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.Utxo").msgclass
|
109
205
|
AssetOutput = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.AssetOutput").msgclass
|
206
|
+
Operation = Google::Protobuf::DescriptorPool.generated_pool.lookup("bitcoin.grpc.Operation").enummodule
|
110
207
|
end
|
111
208
|
end
|
@@ -17,7 +17,10 @@ module Bitcoin
|
|
17
17
|
|
18
18
|
rpc :WatchTxConfirmed, WatchTxConfirmedRequest, stream(WatchTxConfirmedResponse)
|
19
19
|
rpc :WatchUtxo, WatchUtxoRequest, stream(WatchUtxoResponse)
|
20
|
+
rpc :WatchUtxoSpent, WatchUtxoSpentRequest, stream(WatchUtxoSpentResponse)
|
20
21
|
rpc :WatchToken, WatchTokenRequest, stream(WatchTokenResponse)
|
22
|
+
rpc :GetBlockchainInfo, GetBlockchainInfoRequest, GetBlockchainInfoResponse
|
23
|
+
rpc :Events, stream(EventsRequest), stream(EventsResponse)
|
21
24
|
end
|
22
25
|
|
23
26
|
Stub = Service.rpc_stub_class
|
data/lib/bitcoin/grpc/server.rb
CHANGED
@@ -1,24 +1,38 @@
|
|
1
1
|
module Bitcoin
|
2
2
|
module Grpc
|
3
3
|
class Server < Bitcoin::Grpc::Blockchain::Service
|
4
|
-
def self.run(spv)
|
4
|
+
def self.run(spv, publisher, utxo_handler, asset_handler)
|
5
5
|
addr = "0.0.0.0:8080"
|
6
6
|
s = GRPC::RpcServer.new
|
7
7
|
s.add_http2_port(addr, :this_port_is_insecure)
|
8
|
-
s.handle(new(spv))
|
8
|
+
s.handle(new(spv, publisher, utxo_handler, asset_handler))
|
9
9
|
s.run_till_terminated
|
10
10
|
end
|
11
11
|
|
12
12
|
attr_reader :spv, :utxo_handler, :asset_handler, :publisher, :logger
|
13
13
|
|
14
|
-
def initialize(spv)
|
14
|
+
def initialize(spv, publisher, utxo_handler, asset_handler)
|
15
15
|
@spv = spv
|
16
|
-
@publisher =
|
17
|
-
@utxo_handler =
|
18
|
-
@asset_handler =
|
16
|
+
@publisher = publisher
|
17
|
+
@utxo_handler = utxo_handler
|
18
|
+
@asset_handler = asset_handler
|
19
19
|
@logger = Bitcoin::Logger.create(:debug)
|
20
20
|
end
|
21
21
|
|
22
|
+
def get_blockchain_info(request, call)
|
23
|
+
best_block = spv.chain.latest_block
|
24
|
+
GetBlockchainInfoResponse.new(
|
25
|
+
chain: Bitcoin.chain_params.network.to_s,
|
26
|
+
headers: best_block.height,
|
27
|
+
bestblockhash: best_block.header.block_id,
|
28
|
+
chainwork: best_block.header.work,
|
29
|
+
mediantime: spv.chain.mtp(best_block.block_hash)
|
30
|
+
)
|
31
|
+
rescue => e
|
32
|
+
logger.info("get_blockchain_info: #{e.message}")
|
33
|
+
logger.info("get_blockchain_info: #{e.backtrace}")
|
34
|
+
end
|
35
|
+
|
22
36
|
def watch_tx_confirmed(request, call)
|
23
37
|
logger.info("watch_tx_confirmed: #{request}")
|
24
38
|
utxo_handler << request
|
@@ -43,6 +57,18 @@ module Bitcoin
|
|
43
57
|
logger.info("watch_utxo: #{e.backtrace}")
|
44
58
|
end
|
45
59
|
|
60
|
+
def watch_utxo_spent(request, call)
|
61
|
+
logger.info("watch_utxo_spent: #{request}")
|
62
|
+
utxo_handler << request
|
63
|
+
response = []
|
64
|
+
Receiver.spawn(:receiver, request, response, publisher, [Bitcoin::Grpc::EventUtxoSpent])
|
65
|
+
logger.info("watch_utxo_spent: end")
|
66
|
+
ResponseEnum.new(request, response, WatchUtxoSpentResponseBuilder).each
|
67
|
+
rescue => e
|
68
|
+
logger.info("watch_utxo_spent: #{e.message}")
|
69
|
+
logger.info("watch_utxo_spent: #{e.backtrace}")
|
70
|
+
end
|
71
|
+
|
46
72
|
def watch_token(request, call)
|
47
73
|
logger.info("watch_token: #{request}")
|
48
74
|
utxo_handler << request
|
@@ -54,6 +80,73 @@ module Bitcoin
|
|
54
80
|
logger.info("watch_token: #{e.message}")
|
55
81
|
logger.info("watch_token: #{e.backtrace}")
|
56
82
|
end
|
83
|
+
|
84
|
+
def events(requests)
|
85
|
+
logger.info("events: #{requests}")
|
86
|
+
events = []
|
87
|
+
|
88
|
+
receiver = EventsReceiver.spawn(:receiver, events, publisher)
|
89
|
+
requests.each do |request|
|
90
|
+
receiver << request
|
91
|
+
end
|
92
|
+
|
93
|
+
logger.info("events: end")
|
94
|
+
EventsResponseEnum.new(events).each
|
95
|
+
rescue => e
|
96
|
+
logger.error("events: #{e.message}")
|
97
|
+
logger.error("events: #{e.backtrace}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class EventsReceiver < Concurrent::Actor::Context
|
102
|
+
attr_reader :events, :logger, :publisher
|
103
|
+
|
104
|
+
def initialize(events, publisher)
|
105
|
+
@events = events
|
106
|
+
@publisher = publisher
|
107
|
+
@logger = Bitcoin::Logger.create(:debug)
|
108
|
+
end
|
109
|
+
|
110
|
+
def on_message(message)
|
111
|
+
case message
|
112
|
+
when Bitcoin::Grpc::EventsRequest
|
113
|
+
clazz = Object.const_get("Bitcoin").const_get("Grpc").const_get(message.event_type)
|
114
|
+
case message.operation
|
115
|
+
when :SUBSCRIBE
|
116
|
+
publisher << [:subscribe, clazz]
|
117
|
+
when :UNSUBSCRIBE
|
118
|
+
publisher << [:unsubscribe, clazz]
|
119
|
+
else
|
120
|
+
logger.error("unsupported operation")
|
121
|
+
end
|
122
|
+
else
|
123
|
+
events << message
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class EventsResponseEnum
|
129
|
+
attr_reader :events, :logger
|
130
|
+
|
131
|
+
def initialize(events)
|
132
|
+
@events = events
|
133
|
+
@logger = Bitcoin::Logger.create(:debug)
|
134
|
+
end
|
135
|
+
|
136
|
+
def each
|
137
|
+
return enum_for(:each) unless block_given?
|
138
|
+
loop do
|
139
|
+
event = events.shift
|
140
|
+
if event
|
141
|
+
response = Bitcoin::Grpc::EventsResponse.new
|
142
|
+
field = event.class.name.split('::').last.snake
|
143
|
+
response[field] = event
|
144
|
+
yield response
|
145
|
+
else
|
146
|
+
sleep(1)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
57
150
|
end
|
58
151
|
|
59
152
|
class WatchTxConfirmedResponseBuilder
|
@@ -76,6 +169,15 @@ module Bitcoin
|
|
76
169
|
end
|
77
170
|
end
|
78
171
|
|
172
|
+
class WatchUtxoSpentResponseBuilder
|
173
|
+
def self.build(id, event)
|
174
|
+
case event
|
175
|
+
when Bitcoin::Grpc::EventUtxoSpent
|
176
|
+
Bitcoin::Grpc::WatchUtxoSpentResponse.new(id: id, spent: event)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
79
181
|
class WatchTokenResponseBuilder
|
80
182
|
def self.build(id, event)
|
81
183
|
case event
|
data/lib/bitcoin/grpc/version.rb
CHANGED
@@ -21,7 +21,8 @@ module Bitcoin
|
|
21
21
|
end
|
22
22
|
elsif message[0] == :subscribe?
|
23
23
|
receivers[message[1].name]&.include?(envelope.sender)
|
24
|
-
|
24
|
+
elsif message[0] == :unsubscribe
|
25
|
+
receivers.delete(message[1].name)
|
25
26
|
end
|
26
27
|
else
|
27
28
|
receivers[message&.class&.name]&.each { |r| r << message }
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Bitcoin
|
2
2
|
module Wallet
|
3
3
|
class UtxoHandler < Concurrent::Actor::Context
|
4
|
-
attr_reader :watchings, :spv, :utxo_db, :publisher
|
4
|
+
attr_reader :watchings, :spv, :utxo_db, :publisher, :pendings
|
5
5
|
|
6
6
|
def initialize(spv, publisher)
|
7
7
|
@watchings = []
|
8
|
+
@pendings = []
|
8
9
|
@spv = spv
|
9
10
|
@spv.add_observer(self)
|
10
11
|
|
@@ -21,6 +22,10 @@ module Bitcoin
|
|
21
22
|
when Bitcoin::Grpc::WatchTxConfirmedRequest
|
22
23
|
spv.filter_add(message.tx_hash)
|
23
24
|
watchings << message
|
25
|
+
when Bitcoin::Grpc::WatchUtxoSpentRequest
|
26
|
+
outpoint = Bitcoin::OutPoint.new(message.tx_hash, message.output_index)
|
27
|
+
spv.filter_add(outpoint.to_payload.bth)
|
28
|
+
watchings << message
|
24
29
|
when :watchings
|
25
30
|
watchings
|
26
31
|
end
|
@@ -42,7 +47,18 @@ module Bitcoin
|
|
42
47
|
|
43
48
|
tx.inputs.each do |input|
|
44
49
|
utxo = utxo_db.delete_utxo(input.out_point)
|
45
|
-
|
50
|
+
watchings
|
51
|
+
.select { |item| item.is_a? Bitcoin::Grpc::WatchUtxoSpentRequest }
|
52
|
+
.select { |item| input.out_point.hash == item.tx_hash && input.out_point.index == item.output_index }
|
53
|
+
.each do |item|
|
54
|
+
publisher << Bitcoin::Grpc::EventUtxoSpent.new(
|
55
|
+
request_id: item.id,
|
56
|
+
tx_hash: tx.tx_hash,
|
57
|
+
tx_payload: tx.to_payload.bth,
|
58
|
+
out_point: Bitcoin::Grpc::OutPoint.new(tx_hash: item.tx_hash, index: item.output_index),
|
59
|
+
utxo: utxo
|
60
|
+
)
|
61
|
+
end
|
46
62
|
end
|
47
63
|
|
48
64
|
utxo_db.save_tx(tx.tx_hash, tx.to_payload.bth)
|
@@ -51,12 +67,18 @@ module Bitcoin
|
|
51
67
|
end
|
52
68
|
|
53
69
|
def merkleblock(data)
|
54
|
-
block_height = spv.chain.latest_block.height
|
55
|
-
tree = Bitcoin::MerkleTree.build_partial(data.tx_count, data.hashes, Bitcoin.byte_to_bit(data.flags.htb))
|
56
70
|
tx_blockhash = data.header.block_hash
|
71
|
+
block = spv.chain.find_entry_by_hash(tx_blockhash)
|
72
|
+
if block
|
73
|
+
save_tx_position(data, block)
|
74
|
+
else
|
75
|
+
pendings << data
|
76
|
+
end
|
77
|
+
end
|
57
78
|
|
58
|
-
|
59
|
-
|
79
|
+
def save_tx_position(data, block)
|
80
|
+
tree = Bitcoin::MerkleTree.build_partial(data.tx_count, data.hashes, Bitcoin.byte_to_bit(data.flags.htb))
|
81
|
+
block_height = block.height
|
60
82
|
watchings
|
61
83
|
.select { |item| item.is_a? Bitcoin::Grpc::WatchTxConfirmedRequest }
|
62
84
|
.select { |item| data.hashes.include?(item.tx_hash) }
|
@@ -69,8 +91,22 @@ module Bitcoin
|
|
69
91
|
end
|
70
92
|
|
71
93
|
def header(data)
|
72
|
-
log(::Logger::DEBUG, "UtxoHandler#header:#{[data, watchings]}")
|
73
94
|
block_height = data[:height]
|
95
|
+
block_hash = data[:hash]
|
96
|
+
|
97
|
+
if block_hash && block_height > 0
|
98
|
+
publisher << Bitcoin::Grpc::BlockCreated.new(hash: block_hash, height: block_height)
|
99
|
+
end
|
100
|
+
|
101
|
+
pendings.each do |pending_merkleblock|
|
102
|
+
tx_blockhash = pending_merkleblock.header.block_hash
|
103
|
+
block = spv.chain.find_entry_by_hash(tx_blockhash)
|
104
|
+
if block
|
105
|
+
save_tx_position(pending_merkleblock, block)
|
106
|
+
pendings.delete(pending_merkleblock)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
74
110
|
watchings.select do |item|
|
75
111
|
case item
|
76
112
|
when Bitcoin::Grpc::WatchTxConfirmedRequest
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class String
|
4
|
+
def camel
|
5
|
+
split('_').map { |w| w[0].upcase + w[1..-1] }.join
|
6
|
+
end
|
7
|
+
|
8
|
+
def snake
|
9
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
10
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
11
|
+
.tr('-', '_')
|
12
|
+
.downcase
|
13
|
+
end
|
14
|
+
end
|
data/proto/README.md
ADDED
@@ -6,7 +6,79 @@ package bitcoin.grpc;
|
|
6
6
|
service Blockchain {
|
7
7
|
rpc WatchTxConfirmed(WatchTxConfirmedRequest) returns (stream WatchTxConfirmedResponse);
|
8
8
|
rpc WatchUtxo(WatchUtxoRequest) returns (stream WatchUtxoResponse);
|
9
|
+
rpc WatchUtxoSpent(WatchUtxoSpentRequest) returns (stream WatchUtxoSpentResponse);
|
9
10
|
rpc WatchToken(WatchTokenRequest) returns (stream WatchTokenResponse);
|
11
|
+
rpc GetBlockchainInfo(GetBlockchainInfoRequest) returns (GetBlockchainInfoResponse);
|
12
|
+
rpc Events(stream EventsRequest) returns (stream EventsResponse);
|
13
|
+
}
|
14
|
+
|
15
|
+
enum Operation {
|
16
|
+
SUBSCRIBE = 0;
|
17
|
+
UNSUBSCRIBE = 1;
|
18
|
+
}
|
19
|
+
|
20
|
+
message EventsRequest {
|
21
|
+
Operation operation = 1;
|
22
|
+
string event_type = 2;
|
23
|
+
}
|
24
|
+
|
25
|
+
message EventsResponse {
|
26
|
+
oneof event {
|
27
|
+
Connect connect = 1;
|
28
|
+
Disconnect disconnect = 2;
|
29
|
+
BlockCreated block_created = 3;
|
30
|
+
Reorganized reorganized = 4;
|
31
|
+
TxReceived tx_received = 5;
|
32
|
+
TxBroadcasted tx_broadcasted = 6;
|
33
|
+
UtxoRegistered utxo_registered = 7;
|
34
|
+
UtxoSpent utxo_spent = 8;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
message Connect {
|
39
|
+
string host = 1;
|
40
|
+
uint32 port = 2;
|
41
|
+
string local_version = 3;
|
42
|
+
string remote_version = 4;
|
43
|
+
}
|
44
|
+
|
45
|
+
message Disconnect {
|
46
|
+
string host = 1;
|
47
|
+
uint32 port = 2;
|
48
|
+
}
|
49
|
+
|
50
|
+
message BlockCreated {
|
51
|
+
string hash = 1;
|
52
|
+
uint32 height = 2;
|
53
|
+
}
|
54
|
+
|
55
|
+
message Reorganized {
|
56
|
+
string hash = 1;
|
57
|
+
uint32 height = 2;
|
58
|
+
string orphan_block_hash = 3;
|
59
|
+
}
|
60
|
+
|
61
|
+
message TxReceived {
|
62
|
+
string tx_hash = 1;
|
63
|
+
string tx_payload = 2;
|
64
|
+
}
|
65
|
+
|
66
|
+
message TxBroadcasted {
|
67
|
+
string tx_hash = 1;
|
68
|
+
string tx_payload = 2;
|
69
|
+
}
|
70
|
+
|
71
|
+
message UtxoRegistered {
|
72
|
+
string tx_hash = 1;
|
73
|
+
string tx_payload = 2;
|
74
|
+
Utxo utxo = 3;
|
75
|
+
}
|
76
|
+
|
77
|
+
message UtxoSpent {
|
78
|
+
string tx_hash = 1;
|
79
|
+
string tx_payload = 2;
|
80
|
+
Utxo utxo = 3;
|
81
|
+
OutPoint out_point = 4;
|
10
82
|
}
|
11
83
|
|
12
84
|
message WatchTxConfirmedRequest {
|
@@ -34,6 +106,18 @@ message WatchUtxoResponse {
|
|
34
106
|
}
|
35
107
|
}
|
36
108
|
|
109
|
+
message WatchUtxoSpentRequest {
|
110
|
+
uint32 id = 1;
|
111
|
+
string tx_hash = 2;
|
112
|
+
uint32 output_index = 3;
|
113
|
+
}
|
114
|
+
|
115
|
+
message WatchUtxoSpentResponse {
|
116
|
+
uint32 id = 1;
|
117
|
+
EventUtxoSpent spent = 2;
|
118
|
+
}
|
119
|
+
|
120
|
+
|
37
121
|
message WatchTokenRequest {
|
38
122
|
uint32 id = 1;
|
39
123
|
bytes asset_type = 2;
|
@@ -53,6 +137,17 @@ message WatchAssetIdAssignedRequest {
|
|
53
137
|
string tx_payload = 2;
|
54
138
|
}
|
55
139
|
|
140
|
+
message GetBlockchainInfoRequest {
|
141
|
+
|
142
|
+
}
|
143
|
+
message GetBlockchainInfoResponse {
|
144
|
+
string chain = 1;
|
145
|
+
uint32 headers = 2;
|
146
|
+
string bestblockhash = 3;
|
147
|
+
uint32 chainwork = 4;
|
148
|
+
uint32 mediantime = 5;
|
149
|
+
}
|
150
|
+
|
56
151
|
message EventTxConfirmed {
|
57
152
|
uint32 request_id = 1;
|
58
153
|
string tx_hash = 2;
|
@@ -74,6 +169,7 @@ message EventUtxoSpent {
|
|
74
169
|
string tx_hash = 2;
|
75
170
|
string tx_payload = 3;
|
76
171
|
Utxo utxo = 4;
|
172
|
+
OutPoint out_point = 5;
|
77
173
|
}
|
78
174
|
|
79
175
|
message EventTokenIssued {
|
@@ -91,6 +187,11 @@ message EventTokenBurned {
|
|
91
187
|
AssetOutput asset = 4;
|
92
188
|
}
|
93
189
|
|
190
|
+
message OutPoint {
|
191
|
+
string tx_hash = 1;
|
192
|
+
uint32 index = 2;
|
193
|
+
}
|
194
|
+
|
94
195
|
message Utxo {
|
95
196
|
string tx_hash = 1;
|
96
197
|
uint32 index = 2;
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoinrb-grpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hajime Yamaguchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- Rakefile
|
167
167
|
- bin/bitcoinrbd
|
168
168
|
- bin/console
|
169
|
+
- bin/grpc-healthcheck
|
169
170
|
- bin/setup
|
170
171
|
- bitcoinrb-grpc.gemspec
|
171
172
|
- config.yml
|
@@ -185,6 +186,8 @@ files:
|
|
185
186
|
- lib/extensions/bitcoin/tx.rb
|
186
187
|
- lib/extensions/bitcoin/wallet/base.rb
|
187
188
|
- lib/extensions/bitcoin/wallet/db.rb
|
189
|
+
- lib/extensions/string.rb
|
190
|
+
- proto/README.md
|
188
191
|
- proto/bitcoin/grpc/grpc.proto
|
189
192
|
homepage: ''
|
190
193
|
licenses: []
|