bitcoinrb-grpc 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|