bitcoinrb 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa485194b6a1f5ea2ba0a4ed3355cae8cc8cdd4252475a78dc811ec54a93dfa4
4
- data.tar.gz: 5c2c91ab2041b88e0ada77a1099df5e4d564f2d1da0a2963e3ff1beec0b63172
3
+ metadata.gz: 5026fc4446c5a88954149551d7168d8022e5aa41cd45cbf86b44ce62fdb308a5
4
+ data.tar.gz: a6a3708940e5eca7d8eba4ac71ca941e98ba5af7e09f516ea6e7583b38299c80
5
5
  SHA512:
6
- metadata.gz: c84550004b92167af33a7832c69cafeb44e24a98d491865976194614eed2ea6c831f5ac47a716ec989d1f759786741a26cb81fa6b5da33850a2e2b4dc0fa331b
7
- data.tar.gz: fb8bdfea37eb452b565f826cb2a12c56865fd80410376f0d444cddf339a1e4eb423988ef617a1dc84b573bb114db3ddae89ae49f60afd882349e92169bac6031
6
+ metadata.gz: 0f3ce5cc15d9de9c0fbb578ec179a81148228bb7f12c9937f6a3fe6f73585d09f6d62798abe1255e95e5d415c0a0e7fdeeb51457245b16565c8908b76d2d0249
7
+ data.tar.gz: fc3f64635b4330c15152b9a38b49a2ec7b324d3a7940cd7a51c64a601d6f9e89a6731014a9f7d2c86cef8a0453b18beb39c5ecec288b8a77c24d73d615c2aebf
data/.travis.yml CHANGED
@@ -1,3 +1,4 @@
1
+ dist: bionic
1
2
  language: ruby
2
3
  rvm:
3
4
  - 2.4.10
data/bitcoinrb.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'ecdsa'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
25
  spec.add_runtime_dependency 'murmurhash3'
26
- spec.add_runtime_dependency 'bech32', '~> 1.0.3'
26
+ spec.add_runtime_dependency 'bech32', '~> 1.1.0'
27
27
  spec.add_runtime_dependency 'daemon-spawn'
28
28
  spec.add_runtime_dependency 'thor'
29
29
  spec.add_runtime_dependency 'ffi'
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency 'protobuf', '3.8.5'
35
35
  spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
36
36
  spec.add_runtime_dependency 'bip-schnorr', '>= 0.3.2'
37
+ spec.add_runtime_dependency 'base32', '>= 0.3.4'
37
38
 
38
39
  # for options
39
40
  spec.add_development_dependency 'leveldb-native'
data/lib/bitcoin.rb CHANGED
@@ -133,14 +133,7 @@ module Bitcoin
133
133
 
134
134
  # get opcode
135
135
  def opcode
136
- case encoding
137
- when Encoding::ASCII_8BIT
138
- each_byte.next
139
- when Encoding::US_ASCII
140
- ord
141
- else
142
- to_i
143
- end
136
+ force_encoding(Encoding::ASCII_8BIT).ord
144
137
  end
145
138
 
146
139
  def opcode?
@@ -1,6 +1,8 @@
1
1
  module Bitcoin
2
2
  module Message
3
3
 
4
+ class Error < StandardError; end
5
+
4
6
  autoload :Base, 'bitcoin/message/base'
5
7
  autoload :Inventory, 'bitcoin/message/inventory'
6
8
  autoload :InventoriesParser, 'bitcoin/message/inventories_parser'
@@ -44,6 +46,8 @@ module Bitcoin
44
46
  autoload :CFCheckpt, 'bitcoin/message/cfcheckpt'
45
47
  autoload :CFilter, 'bitcoin/message/cfilter'
46
48
  autoload :CFHeaders, 'bitcoin/message/cfheaders'
49
+ autoload :SendAddrV2, 'bitcoin/message/send_addr_v2'
50
+ autoload :AddrV2, 'bitcoin/message/addr_v2'
47
51
 
48
52
  USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
49
53
 
@@ -73,5 +77,73 @@ module Bitcoin
73
77
  compact_witness: 70015
74
78
  }
75
79
 
