tapyrus 0.1.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 (128) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +12 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +100 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/exe/tapyrusrb-cli +5 -0
  15. data/exe/tapyrusrbd +41 -0
  16. data/lib/openassets/marker_output.rb +20 -0
  17. data/lib/openassets/payload.rb +54 -0
  18. data/lib/openassets/util.rb +28 -0
  19. data/lib/openassets.rb +9 -0
  20. data/lib/tapyrus/base58.rb +38 -0
  21. data/lib/tapyrus/block.rb +77 -0
  22. data/lib/tapyrus/block_header.rb +88 -0
  23. data/lib/tapyrus/bloom_filter.rb +78 -0
  24. data/lib/tapyrus/chain_params.rb +90 -0
  25. data/lib/tapyrus/chainparams/mainnet.yml +41 -0
  26. data/lib/tapyrus/chainparams/regtest.yml +38 -0
  27. data/lib/tapyrus/chainparams/testnet.yml +41 -0
  28. data/lib/tapyrus/constants.rb +195 -0
  29. data/lib/tapyrus/descriptor.rb +147 -0
  30. data/lib/tapyrus/ext_key.rb +337 -0
  31. data/lib/tapyrus/key.rb +296 -0
  32. data/lib/tapyrus/key_path.rb +26 -0
  33. data/lib/tapyrus/logger.rb +42 -0
  34. data/lib/tapyrus/merkle_tree.rb +149 -0
  35. data/lib/tapyrus/message/addr.rb +35 -0
  36. data/lib/tapyrus/message/base.rb +28 -0
  37. data/lib/tapyrus/message/block.rb +46 -0
  38. data/lib/tapyrus/message/block_transaction_request.rb +45 -0
  39. data/lib/tapyrus/message/block_transactions.rb +31 -0
  40. data/lib/tapyrus/message/block_txn.rb +27 -0
  41. data/lib/tapyrus/message/cmpct_block.rb +42 -0
  42. data/lib/tapyrus/message/error.rb +10 -0
  43. data/lib/tapyrus/message/fee_filter.rb +27 -0
  44. data/lib/tapyrus/message/filter_add.rb +28 -0
  45. data/lib/tapyrus/message/filter_clear.rb +17 -0
  46. data/lib/tapyrus/message/filter_load.rb +39 -0
  47. data/lib/tapyrus/message/get_addr.rb +17 -0
  48. data/lib/tapyrus/message/get_block_txn.rb +27 -0
  49. data/lib/tapyrus/message/get_blocks.rb +29 -0
  50. data/lib/tapyrus/message/get_data.rb +21 -0
  51. data/lib/tapyrus/message/get_headers.rb +28 -0
  52. data/lib/tapyrus/message/header_and_short_ids.rb +57 -0
  53. data/lib/tapyrus/message/headers.rb +35 -0
  54. data/lib/tapyrus/message/headers_parser.rb +24 -0
  55. data/lib/tapyrus/message/inv.rb +21 -0
  56. data/lib/tapyrus/message/inventories_parser.rb +23 -0
  57. data/lib/tapyrus/message/inventory.rb +51 -0
  58. data/lib/tapyrus/message/mem_pool.rb +17 -0
  59. data/lib/tapyrus/message/merkle_block.rb +42 -0
  60. data/lib/tapyrus/message/network_addr.rb +63 -0
  61. data/lib/tapyrus/message/not_found.rb +21 -0
  62. data/lib/tapyrus/message/ping.rb +30 -0
  63. data/lib/tapyrus/message/pong.rb +26 -0
  64. data/lib/tapyrus/message/prefilled_tx.rb +29 -0
  65. data/lib/tapyrus/message/reject.rb +46 -0
  66. data/lib/tapyrus/message/send_cmpct.rb +43 -0
  67. data/lib/tapyrus/message/send_headers.rb +16 -0
  68. data/lib/tapyrus/message/tx.rb +30 -0
  69. data/lib/tapyrus/message/ver_ack.rb +17 -0
  70. data/lib/tapyrus/message/version.rb +69 -0
  71. data/lib/tapyrus/message.rb +70 -0
  72. data/lib/tapyrus/mnemonic/wordlist/chinese_simplified.txt +2048 -0
  73. data/lib/tapyrus/mnemonic/wordlist/chinese_traditional.txt +2048 -0
  74. data/lib/tapyrus/mnemonic/wordlist/english.txt +2048 -0
  75. data/lib/tapyrus/mnemonic/wordlist/french.txt +2048 -0
  76. data/lib/tapyrus/mnemonic/wordlist/italian.txt +2048 -0
  77. data/lib/tapyrus/mnemonic/wordlist/japanese.txt +2048 -0
  78. data/lib/tapyrus/mnemonic/wordlist/spanish.txt +2048 -0
  79. data/lib/tapyrus/mnemonic.rb +77 -0
  80. data/lib/tapyrus/network/connection.rb +73 -0
  81. data/lib/tapyrus/network/message_handler.rb +241 -0
  82. data/lib/tapyrus/network/peer.rb +223 -0
  83. data/lib/tapyrus/network/peer_discovery.rb +42 -0
  84. data/lib/tapyrus/network/pool.rb +135 -0
  85. data/lib/tapyrus/network.rb +13 -0
  86. data/lib/tapyrus/node/cli.rb +112 -0
  87. data/lib/tapyrus/node/configuration.rb +38 -0
  88. data/lib/tapyrus/node/spv.rb +79 -0
  89. data/lib/tapyrus/node.rb +7 -0
  90. data/lib/tapyrus/opcodes.rb +178 -0
  91. data/lib/tapyrus/out_point.rb +44 -0
  92. data/lib/tapyrus/rpc/http_server.rb +65 -0
  93. data/lib/tapyrus/rpc/request_handler.rb +150 -0
  94. data/lib/tapyrus/rpc/tapyrus_core_client.rb +72 -0
  95. data/lib/tapyrus/rpc.rb +7 -0
  96. data/lib/tapyrus/script/multisig.rb +92 -0
  97. data/lib/tapyrus/script/script.rb +551 -0
  98. data/lib/tapyrus/script/script_error.rb +111 -0
  99. data/lib/tapyrus/script/script_interpreter.rb +668 -0
  100. data/lib/tapyrus/script/tx_checker.rb +81 -0
  101. data/lib/tapyrus/script_witness.rb +38 -0
  102. data/lib/tapyrus/secp256k1/native.rb +174 -0
  103. data/lib/tapyrus/secp256k1/ruby.rb +123 -0
  104. data/lib/tapyrus/secp256k1.rb +12 -0
  105. data/lib/tapyrus/slip39/share.rb +122 -0
  106. data/lib/tapyrus/slip39/sss.rb +245 -0
  107. data/lib/tapyrus/slip39/wordlist/english.txt +1024 -0
  108. data/lib/tapyrus/slip39.rb +93 -0
  109. data/lib/tapyrus/store/chain_entry.rb +67 -0
  110. data/lib/tapyrus/store/db/level_db.rb +98 -0
  111. data/lib/tapyrus/store/db.rb +9 -0
  112. data/lib/tapyrus/store/spv_chain.rb +101 -0
  113. data/lib/tapyrus/store.rb +9 -0
  114. data/lib/tapyrus/tx.rb +347 -0
  115. data/lib/tapyrus/tx_in.rb +89 -0
  116. data/lib/tapyrus/tx_out.rb +74 -0
  117. data/lib/tapyrus/util.rb +133 -0
  118. data/lib/tapyrus/validation.rb +115 -0
  119. data/lib/tapyrus/version.rb +3 -0
  120. data/lib/tapyrus/wallet/account.rb +151 -0
  121. data/lib/tapyrus/wallet/base.rb +162 -0
  122. data/lib/tapyrus/wallet/db.rb +81 -0
  123. data/lib/tapyrus/wallet/master_key.rb +110 -0
  124. data/lib/tapyrus/wallet.rb +8 -0
  125. data/lib/tapyrus.rb +219 -0
  126. data/tapyrusrb.conf.sample +0 -0
  127. data/tapyrusrb.gemspec +47 -0
  128. metadata +451 -0
