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,10 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # P2P message layer Error
5
+ class Error < StandardError
6
+
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # feefilter message
5
+ # https://bitcoin.org/en/developer-reference#feefilter
6
+ class FeeFilter < Base
7
+
8
+ COMMAND = 'feefilter'
9
+
10
+ # The fee rate (in satoshis per kilobyte)
11
+ attr_accessor :fee_rate
12
+
13
+ def initialize(fee_rate)
14
+ @fee_rate = fee_rate
15
+ end
16
+
17
+ def self.parse_from_payload(payload)
18
+ new(payload.unpack('Q').first)
19
+ end
20
+
21
+ def to_payload
22
+ [fee_rate].pack('Q')
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # filteradd message
5
+ # https://bitcoin.org/en/developer-reference#filteradd
6
+ class FilterAdd < Base
7
+
8
+ COMMAND = 'filteradd'
9
+
10
+ # element must be sent in the byte order they would use when appearing in a raw transaction;
11
+ attr_accessor :element
12
+
13
+ def initialize(element)
14
+ @element = element
15
+ end
16
+
17
+ def self.parse_from_payload(payload)
18
+ new(Bitcoin.unpack_var_string(payload).first.bth)
19
+ end
20
+
21
+ def to_payload
22
+ Bitcoin.pack_var_string(element.htb)
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # filterclear message
5
+ # https://bitcoin.org/en/developer-reference#filterclear
6
+ class FilterClear < Base
7
+
8
+ COMMAND = 'filterclear'
9
+
10
+ def to_payload
11
+ ''
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # filterload message
5
+ # https://bitcoin.org/en/developer-reference#filterload
6
+ class FilterLoad < Base
7
+
8
+ COMMAND = 'filterload'
9
+
10
+ BLOOM_UPDATE_NONE = 0
11
+ BLOOM_UPDATE_ALL = 1
12
+ BLOOM_UPDATE_P2PUBKEY_ONLY = 2
13
+
14
+ attr_accessor :filter # bin format
15
+ attr_accessor :func_count
16
+ attr_accessor :tweak
17
+ attr_accessor :flag
18
+
19
+ def initialize(filter, func_count, tweak = 0, flag = BLOOM_UPDATE_ALL)
20
+ @filter = filter
21
+ @func_count = func_count
22
+ @tweak = tweak
23
+ @flag = flag
24
+ end
25
+
26
+ def self.parse_from_payload(payload)
27
+ buf = StringIO.new(payload)
28
+ filter_count = Bitcoin.unpack_var_int_from_io(buf)
29
+ filter = buf.read(filter_count).unpack('C*')
30
+ func_count = buf.read(4).unpack('V').first
31
+ tweak = buf.read(4).unpack('V').first
32
+ flag = buf.read(1).unpack('C').first
33
+ new(filter, func_count, tweak, flag)
34
+ end
35
+
36
+ def to_payload
37
+ Bitcoin.pack_var_int(filter.size) << filter.pack('C*') << [func_count, tweak, flag].pack('VVC')
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # getaddr message
5
+ # https://bitcoin.org/en/developer-reference#getaddr
6
+ class GetAddr < Base
7
+
8
+ COMMAND = 'getaddr'
9
+
10
+ def to_payload
11
+ ''
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # block message
5
+ # https://bitcoin.org/en/developer-reference#getblocks
6
+ class GetBlocks < Base
7
+ include HeadersParser
8
+ extend HeadersParser
9
+
10
+ COMMAND = 'getblocks'
11
+
12
+ # protocol version
13
+ attr_accessor :version
14
+
15
+ # block header hashes
16
+ attr_accessor :hashes
17
+
18
+ attr_accessor :stop_hash
19
+
20
+ def initialize(version, hashes, stop_hash = DEFAULT_STOP_HASH)
21
+ @version = version
22
+ @hashes = hashes
23
+ @stop_hash = stop_hash
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # getdadta message
5
+ # https://bitcoin.org/en/developer-reference#getdata
6
+ class GetData < Base
7
+ include InventoriesParser
8
+ extend InventoriesParser
9
+
10
+ COMMAND ='getdata'
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,28 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # getheaders message
5
+ # https://bitcoin.org/en/developer-reference#getheaders
6
+ class GetHeaders < Base
7
+ include HeadersParser
8
+ extend HeadersParser
9
+
10
+ COMMAND = 'getheaders'
11
+
12
+ # protocol version
13
+ attr_accessor :version
14
+
15
+ # block header hashes
16
+ attr_accessor :hashes
17
+
18
+ attr_accessor :stop_hash
19
+
20
+ def initialize(version, hashes, stop_hash = DEFAULT_STOP_HASH)
21
+ @version = version
22
+ @hashes = hashes
23
+ @stop_hash = stop_hash
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,170 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # Default P2P message handler.
5
+ class Handler
6
+
7
+ attr_reader :logger
8
+ attr_reader :conn
9
+
10
+ def initialize(conn, logger = Bitcoin::Logger.create(:parser))
11
+ @conn = conn
12
+ @logger = logger
13
+ @message = ""
14
+ end
15
+
16
+ # handle p2p message.
17
+ def handle(message)
18
+ logger.info "handle message #{message.bth}"
19
+ begin
20
+ parse(message)
21
+ rescue Error => e
22
+ logger.error("invalid header magic. #{e.message}")
23
+ conn.close
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def parse(message)
30
+ @message += message
31
+ command, payload, rest = parse_header
32
+ return unless command
33
+
34
+ handle_command(command, payload)
35
+ @message = ""
36
+ parse(rest) if rest && rest.bytesize > 0
37
+ end
38
+
39
+ def parse_header
40
+ head_magic = Bitcoin.chain_params.magic_head
41
+ return if @message.nil? || @message.size < HEADER_SIZE
42
+
43
+ magic, command, length, checksum = @message.unpack('a4A12Va4')
44
+ raise Error, "invalid header magic. #{magic.bth}" unless magic.bth == head_magic
45
+
46
+ payload = @message[HEADER_SIZE...(HEADER_SIZE + length)]
47
+ return if payload.size < length
48
+ raise Error, "header checksum mismatch. #{checksum.bth}" unless Bitcoin.double_sha256(payload)[0...4] == checksum
49
+
50
+ rest = @message[(HEADER_SIZE + length)..-1]
51
+ [command, payload, rest]
52
+ end
53
+
54
+ def handle_command(command, payload)
55
+ logger.info("process command #{command}. payload = #{payload.bth}")
56
+ case command
57
+ when Version::COMMAND
58
+ on_version(Version.parse_from_payload(payload))
59
+ when VerAck::COMMAND
60
+ on_ver_ack
61
+ when Addr::COMMAND
62
+ on_addr(Addr.parse_from_payload(payload))
63
+ when SendHeaders::COMMAND
64
+ on_send_headers
65
+ when FeeFilter::COMMAND
66
+ on_fee_filter(FeeFilter.parse_from_payload(payload))
67
+ when Ping::COMMAND
68
+ on_ping(Ping.parse_from_payload(payload))
69
+ when Pong::COMMAND
70
+ on_pong(Pong.parse_from_payload(payload))
71
+ when GetHeaders::COMMAND
72
+ on_get_headers(GetHeaders.parse_from_payload(payload))
73
+ when Headers::COMMAND
74
+ on_headers(Headers.parse_from_payload(payload))
75
+ when Block::COMMAND
76
+ on_block(Block.parse_from_payload(payload))
77
+ when Tx::COMMAND
78
+ on_tx(Tx.parse_from_payload(payload))
79
+ when NotFound::COMMAND
80
+ on_not_found(NotFound.parse_from_payload(payload))
81
+ when MemPool::COMMAND
82
+ on_mem_pool
83
+ when Reject::COMMAND
84
+ on_reject(Reject.parse_from_payload(payload))
85
+ when SendCmpct::COMMAND
86
+ on_send_cmpct(SendCmpct.parse_from_payload(payload))
87
+ else
88
+ logger.warn("unsupported command received. #{command}")
89
+ conn.close("with command #{command}")
90
+ end
91
+ end
92
+
93
+ def on_version(version)
94
+ logger.info("receive version message. #{version.to_json}")
95
+ conn.send_data(VerAck.new.to_pkt)
96
+ end
97
+
98
+ def on_ver_ack
99
+ logger.info('receive verack message.')
100
+ conn.handshake_done
101
+ end
102
+
103
+ def on_addr(addr)
104
+ logger.info("receive addr message. #{addr.to_json}")
105
+ # TODO
106
+ end
107
+
108
+ def on_send_headers
109
+ logger.info('receive sendheaders message.')
110
+ conn.sendheaders = true
111
+ end
112
+
113
+ def on_fee_filter(fee_filter)
114
+ logger.info("receive feefilter message. #{fee_filter.to_json}")
115
+ conn.fee_rate = fee_filter.fee_rate
116
+ end
117
+
118
+ def on_ping(ping)
119
+ logger.info("receive ping message. #{ping.to_json}")
120
+ conn.send_data(ping.to_response)
121
+ end
122
+
123
+ def on_pong(pong)
124
+ logger.info("receive pong message. #{pong.to_json}")
125
+ # TODO calculate response
126
+ end
127
+
128
+ def on_get_headers(headers)
129
+ logger.info("receive getheaders message. #{headers.to_json}")
130
+ # TODO
131
+ end
132
+
133
+ def on_headers(headers)
134
+ logger.info("receive headers message. #{headers.to_json}")
135
+ # TODO
136
+ end
137
+
138
+ def on_block(block)
139
+ logger.info("receive block message. #{block.to_json}")
140
+ # TODO
141
+ end
142
+
143
+ def on_tx(tx)
144
+ logger.info("receive tx message. #{tx.to_json}")
145
+ # TODO
146
+ end
147
+
148
+ def on_not_found(not_found)
149
+ logger.info("receive notfound message. #{not_found.to_json}")
150
+ # TODO
151
+ end
152
+
153
+ def on_mem_pool
154
+ logger.info('receive mempool message.')
155
+ # TODO return mempool tx
156
+ end
157
+
158
+ def on_reject(reject)
159
+ logger.warn("receive reject message. #{reject.to_json}")
160
+ # TODO
161
+ end
162
+
163
+ def on_send_cmpct(cmpct)
164
+ logger.info("receive sendcmpct message. #{cmpct.to_json}")
165
+ # TODO if mode is high and version is 1, relay block with cmpctblock message
166
+ end
167
+
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,34 @@
1
+ module Bitcoin
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
+ attr_accessor :headers
11
+
12
+ def initialize(headers = [])
13
+ @headers = headers
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ buf = StringIO.new(payload)
18
+ header_count = Bitcoin.unpack_var_int_from_io(buf)
19
+ h = new
20
+ header_count.times do
21
+ h.headers << Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
22
+ buf.read(1) # read tx count 0x00 (headers message doesn't include any tx.)
23
+ end
24
+ h
25
+ end
26
+
27
+ def to_payload
28
+ Bitcoin.pack_var_int(headers.size) << headers.map { |h| h.to_payload << 0x00 }.join
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ module Bitcoin
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 = Bitcoin.unpack_var_int(payload)
10
+ hashes = []
11
+ buf = StringIO.new(payload)
12
+ size.times do
13
+ hashes << buf.read(32).reverse.bth
14
+ end
15
+ new(ver, hashes, buf.read(32).reverse.bth)
16
+ end
17
+
18
+ def to_payload
19
+ [version].pack('V') << Bitcoin.pack_var_int(hashes.length) << hashes.map{|h|h.htb.reverse}.join << stop_hash.htb.reverse
20
+ end
21
+
22
+ end
23
+ end
24
+ end