Class: Bitcoin::Message::NetworkAddr

Inherits:
Object
  • Object
show all
Defined in:
lib/bitcoin/message/network_addr.rb

Constant Summary collapse

TYPE =
{legacy: 0x01, addr_v2: 0x02}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i, net: NETWORK_ID[:ipv4]) ⇒ NetworkAddr

Returns a new instance of NetworkAddr.



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/bitcoin/message/network_addr.rb', line 33

def initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port,
               services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i, net: NETWORK_ID[:ipv4])
  @time = time
  @port = port
  @services = services
  @net = net
  case net
  when NETWORK_ID[:ipv4], NETWORK_ID[:ipv6]
    @addr = IPAddr.new(ip) if ip
  end
end

Instance Attribute Details

#addrObject

Network address. The interpretation depends on networkID. If ipv4 or ipv6 this field is a IPAddr object, otherwise hex string.



27
28
29
# File 'lib/bitcoin/message/network_addr.rb', line 27

def addr
  @addr
end

#netObject

network ID that defined by BIP-155



23
24
25
# File 'lib/bitcoin/message/network_addr.rb', line 23

def net
  @net
end

#portObject

Returns the value of attribute port.



29
30
31
# File 'lib/bitcoin/message/network_addr.rb', line 29

def port
  @port
end

#servicesObject

The services the node advertised in its version message.



21
22
23
# File 'lib/bitcoin/message/network_addr.rb', line 21

def services
  @services
end

#skip_timeObject (readonly)

Returns the value of attribute skip_time.



31
32
33
# File 'lib/bitcoin/message/network_addr.rb', line 31

def skip_time
  @skip_time
end

#timeObject

unix time. Nodes advertising their own IP address set this to the current time. Nodes advertising IP addresses they’ve connected to set this to the last time they connected to that node. Other nodes just relaying the IP address should not change the time. Nodes can use the time field to avoid relaying old addr messages.



18
19
20
# File 'lib/bitcoin/message/network_addr.rb', line 18

def time
  @time
end

Class Method Details

.load_addr_v2_payload(payload) ⇒ Object

Load addr payload with addr v2 format.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/bitcoin/message/network_addr.rb', line 117

def self.load_addr_v2_payload(payload)
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
  addr = NetworkAddr.new(time: buf.read(4).unpack1('V'))
  addr.services = Bitcoin.unpack_var_int_from_io(buf)
  addr.net = buf.read(1).unpack1('C')
  raise Bitcoin::Message::Error, "Unknown network id: #{addr.net}" unless NETWORK_ID.value?(addr.net)
  addr_len = Bitcoin.unpack_var_int_from_io(buf)
  addr.addr = case addr.net 
              when NETWORK_ID[:ipv4]
                raise Bitcoin::Message::Error, "Invalid IPv4 address." unless addr_len == 4
                IPAddr::new_ntoh(buf.read(addr_len))
              when NETWORK_ID[:ipv6]
                raise Bitcoin::Message::Error, "Invalid IPv6 address." unless addr_len == 16
                a = IPAddr::new_ntoh(buf.read(addr_len))
                raise Bitcoin::Message::Error, "Invalid IPv6 address." if a.ipv4_mapped?
                a
              when NETWORK_ID[:tor_v2]
                raise Bitcoin::Message::Error, "Invalid Tor v2 address." unless addr_len == 10
                buf.read(addr_len).bth
              when NETWORK_ID[:tor_v3]
                raise Bitcoin::Message::Error, "Invalid Tor v3 address." unless addr_len == 32
                buf.read(addr_len).bth
              when NETWORK_ID[:i2p]
                raise Bitcoin::Message::Error, "Invalid I2P address." unless addr_len == 32
                buf.read(addr_len).bth
              when NETWORK_ID[:cjdns]
                raise Bitcoin::Message::Error, "Invalid CJDNS address." unless addr_len == 16
                a = IPAddr::new_ntoh(buf.read(addr_len))
                raise Bitcoin::Message::Error, "Invalid CJDNS address." unless a.to_s.start_with?('fc00:')
                a
              end
  addr.port = buf.read(2).unpack1('n')
  addr
end

.load_legacy_payload(payload) ⇒ Object

Load addr payload with legacy format.



105
106
107
108
109
110
111
112
113
114
# File 'lib/bitcoin/message/network_addr.rb', line 105

