bitcoinrb 0.0.1

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 (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/bitcoinrb.gemspec +32 -0
  15. data/exe/bitcoinrb-cli +5 -0
  16. data/exe/bitcoinrbd +49 -0
  17. data/lib/bitcoin.rb +121 -0
  18. data/lib/bitcoin/base58.rb +40 -0
  19. data/lib/bitcoin/block_header.rb +41 -0
  20. data/lib/bitcoin/chain_params.rb +57 -0
  21. data/lib/bitcoin/chainparams/mainnet.yml +25 -0
  22. data/lib/bitcoin/chainparams/regtest.yml +20 -0
  23. data/lib/bitcoin/chainparams/testnet.yml +24 -0
  24. data/lib/bitcoin/connection.rb +66 -0
  25. data/lib/bitcoin/ext_key.rb +205 -0
  26. data/lib/bitcoin/key.rb +131 -0
  27. data/lib/bitcoin/logger.rb +18 -0
  28. data/lib/bitcoin/merkle_tree.rb +120 -0
  29. data/lib/bitcoin/message.rb +42 -0
  30. data/lib/bitcoin/message/addr.rb +74 -0
  31. data/lib/bitcoin/message/base.rb +40 -0
  32. data/lib/bitcoin/message/block.rb +41 -0
  33. data/lib/bitcoin/message/error.rb +10 -0
  34. data/lib/bitcoin/message/fee_filter.rb +27 -0
  35. data/lib/bitcoin/message/filter_add.rb +28 -0
  36. data/lib/bitcoin/message/filter_clear.rb +17 -0
  37. data/lib/bitcoin/message/filter_load.rb +43 -0
  38. data/lib/bitcoin/message/get_addr.rb +17 -0
  39. data/lib/bitcoin/message/get_blocks.rb +29 -0
  40. data/lib/bitcoin/message/get_data.rb +21 -0
  41. data/lib/bitcoin/message/get_headers.rb +28 -0
  42. data/lib/bitcoin/message/handler.rb +170 -0
  43. data/lib/bitcoin/message/headers.rb +34 -0
  44. data/lib/bitcoin/message/headers_parser.rb +24 -0
  45. data/lib/bitcoin/message/inv.rb +21 -0
  46. data/lib/bitcoin/message/inventories_parser.rb +23 -0
  47. data/lib/bitcoin/message/inventory.rb +47 -0
  48. data/lib/bitcoin/message/mem_pool.rb +17 -0
  49. data/lib/bitcoin/message/merkle_block.rb +42 -0
  50. data/lib/bitcoin/message/not_found.rb +29 -0
  51. data/lib/bitcoin/message/ping.rb +30 -0
  52. data/lib/bitcoin/message/pong.rb +26 -0
  53. data/lib/bitcoin/message/reject.rb +46 -0
  54. data/lib/bitcoin/message/send_cmpct.rb +43 -0
  55. data/lib/bitcoin/message/send_headers.rb +16 -0
  56. data/lib/bitcoin/message/tx.rb +30 -0
  57. data/lib/bitcoin/message/ver_ack.rb +17 -0
  58. data/lib/bitcoin/message/version.rb +79 -0
  59. data/lib/bitcoin/mnemonic.rb +76 -0
  60. data/lib/bitcoin/mnemonic/wordlist/chinese_simplified.txt +2048 -0
  61. data/lib/bitcoin/mnemonic/wordlist/chinese_traditional.txt +2048 -0
  62. data/lib/bitcoin/mnemonic/wordlist/english.txt +2048 -0
  63. data/lib/bitcoin/mnemonic/wordlist/french.txt +2048 -0
  64. data/lib/bitcoin/mnemonic/wordlist/italian.txt +2048 -0
  65. data/lib/bitcoin/mnemonic/wordlist/japanese.txt +2048 -0
  66. data/lib/bitcoin/mnemonic/wordlist/spanish.txt +2048 -0
  67. data/lib/bitcoin/nodes.rb +5 -0
  68. data/lib/bitcoin/nodes/spv.rb +13 -0
  69. data/lib/bitcoin/nodes/spv/cli.rb +12 -0
  70. data/lib/bitcoin/nodes/spv/daemon.rb +21 -0
  71. data/lib/bitcoin/opcodes.rb +172 -0
  72. data/lib/bitcoin/out_point.rb +31 -0
  73. data/lib/bitcoin/script/script.rb +347 -0
  74. data/lib/bitcoin/script/script_error.rb +168 -0
  75. data/lib/bitcoin/script/script_interpreter.rb +694 -0
  76. data/lib/bitcoin/script/tx_checker.rb +44 -0
  77. data/lib/bitcoin/script_witness.rb +29 -0
  78. data/lib/bitcoin/secp256k1.rb +10 -0
  79. data/lib/bitcoin/secp256k1/native.rb +22 -0
  80. data/lib/bitcoin/secp256k1/ruby.rb +96 -0
  81. data/lib/bitcoin/tx.rb +191 -0
  82. data/lib/bitcoin/tx_in.rb +45 -0
  83. data/lib/bitcoin/tx_out.rb +32 -0
  84. data/lib/bitcoin/util.rb +105 -0
  85. data/lib/bitcoin/version.rb +3 -0
  86. metadata +256 -0
@@ -0,0 +1,21 @@
1
+ module Bitcoin
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 Bitcoin
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 = Bitcoin.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
+ Bitcoin.pack_var_int(inventories.length) << inventories.map(&:to_payload).join
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ module Bitcoin
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].reverse.bth # internal byte order
31
+ new(identifier, hash)
32
+ end
33
+
34
+ def to_payload
35
+ [identifier].pack('V') << hash.htb.reverse
36
+ end
37
+
38
+ private
39
+
40
+ def valid_identifier?(identifier)
41
+ [MSG_TX, MSG_BLOCK, MSG_FILTERED_BLOCK, MSG_CMPCT_BLOCK, MSG_WITNESS_TX,
42
+ MSG_WITNESS_BLOCK, MSG_FILTERED_WITNESS_BLOCK].include?(identifier)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ module Bitcoin
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 Bitcoin
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 = Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
23
+ m.tx_count = buf.read(4).unpack('V').first
24
+ hash_count = Bitcoin.unpack_var_int_from_io(buf)
25
+ hash_count.times do
26
+ m.hashes << buf.read(32).reverse.bth
27
+ end
28
+ flag_count = Bitcoin.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') << Bitcoin.pack_var_int(hashes.size) <<
36
+ hashes.map{|h|h.htb.reverse}.join << Bitcoin.pack_var_int(flags.htb.bytesize) << flags.htb
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,29 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # notfound message
5
+ # https://bitcoin.org/en/developer-reference#notfound
6
+ class NotFound < Base
7
+
8
+ attr_accessor :inventory
9
+
10
+ COMMAND = 'notfound'
11
+
12
+ def initialize(identifier, hash)
13
+ @inventory = Inventory.new(identifier, hash)
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ size, inventory_payload = Bitcoin.unpack_var_int(payload)
18
+ inventory = Inventory.parse_from_payload(inventory_payload)
19
+ new(inventory.identifier, inventory.hash)
20
+ end
21
+
22
+ def to_payload
23
+ Bitcoin.pack_var_int(1) << inventory.to_payload
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ module Bitcoin
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 Bitcoin
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,46 @@
1
+ module Bitcoin
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 = Bitcoin.unpack_var_string(payload)
33
+ code, payload = payload.unpack('Ca*')
34
+ reason, payload = Bitcoin.unpack_var_string(payload)
35
+ extra = ['tx', 'block'].include?(message) ? payload.reverse.bth : payload
36
+ new(message, code, reason, extra)
37
+ end
38
+
39
+ def to_payload
40
+ e = ['tx', 'block'].include?(message) ? extra.htb.reverse : extra
41
+ Bitcoin.pack_var_string(message) << [code].pack('C') << Bitcoin.pack_var_string(reason) << e
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module Bitcoin
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, version)
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 Bitcoin
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 Bitcoin
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 = Bitcoin::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 Bitcoin
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,79 @@
1
+ # encoding: ascii-8bit
2
+ module Bitcoin
3
+ module Message
4
+
5
+ # https://bitcoin.org/en/developer-reference#version
6
+ class Version < Base
7
+
8
+ COMMAND = 'version'
9
+
10
+ attr_accessor :version
11
+ attr_accessor :services
12
+ attr_accessor :timestamp
13
+ attr_accessor :local_addr
14
+ attr_accessor :remote_addr
15
+ attr_accessor :nonce
16
+ attr_accessor :user_agent
17
+ attr_accessor :start_height
18
+ attr_accessor :relay
19
+
20
+ def initialize(opts = {})
21
+ @version = Bitcoin.chain_params.protocol_version
22
+ @services = Bitcoin::Message::SERVICE_FLAGS[:network]
23
+ @timestamp = Time.now.to_i
24
+ @local_addr = "127.0.0.1:#{Bitcoin.chain_params.default_port}"
25
+ @remote_addr = "127.0.0.1:#{Bitcoin.chain_params.default_port}"
26
+ @nonce = SecureRandom.random_number(0xffffffffffffffff)
27
+ @user_agent = Bitcoin::Message::USER_AGENT
28
+ @start_height = 0
29
+ @relay = true
30
+ opts.each { |k, v| send "#{k}=", v }
31
+ end
32
+
33
+ def self.parse_from_payload(payload)
34
+ version, services, timestamp, remote_addr, local_addr, nonce, rest = payload.unpack('VQQa26a26Qa*')
35
+ v = new
36
+ v.version = version
37
+ v.services = services
38
+ v.timestamp = timestamp
39
+ v.remote_addr = v.unpack_addr(remote_addr)
40
+ v.local_addr = v.unpack_addr(local_addr)
41
+ v.nonce = nonce
42
+ user_agent, rest = unpack_var_string(rest)
43
+ start_height, rest = rest.unpack('Va*')
44
+ v.user_agent = user_agent
45
+ v.start_height = start_height
46
+ v.relay = v.unpack_relay_field(rest).first
47
+ v
48
+ end
49
+
50
+ def to_payload
51
+ [
52
+ [version, services, timestamp].pack('VQQ'),
53
+ pack_addr(local_addr),
54
+ pack_addr(remote_addr),
55
+ [nonce].pack('Q'),
56
+ pack_var_string(user_agent),
57
+ [start_height].pack('V'),
58
+ pack_boolean(relay)
59
+ ].join
60
+ end
61
+
62
+ def pack_addr(addr)
63
+ host, port = addr.split(':')
64
+ sockaddr = Socket.pack_sockaddr_in(port.to_i, host)
65
+ [[1].pack('Q'), "\x00" * 10, "\xFF\xFF", sockaddr[4...8], sockaddr[2...4]].join
66
+ end
67
+
68
+ def unpack_addr(addr)
69
+ host, port = addr.unpack('x8x12a4n')
70
+ "#{host.unpack('C*').join('.')}:#{port}"
71
+ end
72
+
73
+ def unpack_relay_field(payload)
74
+ ( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
75
+ end
76
+
77
+ end
78
+ end
79
+ end