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 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: []