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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f7e6f764cbbfd51ed4f6a98ff9f0466bed57257420683babbc400ca0cac4c79
4
- data.tar.gz: 2e34dfe009279d0b0e75aa38fa8de9541990280438671010c4aee78e8d117709
3
+ metadata.gz: e14a674b10187c411a04fffc871add50169f06db7670812c7e8ea81df0208e16
4
+ data.tar.gz: 7259ab262c29de7acc1ec01798731635c63247c3faeb69eeee24fe0d37ded098
5
5
  SHA512:
6
- metadata.gz: 21ca38a506f519362795143e2a455a0107c11fca9d6ec7f1755f9ad346da6edd311716bc642af0ff0a1a44f685474edb560d3e58eec5dd2d343ee3a316a6fb0e
7
- data.tar.gz: 9531c54501014015b9c2cacf59bd87bf19d796697791a3e930fa0ae13c86f43f4873f8e9c118aa953ef1bd36225554710661f21e202088c0b0d237bb2b61a701
6
+ metadata.gz: 26b335c3419c163165071696690e167d995fc53bac47c52da777180bddb48e80bff831d39f9bdc75988aff1c0faaae51f78692252ce51ee31737cb63581de665
7
+ data.tar.gz: c01904de80b687ccb4460e362b83edd10cfcf939dcaf716e6c9b34f6a3756b82ae23ebb177e005404d37edd78bcdbbb95d626ce4bdc48b851770dc9e50ee09fc
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /vendor/
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
@@ -15,7 +15,10 @@ class BitcoinDaemon < DaemonSpawn::Base
15
15
 
16
16
  refresh_wallet
17
17
  node = Bitcoin::Node::SPV.new(conf)
18
- Thread.new { Bitcoin::Grpc::Server.run(node) }
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
@@ -5,6 +5,8 @@ require 'concurrent'
5
5
  require 'concurrent-edge'
6
6
  require 'leveldb'
7
7
 
8
+ require 'extensions/string'
9
+
8
10
  module Bitcoin
9
11
  module Grpc
10
12
  class Error < StandardError; end
@@ -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
@@ -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 = Bitcoin::Wallet::Publisher.spawn(:publisher)
17
- @utxo_handler = Bitcoin::Wallet::UtxoHandler.spawn(:utxo_handler, spv, publisher)
18
- @asset_handler = Bitcoin::Wallet::AssetHandler.spawn(:asset_handler, spv, publisher)
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
@@ -1,5 +1,5 @@
1
1
  module Bitcoin
2
2
  module Grpc
3
- VERSION = "0.1.4"
3
+ VERSION = "0.1.5"
4
4
  end
5
5
  end
@@ -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
- else
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
- publisher << Bitcoin::Grpc::EventUtxoSpent.new(request_id: 0, tx_hash: tx.tx_hash, tx_payload: tx.to_payload.bth, utxo: utxo) if utxo
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
- log(::Logger::DEBUG, "UtxoHandler#merkleblock:#{data.hashes}")
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
@@ -0,0 +1,4 @@
1
+ Run following command on the top directory of this repository.
2
+
3
+ grpc_tools_ruby_protoc -I proto --ruby_out=lib --grpc_out=lib proto/bitcoin/grpc/grpc.proto
4
+
@@ -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
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-01-28 00:00:00.000000000 Z
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: []