80
+ module_function
81
+
82
+ # Decode P2P message.
83
+ # @param [String] command P2P message command string.
84
+ # @param [String] payload P2P message payload with hex format..
85
+ # @return [Bitcoin::Message::]
86
+ def decode(command, payload = nil)
87
+ payload = payload.htb if payload
88
+ case command
89
+ when Bitcoin::Message::Version::COMMAND
90
+ Bitcoin::Message::Version.parse_from_payload(payload)
91
+ when Bitcoin::Message::VerAck::COMMAND
92
+ Bitcoin::Message::VerAck.new
93
+ when Bitcoin::Message::GetAddr::COMMAND
94
+ Bitcoin::Message::GetAddr.new
95
+ when Bitcoin::Message::Addr::COMMAND
96
+ Bitcoin::Message::Addr.parse_from_payload(payload)
97
+ when Bitcoin::Message::SendHeaders::COMMAND
98
+ Bitcoin::Message::SendHeaders.new
99
+ when Bitcoin::Message::FeeFilter::COMMAND
100
+ Bitcoin::Message::FeeFilter.parse_from_payload(payload)
101
+ when Bitcoin::Message::Ping::COMMAND
102
+ Bitcoin::Message::Ping.parse_from_payload(payload)
103
+ when Bitcoin::Message::Pong::COMMAND
104
+ Bitcoin::Message::Pong.parse_from_payload(payload)
105
+ when Bitcoin::Message::GetHeaders::COMMAND
106
+ Bitcoin::Message::GetHeaders.parse_from_payload(payload)
107
+ when Bitcoin::Message::Headers::COMMAND
108
+ Bitcoin::Message::Headers.parse_from_payload(payload)
109
+ when Bitcoin::Message::Block::COMMAND
110
+ Bitcoin::Message::Block.parse_from_payload(payload)
111
+ when Bitcoin::Message::Tx::COMMAND
112
+ Bitcoin::Message::Tx.parse_from_payload(payload)
113
+ when Bitcoin::Message::NotFound::COMMAND
114
+ Bitcoin::Message::NotFound.parse_from_payload(payload)
115
+ when Bitcoin::Message::MemPool::COMMAND
116
+ Bitcoin::Message::MemPool.new
117
+ when Bitcoin::Message::Reject::COMMAND
118
+ Bitcoin::Message::Reject.parse_from_payload(payload)
119
+ when Bitcoin::Message::SendCmpct::COMMAND
120
+ Bitcoin::Message::SendCmpct.parse_from_payload(payload)
121
+ when Bitcoin::Message::Inv::COMMAND
122
+ Bitcoin::Message::Inv.parse_from_payload(payload)
123
+ when Bitcoin::Message::MerkleBlock::COMMAND
124
+ Bitcoin::Message::MerkleBlock.parse_from_payload(payload)
125
+ when Bitcoin::Message::CmpctBlock::COMMAND
126
+ Bitcoin::Message::CmpctBlock.parse_from_payload(payload)
127
+ when Bitcoin::Message::GetData::COMMAND
128
+ Bitcoin::Message::GetData.parse_from_payload(payload)
129
+ when Bitcoin::Message::GetCFHeaders::COMMAND
130
+ Bitcoin::Message::GetCFHeaders.parse_from_payload(payload)
131
+ when Bitcoin::Message::GetCFilters::COMMAND
132
+ Bitcoin::Message::GetCFilters.parse_from_payload(payload)
133
+ when Bitcoin::Message::GetCFCheckpt::COMMAND
134
+ Bitcoin::Message::GetCFCheckpt.parse_from_payload(payload)
135
+ when Bitcoin::Message::CFCheckpt::COMMAND
136
+ Bitcoin::Message::CFCheckpt.parse_from_payload(payload)
137
+ when Bitcoin::Message::CFHeaders::COMMAND
138
+ Bitcoin::Message::CFHeaders.parse_from_payload(payload)
139
+ when Bitcoin::Message::CFilter::COMMAND
140
+ Bitcoin::Message::CFilter.parse_from_payload(payload)
141
+ when Bitcoin::Message::SendAddrV2::COMMAND
142
+ Bitcoin::Message::SendAddrV2.new
143
+ when Bitcoin::Message::AddrV2::COMMAND
144
+ Bitcoin::Message::AddrV2.parse_from_payload(payload)
145
+ end
146
+ end
147
+
76
148
  end
77
149
  end
