tapyrus 0.3.9 → 0.4.0

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -2
  3. data/.ruby-version +1 -1
  4. data/README.md +43 -2
  5. data/lib/tapyrus/ext_key.rb +8 -4
  6. data/lib/tapyrus/key.rb +1 -1
  7. data/lib/tapyrus/key_path.rb +17 -13
  8. data/lib/tapyrus/message/fee_filter.rb +2 -2
  9. data/lib/tapyrus/message/header_and_short_ids.rb +4 -4
  10. data/lib/tapyrus/message/inventory.rb +1 -1
  11. data/lib/tapyrus/message/network_addr.rb +2 -2
  12. data/lib/tapyrus/message/ping.rb +2 -2
  13. data/lib/tapyrus/message/pong.rb +2 -2
  14. data/lib/tapyrus/message/send_cmpct.rb +2 -2
  15. data/lib/tapyrus/message/version.rb +3 -3
  16. data/lib/tapyrus/mnemonic.rb +9 -4
  17. data/lib/tapyrus/pstt/input.rb +422 -0
  18. data/lib/tapyrus/pstt/key_origin_info.rb +63 -0
  19. data/lib/tapyrus/pstt/output.rb +129 -0
  20. data/lib/tapyrus/pstt/proprietary.rb +54 -0
  21. data/lib/tapyrus/pstt/tx.rb +658 -0
  22. data/lib/tapyrus/pstt.rb +335 -0
  23. data/lib/tapyrus/rpc.rb +0 -2
  24. data/lib/tapyrus/script/script.rb +14 -4
  25. data/lib/tapyrus/secp256k1/rfc6979.rb +6 -3
  26. data/lib/tapyrus/tip0020.rb +341 -0
  27. data/lib/tapyrus/tx.rb +6 -2
  28. data/lib/tapyrus/tx_out.rb +2 -2
  29. data/lib/tapyrus/util.rb +3 -3
  30. data/lib/tapyrus/version.rb +1 -1
  31. data/lib/tapyrus/wallet/account.rb +2 -2
  32. data/lib/tapyrus/wallet/db.rb +3 -3
  33. data/lib/tapyrus/wallet/master_key.rb +2 -2
  34. data/lib/tapyrus.rb +3 -3
  35. data/tapyrusrb.gemspec +0 -3
  36. metadata +10 -61
  37. data/exe/tapyrusrb-cli +0 -5
  38. data/exe/tapyrusrbd +0 -42
  39. data/lib/tapyrus/network/connection.rb +0 -70
  40. data/lib/tapyrus/network/message_handler.rb +0 -244
  41. data/lib/tapyrus/network/peer.rb +0 -210
  42. data/lib/tapyrus/network/peer_discovery.rb +0 -43
  43. data/lib/tapyrus/network/pool.rb +0 -135
  44. data/lib/tapyrus/network.rb +0 -11
  45. data/lib/tapyrus/node/cli.rb +0 -114
  46. data/lib/tapyrus/node/configuration.rb +0 -36
  47. data/lib/tapyrus/node/spv.rb +0 -78
  48. data/lib/tapyrus/node.rb +0 -7
  49. data/lib/tapyrus/rpc/http_server.rb +0 -64
  50. data/lib/tapyrus/rpc/request_handler.rb +0 -146