@@ -0,0 +1,57 @@
1
+ require 'siphash'
2
+ module Tapyrus
3
+ module Message
4
+
5
+ # BIP-152 Compact Block's data format.
6
+ # https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki#HeaderAndShortIDs
7
+ class HeaderAndShortIDs
8
+ attr_accessor :header
9
+ attr_accessor :nonce
10
+ attr_accessor :short_ids
11
+ attr_accessor :prefilled_txn
12
+ attr_accessor :siphash_key
13
+
14
+ def initialize(header, nonce, short_ids = [], prefilled_txn = [])
15
+ @header = header
16
+ @nonce = nonce
17
+ @short_ids = short_ids
18
+ @prefilled_txn = prefilled_txn
19
+ @siphash_key = Tapyrus.sha256(header.to_payload << [nonce].pack('q*'))[0...16]
20
+ end
21
+
22
+ def self.parse_from_payload(payload)
23
+ buf = StringIO.new(payload)
24
+ header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
25
+ nonce = buf.read(8).unpack('q*').first
26
+ short_ids_len = Tapyrus.unpack_var_int_from_io(buf)
27
+ short_ids = short_ids_len.times.map do
28
+ buf.read(6).reverse.bth.to_i(16)
29
+ end
30
+ prefilled_txn_len = Tapyrus.unpack_var_int_from_io(buf)
31
+ prefilled_txn = prefilled_txn_len.times.map do
32
+ PrefilledTx.parse_from_io(buf)
33
+ end
34
+ self.new(header, nonce, short_ids, prefilled_txn)
35
+ end
36
+
37
+ def to_payload
38
+ p = header.to_payload
39
+ p << [nonce].pack('q*')
40
+ p << Tapyrus.pack_var_int(short_ids.size)
41
+ p << short_ids.map{|id|sprintf('%12x', id).htb.reverse}.join
42
+ p << Tapyrus.pack_var_int(prefilled_txn.size)
43
+ p << prefilled_txn.map(&:to_payload).join
44
+ p
45
+ end
46
+
47
+ # calculate short transaction id which specified by BIP-152.
48
+ # @param [String] txid a transaction id
49
+ # @return [Integer] 6 bytes short transaction id.
50
+ def short_id(txid)
51
+ hash = SipHash.digest(siphash_key, txid.htb.reverse).to_even_length_hex
52
+ [hash].pack('H*')[2...8].bth.to_i(16)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,35 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # headers message
5
+ # https://bitcoin.org/en/developer-reference#headers
6
+ class Headers < Base
7
+
8
+ COMMAND = 'headers'
9
+
10
+ # Array[Tapyrus::BlockHeader]
11
+ attr_accessor :headers
12
+
13
+ def initialize(headers = [])
14
+ @headers = headers
15
+ end
16
+
17
+ def self.parse_from_payload(payload)
18
+ buf = StringIO.new(payload)
19
+ header_count = Tapyrus.unpack_var_int_from_io(buf)
20
+ h = new
21
+ header_count.times do
22
+ h.headers << Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
23
+ buf.read(1) # read tx count 0x00 (headers message doesn't include any tx.)
24
+ end
25
+ h
26
+ end
27
+
28
+ def to_payload
29
+ Tapyrus.pack_var_int(headers.size) << headers.map { |h| h.to_payload << 0x00 }.join
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # Common message parser which handle multiple block headers as a payload.
5
+ module HeadersParser
6
+
7
+ def parse_from_payload(payload)
8
+ ver, payload = payload.unpack('Va*')
9
+ size, payload = Tapyrus.unpack_var_int(payload)
10
+ hashes = []
11
+ buf = StringIO.new(payload)
12
+ size.times do
13
+ hashes << buf.read(32).bth
14
+ end
15
+ new(ver, hashes, buf.read(32).bth)
16
+ end
17
+
18
+ def to_payload
19
+ [version].pack('V') << Tapyrus.pack_var_int(hashes.length) << hashes.map{|h|h.htb}.join << stop_hash.htb
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # inv message
5
+ # https://bitcoin.org/en/developer-reference#inv
6
+ class Inv < Base
7
+ include InventoriesParser
8
+ extend InventoriesParser
9
+
10
+ COMMAND = 'inv'
11
+
12
+ attr_reader :inventories
13
+
14
+ def initialize(inventories = [])
15
+ @inventories = inventories
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # Common message parser which only handle multiple inventory as payload.
5
+ module InventoriesParser
6
+
7
+ def parse_from_payload(payload)
8
+ size, payload = Tapyrus.unpack_var_int(payload)
9
+ buf = StringIO.new(payload)
10
+ i = new
11
+ size.times do
12
+ i.inventories << Inventory.parse_from_payload(buf.read(36))
13
+ end
14
+ i
15
+ end
16
+
17
+ def to_payload
18
+ Tapyrus.pack_var_int(inventories.length) << inventories.map(&:to_payload).join
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ module Tapyrus
2
+ module Message
3
+ # inventory class. inventory is a part of message.
4
+ # https://bitcoin.org/en/developer-reference#term-inventory
5
+ class Inventory
6
+
7
+ SEGWIT_FLAG = 1 << 30
8
+
9
+ MSG_TX = 1
10
+ MSG_BLOCK = 2
11
+ MSG_FILTERED_BLOCK = 3
12
+ MSG_CMPCT_BLOCK = 4
13
+ MSG_WITNESS_TX = SEGWIT_FLAG | MSG_TX
14
+ MSG_WITNESS_BLOCK = SEGWIT_FLAG | MSG_BLOCK
15
+ MSG_FILTERED_WITNESS_BLOCK = SEGWIT_FLAG | MSG_FILTERED_BLOCK
16
+
17
+ attr_accessor :identifier
18
+ attr_accessor :hash
19
+
20
+ def initialize(identifier, hash)
21
+ raise Error, "invalid type identifier specified. identifier = #{identifier}" unless valid_identifier?(identifier)
22
+ @identifier = identifier
23
+ @hash = hash
24
+ end
25
+
26
+ # parse inventory payload
27
+ def self.parse_from_payload(payload)
28
+ raise Error, 'invalid inventory size.' if payload.bytesize != 36
29
+ identifier = payload[0..4].unpack('V').first
30
+ hash = payload[4..-1].bth # internal byte order
31
+ new(identifier, hash)
32
+ end
33
+
34
+ def to_payload
35
+ [identifier].pack('V') << hash.htb
36
+ end
37
+
38
+ def block?
39
+ [MSG_BLOCK, MSG_WITNESS_BLOCK, MSG_FILTERED_WITNESS_BLOCK].include?(identifier)
40
+ end
41
+
42
+ private
43
+
44
+ def valid_identifier?(identifier)
45
+ [MSG_TX, MSG_BLOCK, MSG_FILTERED_BLOCK, MSG_CMPCT_BLOCK, MSG_WITNESS_TX,
46
+ MSG_WITNESS_BLOCK, MSG_FILTERED_WITNESS_BLOCK].include?(identifier)
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # mempool message
5
+ # https://bitcoin.org/en/developer-reference#mempool
6
+ class MemPool < Base
7
+
8
+ COMMAND = 'mempool'
9
+
10
+ def to_payload
11
+ ''
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # merckleblock message
5
+ # https://bitcoin.org/en/developer-reference#merkleblock
6
+ class MerkleBlock < Base
7
+
8
+ COMMAND = 'merkleblock'
9
+
10
+ attr_accessor :header
11
+ attr_accessor :tx_count
12
+ attr_accessor :hashes
13
+ attr_accessor :flags
14
+
15
+ def initialize
16
+ @hashes = []
17
+ end
18
+
19
+ def self.parse_from_payload(payload)
20
+ m = new
21
+ buf = StringIO.new(payload)
22
+ m.header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
23
+ m.tx_count = buf.read(4).unpack('V').first
24
+ hash_count = Tapyrus.unpack_var_int_from_io(buf)
25
+ hash_count.times do
26
+ m.hashes << buf.read(32).bth
27
+ end
28
+ flag_count = Tapyrus.unpack_var_int_from_io(buf)
29
+ # A sequence of bits packed eight in a byte with the least significant bit first.
30
+ m.flags = buf.read(flag_count).bth
31
+ m
32
+ end
33
+
34
+ def to_payload
35
+ header.to_payload << [tx_count].pack('V') << Tapyrus.pack_var_int(hashes.size) <<
36
+ hashes.map(&:htb).join << Tapyrus.pack_var_int(flags.htb.bytesize) << flags.htb
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,63 @@
1
+ require 'ipaddr'
2
+
3
+ module Tapyrus
4
+ module Message
5
+
6
+ class NetworkAddr
7
+
8
+ # unix time.
9
+ # Nodes advertising their own IP address set this to the current time.
10
+ # Nodes advertising IP addresses they’ve connected to set this to the last time they connected to that node.
11
+ # Other nodes just relaying the IP address should not change the time. Nodes can use the time field to avoid relaying old addr messages.
12
+ attr_accessor :time
13
+
14
+ # The services the node advertised in its version message.
15
+ attr_accessor :services
16
+
17
+ attr_accessor :ip_addr # IPAddr
18
+
19
+ attr_accessor :port
20
+
21
+ attr_reader :skip_time
22
+
23
+ def initialize(ip: '127.0.0.1', port: Tapyrus.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i)
24
+ @time = time
25
+ @ip_addr = IPAddr.new(ip)
26
+ @port = port
27
+ @services = services
28
+ end
29
+
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).unpack('V').first if has_time
35
+ addr.services = buf.read(8).unpack('Q').first
36
+ addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
37
+ addr.port = buf.read(2).unpack('n').first
38
+ addr
39
+ end
40
+
41
+ def self.local_addr
42
+ addr = new
43
+ addr.ip_addr = IPAddr.new('127.0.0.1')
44
+ addr.port = Tapyrus.chain_params.default_port
45
+ addr.services = DEFAULT_SERVICE_FLAGS
46
+ addr
47
+ end
48
+
49
+ def ip
50
+ ip_addr.ipv4_mapped? ? ip_addr.native : ip_addr.to_s
51
+ end
52
+
53
+ def to_payload(skip_time = false)
54
+ p = ''
55
+ 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')
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # notfound message
5
+ # https://bitcoin.org/en/developer-reference#notfound
6
+ class NotFound < Base
7
+ include InventoriesParser
8
+ extend InventoriesParser
9
+
10
+ attr_reader :inventories
11
+
12
+ COMMAND = 'notfound'
13
+
14
+ def initialize(inventories = [])
15
+ @inventories = inventories
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,30 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # ping message class
5
+ # https://bitcoin.org/en/developer-reference#ping
6
+ class Ping < Base
7
+
8
+ COMMAND = 'ping'
9
+
10
+ attr_accessor :nonce
11
+
12
+ def initialize(nonce = SecureRandom.random_number(0xffffffff))
13
+ @nonce = nonce
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ new(payload.unpack('Q').first)
18
+ end
19
+
20
+ def to_payload
21
+ [nonce].pack('Q')
22
+ end
23
+
24
+ def to_response
25
+ Pong.new(nonce)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # pong message
5
+ # https://bitcoin.org/en/developer-reference#pong
6
+ class Pong < Base
7
+
8
+ COMMAND = 'pong'
9
+
10
+ attr_reader :nonce
11
+
12
+ def initialize(nonce)
13
+ @nonce = nonce
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ new(payload.unpack('Q').first)
18
+ end
19
+
20
+ def to_payload
21
+ [nonce].pack('Q')
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # A PrefilledTransaction structure is used in HeaderAndShortIDs to provide a list of a few transactions explicitly.
5
+ # https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
6
+ class PrefilledTx
7
+
8
+ attr_accessor :index
9
+ attr_accessor :tx
10
+
11
+ def initialize(index, tx)
12
+ @index = index
13
+ @tx = tx
14
+ end
15
+
16
+ def self.parse_from_io(io)
17
+ index = Tapyrus.unpack_var_int_from_io(io)
18
+ tx = Tapyrus::Tx.parse_from_payload(io)
19
+ self.new(index, tx)
20
+ end
21
+
22
+ def to_payload
23
+ Tapyrus.pack_var_int(index) << tx.to_payload
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # reject message
5
+ # https://bitcoin.org/en/developer-reference#reject
6
+ class Reject < Base
7
+
8
+ attr_accessor :message
9
+ attr_accessor :code
10
+ attr_accessor :reason
11
+ attr_accessor :extra
12
+
13
+ COMMAND = 'reject'
14
+
15
+ CODE_MALFORMED = 0x01
16
+ CODE_INVALID = 0x10
17
+ CODE_OBSOLETE = 0x11
18
+ CODE_DUPLICATE = 0x12
19
+ CODE_NONSTANDARD = 0x40
20
+ CODE_DUST = 0x41
21
+ CODE_INSUFFICIENT_FEE = 0x42
22
+ CODE_CHECKPOINT = 0x43
23
+
24
+ def initialize(message, code, reason = '', extra = '')
25
+ @message = message
26
+ @code = code
27
+ @reason = reason
28
+ @extra = extra
29
+ end
30
+
31
+ def self.parse_from_payload(payload)
32
+ message, payload = Tapyrus.unpack_var_string(payload)
33
+ code, payload = payload.unpack('Ca*')
34
+ reason, payload = Tapyrus.unpack_var_string(payload)
35
+ extra = ['tx', 'block'].include?(message) ? payload.bth : payload
36
+ new(message, code, reason, extra)
37
+ end
38
+
39
+ def to_payload
40
+ e = ['tx', 'block'].include?(message) ? extra.htb : extra
41
+ Tapyrus.pack_var_string(message) << [code].pack('C') << Tapyrus.pack_var_string(reason) << e
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # sendcmpct message
5
+ # https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
6
+ class SendCmpct < Base
7
+
8
+ COMMAND = 'sendcmpct'
9
+
10
+ MODE_HIGH = 1
11
+ MODE_LOW = 0
12
+
13
+ attr_accessor :mode
14
+ attr_accessor :version
15
+ # TODO support version 2
16
+
17
+ def initialize(mode = MODE_HIGH, version = 1)
18
+ @mode = mode
19
+ @version = version
20
+ end
21
+
22
+ def self.parse_from_payload(payload)
23
+ buf = StringIO.new(payload)
24
+ mode = buf.read(1).unpack('c').first
25
+ version = buf.read(8).unpack('Q').first
26
+ new(mode, version)
27
+ end
28
+
29
+ def to_payload
30
+ [mode, version].pack('cQ')
31
+ end
32
+
33
+ def high?
34
+ mode == 1
35
+ end
36
+
37
+ def low?
38
+ mode.zero?
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # sendheaders message
5
+ # https://bitcoin.org/en/developer-reference#sendheaders
6
+ class SendHeaders < Base
7
+
8
+ COMMAND = 'sendheaders'
9
+
10
+ def to_payload
11
+ ''
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # tx message
5
+ # https://bitcoin.org/en/developer-reference#tx
6
+ class Tx < Base
7
+
8
+ COMMAND = 'tx'
9
+
10
+ attr_accessor :tx
11
+ attr_accessor :use_segwit
12
+
13
+ def initialize(tx, use_segwit = false)
14
+ @tx = tx
15
+ @use_segwit = use_segwit
16
+ end
17
+
18
+ def self.parse_from_payload(payload)
19
+ tx = Tapyrus::Tx.parse_from_payload(payload)
20
+ new(tx, tx.witness?)
21
+ end
22
+
23
+ def to_payload
24
+ use_segwit ? tx.to_payload : tx.serialize_old_format
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module Tapyrus
2
+ module Message
3
+
4
+ # verack message
5
+ # https://bitcoin.org/en/developer-reference#verack
6
+ class VerAck < Base
7
+
8
+ COMMAND = 'verack'
9
+
10
+ def to_payload
11
+ ''
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: ascii-8bit
2
+ require 'ipaddr'
3
+ module Tapyrus
4
+ module Message
5
+
6
+ # https://bitcoin.org/en/developer-reference#version
7
+ class Version < Base
8
+
9
+ COMMAND = 'version'
10
+
11
+ attr_accessor :version
12
+ attr_accessor :services
13
+ attr_accessor :timestamp
14
+ attr_accessor :local_addr
15
+ attr_accessor :remote_addr
16
+ attr_accessor :nonce
17
+ attr_accessor :user_agent
18
+ attr_accessor :start_height
19
+ attr_accessor :relay
20
+
21
+ def initialize(opts = {})
22
+ @version = Tapyrus.chain_params.protocol_version
23
+ @services = DEFAULT_SERVICE_FLAGS
24
+ @timestamp = Time.now.to_i
25
+ @local_addr = NetworkAddr.local_addr
26
+ @remote_addr = NetworkAddr.local_addr
27
+ @nonce = SecureRandom.random_number(0xffffffffffffffff)
28
+ @user_agent = Tapyrus::Message::USER_AGENT
29
+ @start_height = 0
30
+ opts.each { |k, v| send "#{k}=", v }
31
+ @relay = opts[:relay] || false
32
+ end
33
+
34
+ def self.parse_from_payload(payload)
35
+ version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('VQQa26a26Qa*')
36
+ v = new
37
+ v.version = version
38
+ v.services = services
39
+ v.timestamp = timestamp
40
+ v.local_addr = NetworkAddr.parse_from_payload(local_addr)
41
+ v.remote_addr = NetworkAddr.parse_from_payload(remote_addr)
42
+ v.nonce = nonce
43
+ user_agent, rest = unpack_var_string(rest)
44
+ start_height, rest = rest.unpack('Va*')
45
+ v.user_agent = user_agent
46
+ v.start_height = start_height
47
+ v.relay = v.unpack_relay_field(rest).first
48
+ v
49
+ end
50
+
51
+ def to_payload
52
+ [
53
+ [version, services, timestamp].pack('VQQ'),
54
+ local_addr.to_payload(true),
55
+ remote_addr.to_payload(true),
56
+ [nonce].pack('Q'),
57
+ pack_var_string(user_agent),
58
+ [start_height].pack('V'),
59
+ pack_boolean(relay)
60
+ ].join
61
+ end
62
+
63
+ def unpack_relay_field(payload)
64
+ ( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
65
+ end
66
+
67
+ end
68
+ end
69
+ end