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,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