tapyrus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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