@@ -0,0 +1,34 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # addrv2 message class.
5
+ # https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
6
+ class AddrV2 < Base
7
+
8
+ COMMAND = 'addrv2'
9
+
10
+ attr_reader :addrs
11
+
12
+ def initialize(addrs = [])
13
+ @addrs = addrs
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ buf = StringIO.new(payload)
18
+ addr_count = Bitcoin.unpack_var_int_from_io(buf)
19
+ v2 = new
20
+ addr_count.times do
21
+ v2.addrs << NetworkAddr.parse_from_payload(buf, type: NetworkAddr::TYPE[:addr_v2])
22
+ end
23
+ v2
24
+ end
25
+
26
+ def to_payload
27
+ buf = Bitcoin.pack_var_int(addrs.size)
28
+ buf << (addrs.map { |a| a.to_payload(type: NetworkAddr::TYPE[:addr_v2])}.join)
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -23,6 +23,22 @@ module Bitcoin
23
23
  raise 'to_payload must be implemented in a child class.'
24
24
  end
25
25
 
26
+ # Decode message data to message object.
27
+ # @param [String] message with binary format.
28
+ # @return [Bitcoin::Message::XXX] An instance of a class that inherits Bitcoin::Message::Base
29
+ # @raise [ArgumentError] Occurs for data that cannot be decoded.
30
+ def self.from_pkt(message)
31
+ buf = StringIO.new(message)
32
+ magic = buf.read(4)
33
+ raise ArgumentError, 'Invalid magic.' unless magic == Bitcoin.chain_params.magic_head.htb
34
+ command = buf.read(12).delete("\x00")
35
+ length = buf.read(4).unpack1('V')
36
+ checksum = buf.read(4)
37
+ payload = buf.read(length)
38
+ raise ArgumentError, 'Checksum do not match.' unless checksum == Bitcoin.double_sha256(payload)[0...4]
39
+ Bitcoin::Message.decode(command, payload&.bth)
40
+ end
41
+
26
42
  end
27
43
 
28
44
  end
@@ -1,10 +1,16 @@
1
1
  require 'ipaddr'
2
+ require 'base32'
2
3
 
3
4
  module Bitcoin
4
5
  module Message
5
6
 
7
+ NETWORK_ID = {ipv4: 0x01, ipv6: 0x02, tor_v2: 0x03, tor_v3: 0x04, i2p: 0x05, cjdns: 0x06}
8
+ INTERNAL_IN_IPV6_PREFIX = "fd6b:88c0:8724"
9
+
6
10
  class NetworkAddr
7
11
 
12
+ TYPE = {legacy: 0x01, addr_v2: 0x02}
13
+
8
14
  # unix time.
9
15
  # Nodes advertising their own IP address set this to the current time.
10
16
  # Nodes advertising IP addresses they’ve connected to set this to the last time they connected to that node.
@@ -14,47 +20,164 @@ module Bitcoin
14
20
  # The services the node advertised in its version message.
15
21
  attr_accessor :services
16
22
 
17
- attr_accessor :ip_addr # IPAddr
23
+ attr_accessor :net # network ID that defined by BIP-155
24
+
25
+ # Network address. The interpretation depends on networkID.
26
+ # If ipv4 or ipv6 this field is a IPAddr object, otherwise hex string.
27
+ attr_accessor :addr
18
28
 
19
29
  attr_accessor :port
20
30
 
21
31
  attr_reader :skip_time
22
32
 
23
- def initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i)
33
+ def initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port,
34
+ services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i, net: NETWORK_ID[:ipv4])
24
35
  @time = time
25
- @ip_addr = IPAddr.new(ip)
26
36
  @port = port
27
37
  @services = services
38
+ @net = net
39
+ case net
40
+ when NETWORK_ID[:ipv4], NETWORK_ID[:ipv6]
41
+ @addr = IPAddr.new(ip) if ip
42
+ end
28
43
  end
29
44
 
30
- def self.parse_from_payload(payload)
31
- buf = payload.is_a?(String) ? StringIO.new(payload) : payload
32
- has_time = buf.size > 26
33
- addr = new(time: nil)
34
- addr.time = buf.read(4).unpack1('V') if has_time
35
- addr.services = buf.read(8).unpack1('Q')
36
- addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
37
- addr.port = buf.read(2).unpack1('n')
38
- addr
45
+ # Parse addr payload
46
+ # @param [String] payload payload of addr
47
+ # @param [Integer] type Address format type
48
+ # @return [NetworkAddr]
49
+ def self.parse_from_payload(payload, type: TYPE[:legacy])
50
+ case type
51
+ when TYPE[:legacy]
52
+ load_legacy_payload(payload)
53
+ when TYPE[:addr_v2]
54
+ load_addr_v2_payload(payload)
55
+ else
56
+ raise Bitcoin::Message::Error, "Unknown type: #{type}."
57
+ end
39
58
  end