data/exe/tapyrusrbd DELETED
@@ -1,42 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require "thor"
3
- require "tapyrus"
4
- require "daemon_spawn"
5
-
6
- class TapyrusDaemon < DaemonSpawn::Base
7
- def start(args)
8
- puts "Tapyrusrb daemon start : #{Time.now}"
9
- conf = Tapyrus::Node::Configuration.new(network: args.first[:network])
10
- node = Tapyrus::Node::SPV.new(conf)
11
- node.run
12
- end
13
- end
14
-
15
- class Tapyrusrbd < Thor
16
- class_option :network, aliases: "-n", default: :prod
17
-
18
- desc "start", "start tapyrusrb daemon."
19
- def start
20
- network = options["network"] ? options["network"].to_sym : :prod
21
- Tapyrus.chain_params = network
22
- FileUtils.mkdir_p(Tapyrus.base_dir)
23
- execute_daemon(["start", network: network])
24
- end
25
-
26
- private
27
-
28
- def execute_daemon(cmd_args)
29
- TapyrusDaemon.spawn!(
30
- {
31
- working_dir: Tapyrus.base_dir,
32
- log_file: "#{Tapyrus.base_dir}/log/tapyrusrbd.log",
33
- pid_file: "#{Tapyrus.base_dir}/tapyrusrbd.pid",
34
- sync_log: true,
35
- singleton: true
36
- },
37
- cmd_args
38
- )
39
- end
40
- end
41
-
42
- Tapyrusrbd.start(ARGV)
@@ -1,70 +0,0 @@
1
- module Tapyrus
2
- module Network
3
- # Basic Bitcoin P2P connection class
4
- class Connection < EM::Connection
5
- include MessageHandler
6
-
7
- attr_reader :peer, :logger
8
-
9
- # remote peer version.
10
- attr_accessor :version
11
-
12
- # if true, this peer send new block announcements using a headers message rather than an inv message.
13
- attr_accessor :sendheaders
14
-
15
- # minimum fee(in satoshis per kilobyte) for relay tx
16
- attr_accessor :fee_rate
17
-
18
- def initialize(peer)
19
- @peer = peer
20
- @logger = peer.logger
21
- @sendheaders = false
22
- @attr_accessor = 0
23
- @message = ""
24
- self.pending_connect_timeout = 5.0
25
- end
26
-
27
- def post_init
28
- logger.info "connected. #{addr}"
29
- peer.conn_time = Time.now.to_i
30
- begin_handshake
31
- end
32
-
33
- # handle receiving data from remote node.
34
- def receive_data(data)
35
- handle(data)
36
- end
37
-
38
- def post_handshake
39
- peer.post_handshake
40
- end
41
-
42
- def addr
43
- peer.addr
44
- end
45
-
46
- # close network connection.
47
- def close(msg = "")
48
- logger.info "close connection with #{addr}. #{msg}"
49
- close_connection_after_writing
50
- end
51
-
52
- def handle_error(e)
53
- peer.handle_error(e)
54
- end
55
-
56
- def unbind
57
- logger.info "unbind. #{addr}"
58
- peer.unbind
59
- end
60
-
61
- private
62
-
63
- # start handshake
64
- def begin_handshake
65
- logger.info "begin handshake with #{addr}"
66
- send_message(peer.local_version)
67
- end
68
- end
69
- end
70
- end
@@ -1,244 +0,0 @@
1
- module Tapyrus
2
- module Network
3
- # P2P message handler used by peer connection class.
4
- module MessageHandler
5
- using Tapyrus::Ext
6
-
7
- # handle p2p message.
8
- def handle(message)
9
- peer.last_recv = Time.now.to_i
10
- peer.bytes_recv += message.bytesize
11
- begin
12
- parse(message)
13
- rescue Tapyrus::Message::Error => e
14
- logger.error("invalid header magic. #{e.message}")
15
- close
16
- end
17
- end
18
-
19
- def parse(message)
20
- @message += message
21
- command, payload, rest = parse_header
22
- return unless command
23
-
24
- defer_handle_command(command, payload)
25
- @message = ""
26
- parse(rest) if rest && rest.bytesize > 0
27
- end
28
-
29
- def parse_header
30
- head_magic = Tapyrus.chain_params.magic_head
31
- return if @message.nil? || @message.size < MESSAGE_HEADER_SIZE
32
-
33
- magic, command, length, checksum = @message.unpack("a4A12Va4")
34
- raise Tapyrus::Message::Error, "invalid header magic. #{magic.bth}" unless magic.bth == head_magic
35
-
36
- payload = @message[MESSAGE_HEADER_SIZE...(MESSAGE_HEADER_SIZE + length)]
37
- return if payload.size < length
38
- unless Tapyrus.double_sha256(payload)[0...4] == checksum
39
- raise Tapyrus::Message::Error, "header checksum mismatch. #{checksum.bth}"
40
- end
41
-
42
- rest = @message[(MESSAGE_HEADER_SIZE + length)..-1]
43
- [command, payload, rest]
44
- end
45
-
46
- # handle command with EM#defer
47
- def defer_handle_command(command, payload)
48
- operation = proc { handle_command(command, payload) }
49
- callback = proc { |result| }
50
- errback =
51
- proc do |e|
52
- logger.error("error occurred. #{e.message}")
53
- logger.error(e.backtrace)
54
- peer.handle_error(e)
55
- end
56
- EM.defer(operation, callback, errback)
57
- end
58
-
59
- def handle_command(command, payload)
60
- logger.info("[#{addr}] process command #{command}.")
61
- case command
62
- when Tapyrus::Message::Version::COMMAND
63
- on_version(Tapyrus::Message::Version.parse_from_payload(payload))
64
- when Tapyrus::Message::VerAck::COMMAND
65
- on_ver_ack
66
- when Tapyrus::Message::GetAddr::COMMAND
67
- on_get_addr
68
- when Tapyrus::Message::Addr::COMMAND
69
- on_addr(Tapyrus::Message::Addr.parse_from_payload(payload))
70
- when Tapyrus::Message::SendHeaders::COMMAND
71
- on_send_headers
72
- when Tapyrus::Message::FeeFilter::COMMAND
73
- on_fee_filter(Tapyrus::Message::FeeFilter.parse_from_payload(payload))
74
- when Tapyrus::Message::Ping::COMMAND
75
- on_ping(Tapyrus::Message::Ping.parse_from_payload(payload))
76
- when Tapyrus::Message::Pong::COMMAND
77
- on_pong(Tapyrus::Message::Pong.parse_from_payload(payload))
78
- when Tapyrus::Message::GetHeaders::COMMAND
79
- on_get_headers(Tapyrus::Message::GetHeaders.parse_from_payload(payload))
80
- when Tapyrus::Message::Headers::COMMAND
81
- on_headers(Tapyrus::Message::Headers.parse_from_payload(payload))
82
- when Tapyrus::Message::Block::COMMAND
83
- on_block(Tapyrus::Message::Block.parse_from_payload(payload))
84
- when Tapyrus::Message::Tx::COMMAND
85
- on_tx(Tapyrus::Message::Tx.parse_from_payload(payload))
86
- when Tapyrus::Message::NotFound::COMMAND
87
- on_not_found(Tapyrus::Message::NotFound.parse_from_payload(payload))
88
- when Tapyrus::Message::MemPool::COMMAND
89
- on_mem_pool
90
- when Tapyrus::Message::Reject::COMMAND
91
- on_reject(Tapyrus::Message::Reject.parse_from_payload(payload))
92
- when Tapyrus::Message::SendCmpct::COMMAND
93
- on_send_cmpct(Tapyrus::Message::SendCmpct.parse_from_payload(payload))
94
- when Tapyrus::Message::Inv::COMMAND
95
- on_inv(Tapyrus::Message::Inv.parse_from_payload(payload))
96
- when Tapyrus::Message::MerkleBlock::COMMAND
97
- on_merkle_block(Tapyrus::Message::MerkleBlock.parse_from_payload(payload))
98
- when Tapyrus::Message::CmpctBlock::COMMAND
99
- on_cmpct_block(Tapyrus::Message::CmpctBlock.parse_from_payload(payload))
100
- when Tapyrus::Message::GetData::COMMAND
101
- on_get_data(Tapyrus::Message::GetData.parse_from_payload(payload))
102
- else
103
- logger.warn("unsupported command received. command: #{command}, payload: #{payload.bth}")
104
- close("with command #{command}")
105
- end
106
- end
107
-
108
- def send_message(msg)
109
- logger.info "send message #{msg.class::COMMAND}"
110
- pkt = msg.to_pkt
111
- peer.last_send = Time.now.to_i
112
- peer.bytes_sent = pkt.bytesize
113
- send_data(pkt)
114
- end
115
-
116
- def handshake_done
117
- return unless @incomming_handshake && @outgoing_handshake
118
- logger.info "handshake finished."
119
- @connected = true
120
- post_handshake
121
- end
122
-
123
- def on_version(version)
124
- logger.info("receive version message. #{version.build_json}")
125
- @version = version
126
- send_message(Tapyrus::Message::VerAck.new)
127
- @incomming_handshake = true
128
- handshake_done
129
- end
130
-
131
- def on_ver_ack
132
- logger.info("receive verack message.")
133
- @outgoing_handshake = true
134
- handshake_done
135
- end
136
-
137
- def on_get_addr
138
- logger.info("receive getaddr message.")
139
- peer.send_addrs
140
- end
141
-
142
- def on_addr(addr)
143
- logger.info("receive addr message. #{addr.build_json}")
144
- # TODO
145
- end
146
-
147
- def on_send_headers
148
- logger.info("receive sendheaders message.")
149
- @sendheaders = true
150
- end
151
-
152
- def on_fee_filter(fee_filter)
153
- logger.info("receive feefilter message. #{fee_filter.build_json}")
154
- @fee_rate = fee_filter.fee_rate
155
- end
156
-
157
- def on_ping(ping)
158
- logger.info("receive ping message. #{ping.build_json}")
159
- send_message(ping.to_response)
160
- end
161
-
162
- def on_pong(pong)
163
- logger.info("receive pong message. #{pong.build_json}")
164
- if pong.nonce == peer.last_ping_nonce
165
- peer.last_ping_nonce = nil
166
- peer.last_pong = Time.now.to_i
167
- else
168
- logger.debug "The remote peer sent the wrong nonce (#{pong.nonce})."
169
- end
170
- end
171
-
172
- def on_get_headers(headers)
173
- logger.info("receive getheaders message.")
174
- # TODO
175
- end
176
-
177
- def on_headers(headers)
178
- logger.info("receive headers message.")
179
- peer.handle_headers(headers)
180
- end
181
-
182
- def on_block(block)
183
- logger.info("receive block message.")
184
- # TODO
185
- end
186
-
187
- def on_tx(tx)
188
- logger.info("receive tx message. #{tx.build_json}")
189
- peer.handle_tx(tx)
190
- end
191
-
192
- def on_not_found(not_found)
193
- logger.info("receive notfound message. #{not_found.build_json}")
194
- # TODO
195
- end
196
-
197
- def on_mem_pool
198
- logger.info("receive mempool message.")
199
- # TODO return mempool tx
200
- end
201
-
202
- def on_reject(reject)
203
- logger.warn("receive reject message. #{reject.build_json}")
204
- # TODO
205
- end
206
-
207
- def on_send_cmpct(cmpct)
208
- logger.info("receive sendcmpct message. #{cmpct.build_json}")
209
- # TODO if mode is high and version is 1, relay block with cmpctblock message
210
- end
211
-
212
- def on_inv(inv)
213
- logger.info("receive inv message.")
214
- blocks = []
215
- txs = []
216
- inv.inventories.each do |i|
217
- case i.identifier
218
- when Tapyrus::Message::Inventory::MSG_TX
219
- txs << i.hash
220
- when Tapyrus::Message::Inventory::MSG_BLOCK
221
- blocks << i.hash
222
- else
223
- logger.warn("[#{addr}] peer sent unknown inv type: #{i.identifier}")
224
- end
225
- end
226
- logger.info("receive block= #{blocks.size}, txs: #{txs.size}")
227
- peer.handle_block_inv(blocks) unless blocks.empty?
228
- end
229
-
230
- def on_merkle_block(merkle_block)
231
- logger.info("receive merkle block message. #{merkle_block.build_json}")
232
- peer.handle_merkle_block(merkle_block)
233
- end
234
-
235
- def on_cmpct_block(cmpct_block)
236
- logger.info("receive cmpct_block message. #{cmpct_block.build_json}")
237
- end
238
-
239
- def on_get_data(get_data)
240
- logger.info("receive get data message. #{get_data.build_json}")
241
- end
242
- end
243
- end
244
- end
@@ -1,210 +0,0 @@
1
- module Tapyrus
2
- module Network
3
- # remote peer class.
4
- class Peer
5
- # Interval for pinging peers.
6
- PING_INTERVAL = 2 * 60
7
-
8
- attr_reader :logger
9
- attr_accessor :id
10
- attr_accessor :local_version
11
- attr_accessor :last_send
12
- attr_accessor :last_recv
13
- attr_accessor :bytes_sent
14
- attr_accessor :bytes_recv
15
- attr_accessor :conn_time
16
- attr_accessor :last_ping
17
- attr_accessor :last_ping_nonce
18
- attr_accessor :last_pong
19
- attr_accessor :min_ping
20
- attr_accessor :outbound # TODO need implements to accept inbound connection
21
- attr_accessor :best_hash
22
- attr_accessor :best_height
23
-
24
- # remote peer info
25
- attr_reader :host
26
- attr_reader :port
27
-
28
- # remote peer connection
29
- attr_accessor :conn
30
- attr_accessor :connected
31
- attr_accessor :primary
32
-
33
- # parent pool
34
- attr_reader :pool
35
- attr_reader :chain
36
- attr_accessor :fee_rate
37
-
38
- def initialize(host, port, pool, configuration)
39
- @host = host
40
- @port = port
41
- @pool = pool
42
- @chain = pool.chain
43
- @connected = false
44
- @primary = false
45
- @logger = Tapyrus::Logger.create(:debug)
46
- @outbound = true
47
- @best_hash = -1
48
- @best_height = -1
49
- @min_ping = -1
50
- @bytes_sent = 0
51
- @bytes_recv = 0
52
- @relay = configuration.conf[:relay]
53
- current_height = @chain.latest_block.height
54
- remote_addr = Tapyrus::Message::NetworkAddr.new(ip: host, port: port, time: nil)
55
- @local_version =
56
- Tapyrus::Message::Version.new(remote_addr: remote_addr, start_height: current_height, relay: @relay)
57
- end
58
-
59
- def connect
60
- self.conn ||= EM.connect(host, port, Tapyrus::Network::Connection, self)
61
- end
62
-
63
- def connected?
64
- @connected
65
- end
66
-
67
- def outbound?
68
- @outbound
69
- end
70
-
71
- def addr
72
- "#{host}:#{port}"
73
- end
74
-
75
- # calculate ping-pong time.
76
- def ping_time
77
- last_pong ? (last_pong - last_ping) / 1e6 : -1
78
- end
79
-
80
- # set last pong
81
- def last_pong=(time)
82
- @last_pong = time
83
- @min_ping = ping_time if min_ping == -1 || ping_time < min_ping
84
- end
85
-
86
- def post_handshake
87
- @connected = true
88
- pool.handle_new_peer(self)
89
-
90
- # require remote peer to use headers message instead fo inv message.
91
- conn.send_message(Tapyrus::Message::SendHeaders.new)
92
- EM.add_periodic_timer(PING_INTERVAL) { send_ping }
93
- end
94
-
95
- # start block header download
96
- def start_block_header_download
97
- logger.info("[#{addr}] start block header download.")
98
- get_headers =
99
- Tapyrus::Message::GetHeaders.new(Tapyrus.chain_params.protocol_version, [chain.latest_block.block_hash])
100
- conn.send_message(get_headers)
101
- end
102
-
103
- # broadcast tx.
104
- def broadcast_tx(tx)
105
- conn.send_message(Tapyrus::Message::Tx.new(tx))
106
- end
107
-
108
- # get peer's block type.
109
- def block_type
110
- Tapyrus::Message::Inventory::MSG_FILTERED_BLOCK # TODO need other implementation
111
- end
112
-
113
- # get remote peer's version message.
114
- # @return [Tapyrus::Message::Version]
115
- def remote_version
116
- conn.version
117
- end
118
-
119
- # Whether to try and download blocks and transactions from this peer.
120
- def primary?
121
- primary
122
- end
123
-
124
- # handle headers message
125
- # @params [Tapyrus::Message::Headers]
126
- def handle_headers(headers)
127
- headers.headers.each do |header|
128
- break unless header.valid?
129
- entry = chain.append_header(header)
130
- next unless entry
131
- @best_hash = entry.block_hash
132
- @best_height = entry.height
133
- end
134
- pool.changed
135
- pool.notify_observers(:header, { hash: @best_hash, height: @best_height })
136
- start_block_header_download if headers.headers.size > 0 # next header download
137
- end
138
-
139
- # handle error
140
- def handle_error(e)
141
- pool.handle_error(e)
142
- end
143
-
144
- def unbind
145
- pool.handle_close_peer(self)
146
- end
147
-
148
- # close peer connection.
149
- def close(msg = "")
150
- conn.close(msg)
151
- end
152
-
153
- # generate Tapyrus::Message::NetworkAddr object from this peer info.
154
- # @return [Tapyrus::Message::NetworkAddr]
155
- def to_network_addr
156
- v = remote_version
157
- Tapyrus::Message::NetworkAddr.new(ip: host, port: port, services: v.services, time: v.timestamp)
158
- end
159
-
160
- # send +addr+ message to remote peer
161
- def send_addrs
162
- addrs = pool.peers.select { |p| p != self }.map(&:to_network_addr)
163
- conn.send_message(Tapyrus::Message::Addr.new(addrs))
164
- end
165
-
166
- # handle block inv message.
167
- def handle_block_inv(hashes)
168
- getdata = Tapyrus::Message::GetData.new(hashes.map { |h| Tapyrus::Message::Inventory.new(block_type, h) })
169
- conn.send_message(getdata)
170
- end
171
-
172
- def handle_tx(tx)
173
- pool.changed
174
- pool.notify_observers(:tx, tx)
175
- end
176
-
177
- def handle_merkle_block(merkle_block)
178
- pool.changed
179
- pool.notify_observers(:merkleblock, merkle_block)
180
- end
181
-
182
- # send ping message.
183
- def send_ping
184
- ping = Tapyrus::Message::Ping.new
185
- @last_ping = Time.now.to_i
186
- @last_pong = -1
187
- @last_ping_nonce = ping.nonce
188
- conn.send_message(ping)
189
- end
190
-
191
- # send filterload message.
192
- def send_filter_load(bloom)
193
- filter_load = Tapyrus::Message::FilterLoad.new(bloom, Tapyrus::Message::FilterLoad::BLOOM_UPDATE_ALL)
194
- conn.send_message(filter_load)
195
- end
196
-
197
- # send filteradd message.
198
- def send_filter_add(element)
199
- filter_add = Tapyrus::Message::FilterAdd.new(element)
200
- conn.send_message(filter_add)
201
- end
202
-
203
- # send filterclear message.
204
- def send_filter_clear
205
- filter_clear = Tapyrus::Message::FilterClear.new
206
- conn.send_message(filter_clear)
207
- end
208
- end
209
- end
210
- end
@@ -1,43 +0,0 @@
1
- module Tapyrus
2
- module Network
3
- class PeerDiscovery
4
- attr_reader :logger, :configuration
5
-
6
- def initialize(configuration)
7
- @logger = Tapyrus::Logger.create(:debug)
8
- @configuration = configuration
9
- end
10
-
11
- # get peer addresses, from DNS seeds.
12
- def peers
13
- # TODO add find from previous connected peer at first.
14
- (find_from_dns_seeds + seeds).uniq
15
- end
16
-
17
- private
18
-
19
- def dns_seeds
20
- Tapyrus.chain_params.dns_seeds || []
21
- end
22
-
23
- def seeds
24
- [*configuration.conf[:connect]]
25
- end
26
-
27
- def find_from_dns_seeds
28
- logger.debug "discover peer address from DNS seeds."
29
- dns_seeds
30
- .map do |seed|
31
- begin
32
- Socket.getaddrinfo(seed, Tapyrus.chain_params.default_port).map { |a| a[2] }.uniq
33
- rescue SocketError => e
34
- logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
35
- nil
36
- end
37
- end
38
- .flatten
39
- .compact
40
- end
41
- end
42
- end
43
- end