def self.load_legacy_payload(payload)
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
  has_time = buf.size > 26
  addr = NetworkAddr.new(time: nil)
  addr.time = buf.read(4).unpack1('V') if has_time
  addr.services = buf.read(8).unpack1('Q')
  addr.addr = IPAddr::new_ntoh(buf.read(16))
  addr.port = buf.read(2).unpack1('n')
  addr
end

.local_addrObject



60
61
62
63
64
65
66
# File 'lib/bitcoin/message/network_addr.rb', line 60

def self.local_addr
  addr = new
  addr.addr = IPAddr.new('127.0.0.1')
  addr.port = Bitcoin.chain_params.default_port
  addr.services = DEFAULT_SERVICE_FLAGS
  addr
end

.parse_from_payload(payload, type: TYPE[:legacy]) ⇒ NetworkAddr

Parse addr payload

Parameters:

  • payload (String)

    payload of addr

  • type (Integer) (defaults to: TYPE[:legacy])

    Address format type

Returns:



49
50
51
52
53
54
55
56
57
58
# File 'lib/bitcoin/message/network_addr.rb', line 49

def self.parse_from_payload(payload, type: TYPE[:legacy])
  case type
  when TYPE[:legacy]
    load_legacy_payload(payload)
  when TYPE[:addr_v2]
    load_addr_v2_payload(payload)
  else
    raise Bitcoin::Message::Error, "Unknown type: #{type}."
  end
end

Instance Method Details

#addr_stringObject

Show addr string. e.g 127.0.0.1



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/bitcoin/message/network_addr.rb', line 69

def addr_string
  case net
  when NETWORK_ID[:ipv4]
    addr.native
  when NETWORK_ID[:ipv6]
    if addr.to_s.start_with?(INTERNAL_IN_IPV6_PREFIX)
      Base32.encode(addr.hton[6..-1]).downcase.delete('=') + ".internal"
    else
      addr.to_s
    end
  when NETWORK_ID[:tor_v2]
    Base32.encode(addr.htb).downcase + ".onion"
  when NETWORK_ID[:tor_v3]
    # TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
    pubkey = addr.htb
    checksum = OpenSSL::Digest.new('SHA3-256').digest('.onion checksum' + pubkey + "\x03")
    Base32.encode(pubkey + checksum[0...2] + "\x03").downcase + ".onion"
  when NETWORK_ID[:i2p]
    Base32.encode(addr.htb).downcase.delete('=') + ".b32.i2p"
  when NETWORK_ID[:cjdns]
    addr.to_s
  end
end

#legacy_payload(skip_time) ⇒ Object



152
153
154
155
156
157
# File 'lib/bitcoin/message/network_addr.rb', line 152

def legacy_payload(skip_time)
  p = ''
  p << [time].pack('V') unless skip_time
  ip = addr.ipv4? ? addr.ipv4_mapped : addr
  p << [services].pack('Q') << ip.hton << [port].pack('n')
end

#to_payload(skip_time = false, type: TYPE[:legacy]) ⇒ Object



93
94
95
96
97
98
99
100
101
102
# File 'lib/bitcoin/message/network_addr.rb', line 93

def to_payload(skip_time = false, type: TYPE[:legacy])
  case type
  when TYPE[:legacy]
    legacy_payload(skip_time)
  when TYPE[:addr_v2]
    v2_payload
  else
    raise Bitcoin::Message::Error, "Unknown type: #{type}."
  end
end

#v2_payloadObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/bitcoin/message/network_addr.rb', line 159

def v2_payload
  p = [time].pack('V')
  p << Bitcoin.pack_var_int(services)
  p << [net].pack('C')
  case net
  when NETWORK_ID[:ipv4]
    p << Bitcoin.pack_var_int(4)
    p << addr.to_i.to_s(16).htb
  when NETWORK_ID[:ipv6]
    p << Bitcoin.pack_var_int(16)
    p << addr.hton
  when NETWORK_ID[:tor_v2]
    p << Bitcoin.pack_var_int(10)
  when NETWORK_ID[:tor_v3]
    p << Bitcoin.pack_var_int(32)
  when NETWORK_ID[:i2p]
    p << Bitcoin.pack_var_int(32)
  when NETWORK_ID[:cjdns]
    p << Bitcoin.pack_var_int(16)
  end
  p << [port].pack('n')
  p
end