40
59
 
41
60
  def self.local_addr
42
61
  addr = new
43
- addr.ip_addr = IPAddr.new('127.0.0.1')
62
+ addr.addr = IPAddr.new('127.0.0.1')
44
63
  addr.port = Bitcoin.chain_params.default_port
45
64
  addr.services = DEFAULT_SERVICE_FLAGS
46
65
  addr
47
66
  end
48
67
 
49
- def ip
50
- ip_addr.ipv4_mapped? ? ip_addr.native : ip_addr.to_s
68
+ # Show addr string. e.g 127.0.0.1
69
+ def addr_string
70
+ case net
71
+ when NETWORK_ID[:ipv4]
72
+ addr.native
73
+ when NETWORK_ID[:ipv6]
74
+ if addr.to_s.start_with?(INTERNAL_IN_IPV6_PREFIX)
75
+ Base32.encode(addr.hton[6..-1]).downcase.delete('=') + ".internal"
76
+ else
77
+ addr.to_s
78
+ end
79
+ when NETWORK_ID[:tor_v2]
80
+ Base32.encode(addr.htb).downcase + ".onion"
81
+ when NETWORK_ID[:tor_v3]
82
+ # TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
83
+ pubkey = addr.htb
84
+ checksum = OpenSSL::Digest.new('SHA3-256').digest('.onion checksum' + pubkey + "\x03")
85
+ Base32.encode(pubkey + checksum[0...2] + "\x03").downcase + ".onion"
86
+ when NETWORK_ID[:i2p]
87
+ Base32.encode(addr.htb).downcase.delete('=') + ".b32.i2p"
88
+ when NETWORK_ID[:cjdns]
89
+ addr.to_s
90
+ end
91
+ end
92
+
93
+ def to_payload(skip_time = false, type: TYPE[:legacy])
94
+ case type
95
+ when TYPE[:legacy]
96
+ legacy_payload(skip_time)
97
+ when TYPE[:addr_v2]
98
+ v2_payload
99
+ else
100
+ raise Bitcoin::Message::Error, "Unknown type: #{type}."
101
+ end
102
+ end
103
+
104
+ # Load addr payload with legacy format.
105
+ def self.load_legacy_payload(payload)
106
+ buf = payload.is_a?(String) ? StringIO.new(payload) : payload
107
+ has_time = buf.size > 26
108
+ addr = NetworkAddr.new(time: nil)
109
+ addr.time = buf.read(4).unpack1('V') if has_time
110
+ addr.services = buf.read(8).unpack1('Q')
111
+ addr.addr = IPAddr::new_ntoh(buf.read(16))
112
+ addr.port = buf.read(2).unpack1('n')
113
+ addr
114
+ end
115
+
116
+ # Load addr payload with addr v2 format.
117
+ def self.load_addr_v2_payload(payload)
118
+ buf = payload.is_a?(String) ? StringIO.new(payload) : payload
119
+ addr = NetworkAddr.new(time: buf.read(4).unpack1('V'))
120
+ addr.services = Bitcoin.unpack_var_int_from_io(buf)
121
+ addr.net = buf.read(1).unpack1('C')
122
+ raise Bitcoin::Message::Error, "Unknown network id: #{addr.net}" unless NETWORK_ID.value?(addr.net)
123
+ addr_len = Bitcoin.unpack_var_int_from_io(buf)
124
+ addr.addr = case addr.net
125
+ when NETWORK_ID[:ipv4]
126
+ raise Bitcoin::Message::Error, "Invalid IPv4 address." unless addr_len == 4
127
+ IPAddr::new_ntoh(buf.read(addr_len))
128
+ when NETWORK_ID[:ipv6]
129
+ raise Bitcoin::Message::Error, "Invalid IPv6 address." unless addr_len == 16
130
+ a = IPAddr::new_ntoh(buf.read(addr_len))
131
+ raise Bitcoin::Message::Error, "Invalid IPv6 address." if a.ipv4_mapped?
132
+ a
133
+ when NETWORK_ID[:tor_v2]
134
+ raise Bitcoin::Message::Error, "Invalid Tor v2 address." unless addr_len == 10
135
+ buf.read(addr_len).bth
136
+ when NETWORK_ID[:tor_v3]
137
+ raise Bitcoin::Message::Error, "Invalid Tor v3 address." unless addr_len == 32
138
+ buf.read(addr_len).bth
139
+ when NETWORK_ID[:i2p]
140
+ raise Bitcoin::Message::Error, "Invalid I2P address." unless addr_len == 32
141
+ buf.read(addr_len).bth
142
+ when NETWORK_ID[:cjdns]
143
+ raise Bitcoin::Message::Error, "Invalid CJDNS address." unless addr_len == 16
144
+ a = IPAddr::new_ntoh(buf.read(addr_len))
145
+ raise Bitcoin::Message::Error, "Invalid CJDNS address." unless a.to_s.start_with?('fc00:')
146
+ a
147
+ end
148
+ addr.port = buf.read(2).unpack1('n')
149
+ addr
51
150
  end
