bitcoinrb 0.0.1

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