52
151
 
53
- def to_payload(skip_time = false)
152
+ def legacy_payload(skip_time)
54
153
  p = ''
55
154
  p << [time].pack('V') unless skip_time
56
- addr = ip_addr.ipv4? ? ip_addr.ipv4_mapped : ip_addr
57
- p << [services].pack('Q') << addr.hton << [port].pack('n')
155
+ ip = addr.ipv4? ? addr.ipv4_mapped : addr
156
+ p << [services].pack('Q') << ip.hton << [port].pack('n')
157
+ end
158
+
159
+ def v2_payload
160
+ p = [time].pack('V')
161
+ p << Bitcoin.pack_var_int(services)
162
+ p << [net].pack('C')
163
+ case net
164
+ when NETWORK_ID[:ipv4]
165
+ p << Bitcoin.pack_var_int(4)
166
+ p << addr.to_i.to_s(16).htb
167
+ when NETWORK_ID[:ipv6]
168
+ p << Bitcoin.pack_var_int(16)
169
+ p << addr.hton
170
+ when NETWORK_ID[:tor_v2]
171
+ p << Bitcoin.pack_var_int(10)
172
+ when NETWORK_ID[:tor_v3]
173
+ p << Bitcoin.pack_var_int(32)
174
+ when NETWORK_ID[:i2p]
175
+ p << Bitcoin.pack_var_int(32)
176
+ when NETWORK_ID[:cjdns]
177
+ p << Bitcoin.pack_var_int(16)
178
+ end
179
+ p << [port].pack('n')
180
+ p
58
181
  end
59
182
 
60
183
  end
@@ -0,0 +1,13 @@
1
+ module Bitcoin
2
+ module Message
3
+ class SendAddrV2 < Base
4
+
5
+ COMMAND = 'sendaddrv2'
6
+
7
+ def to_payload
8
+ ''
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -49,7 +49,7 @@ module Bitcoin
49
49
  # Returns connected peer information.
50
50
  def getpeerinfo
51
51
  node.pool.peers.map do |peer|
52
- local_addr = "#{peer.remote_version.remote_addr.ip}:18333"
52
+ local_addr = "#{peer.remote_version.remote_addr.addr_string}:18333"
53
53
  {
54
54
  id: peer.id,
55
55
  addr: "#{peer.host}:#{peer.port}",
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-20 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.3
61
+ version: 1.1.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.3
68
+ version: 1.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: daemon-spawn
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: 0.3.2
209
+ - !ruby/object:Gem::Dependency
210
+ name: base32
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: 0.3.4
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: 0.3.4
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: leveldb-native
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -342,6 +356,7 @@ files:
342
356
  - lib/bitcoin/merkle_tree.rb
343
357
  - lib/bitcoin/message.rb
344
358
  - lib/bitcoin/message/addr.rb
359
+ - lib/bitcoin/message/addr_v2.rb
345
360
  - lib/bitcoin/message/base.rb
346
361
  - lib/bitcoin/message/block.rb
347
362
  - lib/bitcoin/message/block_transaction_request.rb
@@ -379,6 +394,7 @@ files:
379
394
  - lib/bitcoin/message/pong.rb
380
395
  - lib/bitcoin/message/prefilled_tx.rb
381
396
  - lib/bitcoin/message/reject.rb
397
+ - lib/bitcoin/message/send_addr_v2.rb
382
398
  - lib/bitcoin/message/send_cmpct.rb
383
399
  - lib/bitcoin/message/send_headers.rb
384
400
  - lib/bitcoin/message/tx.rb