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,41 @@
1
+ module Bitcoin
2
+
3
+ # Block Header
4
+ class BlockHeader
5
+
6
+ attr_accessor :hash
7
+ attr_accessor :version
8
+ attr_accessor :prev_hash
9
+ attr_accessor :merkle_root
10
+ attr_accessor :time
11
+ attr_accessor :bits
12
+ attr_accessor :nonce
13
+
14
+ def initialize(version, prev_hash, merkle_root, time, bits, nonce)
15
+ @version = version
16
+ @prev_hash = prev_hash
17
+ @merkle_root = merkle_root
18
+ @time = time
19
+ @bits = bits
20
+ @nonce = nonce
21
+ @hash = calc_hash
22
+ end
23
+
24
+ def self.parse_from_payload(payload)
25
+ version, prev_hash, merkle_root, time, bits, nonce = payload.unpack('Va32a32VVV')
26
+ new(version, prev_hash.reverse.bth, merkle_root.reverse.bth, time, bits, nonce)
27
+ end
28
+
29
+ def to_payload
30
+ [version, prev_hash.htb.reverse, merkle_root.htb.reverse, time, bits, nonce].pack('Va32a32VVV')
31
+ end
32
+
33
+ private
34
+
35
+ def calc_hash
36
+ Bitcoin.double_sha256(to_payload).reverse.bth
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,57 @@
1
+ require 'yaml'
2
+
3
+ module Bitcoin
4
+
5
+ # Network parameter class
6
+ class ChainParams
7
+
8
+ attr_reader :network
9
+ attr_reader :magic_head
10
+ attr_reader :message_magic
11
+ attr_reader :address_version
12
+ attr_reader :p2sh_version
13
+ attr_reader :bech32_hrp
14
+ attr_reader :privkey_version
15
+ attr_reader :extended_privkey_version
16
+ attr_reader :extended_pubkey_version
17
+ attr_reader :default_port
18
+ attr_reader :protocol_version
19
+ attr_reader :retarget_interval
20
+ attr_reader :retarget_time
21
+ attr_reader :target_spacing
22
+ attr_reader :max_money
23
+ attr_reader :bip34_height
24
+ attr_reader :genesis_hash
25
+ attr_reader :proof_of_work_limit
26
+ attr_reader :dns_seeds
27
+
28
+ # mainnet params
29
+ def self.mainnet
30
+ YAML.load(File.open("#{__dir__}/chainparams/mainnet.yml"))
31
+ end
32
+
33
+ # testnet params
34
+ def self.testnet
35
+ YAML.load(File.open("#{__dir__}/chainparams/testnet.yml"))
36
+ end
37
+
38
+ # regtest params
39
+ def self.regtest
40
+ YAML.load(File.open("#{__dir__}/chainparams/regtest.yml"))
41
+ end
42
+
43
+ def mainnet?
44
+ network == 'mainnet'
45
+ end
46
+
47
+ def testnet?
48
+ network == 'testnet'
49
+ end
50
+
51
+ def regtest?
52
+ network == 'regtest'
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,25 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "mainnet"
3
+ magic_head: "f9beb4d9"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "00"
6
+ p2sh_version: "05"
7
+ bech32_hrp: 'bc'
8
+ privkey_version: "80"
9
+ extended_privkey_version: "0488ade4"
10
+ extended_pubkey_version: "0488b21e"
11
+ default_port: 8333
12
+ protocol_version: 70013
13
+ retarget_interval: 2016
14
+ retarget_time: 1209600 # 2 weeks
15
+ target_spacing: 600 # block interval
16
+ max_money: 21000000
17
+ bip34_height: 227931
18
+ genesis_hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
19
+ proof_of_work_limit: 0x1d00ffff
20
+ dns_seeds:
21
+ - "seed.bitcoin.sipa.be"
22
+ - "dnsseed.bluematt.me"
23
+ - "dnsseed.bitcoin.dashjr.org"
24
+ - "seed.bitcoinstats.com"
25
+ - "seed.bitcoin.jonasschnelli.ch"
@@ -0,0 +1,20 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "regtest"
3
+ magic_head: "fabfb5da"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "6f"
6
+ p2sh_version: "c4"
7
+ bech32_hrp: 'tb'
8
+ privkey_version: "ef"
9
+ extended_privkey_version: "04358394"
10
+ extended_pubkey_version: "043587cf"
11
+ default_port: 18444
12
+ protocol_version: 70013
13
+ retarget_interval: 2016
14
+ retarget_time: 1209600 # 2 weeks
15
+ target_spacing: 600 # block interval
16
+ max_money: 21000000
17
+ bip34_height: 0
18
+ genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
19
+ proof_of_work_limit: 0x207fffff
20
+ dns_seeds:
@@ -0,0 +1,24 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "testnet"
3
+ magic_head: "0b110907"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "6f"
6
+ p2sh_version: "c4"
7
+ bech32_hrp: 'tb'
8
+ privkey_version: "ef"
9
+ extended_privkey_version: "04358394"
10
+ extended_pubkey_version: "043587cf"
11
+ default_port: 18333
12
+ protocol_version: 70013
13
+ retarget_interval: 2016
14
+ retarget_time: 1209600 # 2 weeks
15
+ target_spacing: 600 # block interval
16
+ max_money: 21000000
17
+ bip34_height: 227931
18
+ genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
19
+ proof_of_work_limit: 0x1d00ffff
20
+ dns_seeds:
21
+ - "testnet-seed.bitcoin.jonasschnelli.ch"
22
+ - "seed.tbtc.petertodd.org"
23
+ - "testnet-seed.bluematt.me"
24
+ - "testnet-seed.bitcoin.schildbach.de"
@@ -0,0 +1,66 @@
1
+ module Bitcoin
2
+
3
+ # Basic Bitcoin P2P connection class
4
+ class Connection < EM::Connection
5
+
6
+ attr_reader :host, :port, :handler, :logger
7
+ attr_accessor :connected
8
+
9
+ # if true, this peer send new block announcements using a headers message rather than an inv message.
10
+ attr_accessor :sendheaders
11
+
12
+ # minimum fee(in satoshis per kilobyte) for relay tx
13
+ attr_accessor :fee_rate
14
+
15
+ def initialize(host, port)
16
+ @host = host
17
+ @port = port
18
+ @logger = Bitcoin::Logger.create(:connection)
19
+ @handler = Message::Handler.new(self, @logger)
20
+ @connected = false
21
+ @sendheaders = false
22
+ @attr_accessor = 0
23
+ end
24
+
25
+ def post_init
26
+ logger.info "connected. #{remote_node}"
27
+ begin_handshake
28
+ end
29
+
30
+ # handle receiving data from remote node.
31
+ def receive_data(data)
32
+ logger.info "receive data from #{remote_node}, data : #{data}"
33
+ handler.handle(data)
34
+ end
35
+
36
+ # close network connection.
37
+ def close(msg = '')
38
+ logger.info "close connection with #{remote_node}. #{msg}"
39
+ close_connection_after_writing
40
+ EM.stop
41
+ end
42
+
43
+ def handshake_done
44
+ logger.info 'handshake finished.'
45
+ @connected = true
46
+ # send_data(Message::SendCmpct.new(0, 1))
47
+
48
+ end
49
+
50
+ private
51
+
52
+ def remote_node
53
+ "#{host}:#{port}"
54
+ end
55
+
56
+ # start handshake
57
+ def begin_handshake
58
+ logger.info "begin handshake with #{remote_node}"
59
+ ver = Bitcoin::Message::Version.new(remote_addr: remote_node, start_height: 1150660)
60
+ logger.info "send version message. #{ver.to_json}"
61
+ send_data(ver.to_pkt)
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,205 @@
1
+ module Bitcoin
2
+
3
+ def self.hmac_sha512(key, data)
4
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA512'), key, data)
5
+ end
6
+
7
+ # Integers modulo the order of the curve(secp256k1)
8
+ CURVE_ORDER = 115792089237316195423570985008687907852837564279074904382605163141518161494337
9
+
10
+ # BIP32 Extended private key
11
+ class ExtKey
12
+
13
+ attr_accessor :depth
14
+ attr_accessor :number
15
+ attr_accessor :chain_code
16
+ attr_accessor :key
17
+ attr_accessor :parent_fingerprint
18
+
19
+ # generate master key from seed.
20
+ def self.generate_master(seed)
21
+ ext_key = ExtKey.new
22
+ ext_key.depth = ext_key.number = 0
23
+ ext_key.parent_fingerprint = '00000000'
24
+ l = Bitcoin.hmac_sha512('Bitcoin seed', seed)
25
+ left = l[0..31].bth.to_i(16)
26
+ raise 'invalid key' if left >= CURVE_ORDER || left == 0
27
+ ext_key.key = Bitcoin::Key.new(priv_key: l[0..31].bth)
28
+ ext_key.chain_code = l[32..-1]
29
+ ext_key
30
+ end
31
+
32
+ # get ExtPubkey from priv_key
33
+ def ext_pubkey
34
+ k = ExtPubkey.new
35
+ k.depth = depth
36
+ k.number = number
37
+ k.parent_fingerprint = parent_fingerprint
38
+ k.chain_code = chain_code
39
+ k.pubkey = key.pubkey
40
+ k
41
+ end
42
+
43
+ # serialize extended private key
44
+ def to_payload
45
+ Bitcoin.chain_params.extended_privkey_version.htb << [depth].pack('C') <<
46
+ parent_fingerprint.htb << [number].pack('N') << chain_code << [0x00].pack('C') << key.priv_key.htb
47
+ end
48
+
49
+ # Base58 encoded extended private key
50
+ def to_base58
51
+ h = to_payload.bth
52
+ hex = h + Bitcoin.calc_checksum(h)
53
+ Base58.encode(hex)
54
+ end
55
+
56
+ # get private key(hex)
57
+ def priv
58
+ key.priv_key
59
+ end
60
+
61
+ # get public key(hex)
62
+ def pub
63
+ key.pubkey
64
+ end
65
+
66
+ # get address
67
+ def addr
68
+ key.to_p2pkh
69
+ end
70
+
71
+ # get segwit p2wpkh address
72
+ def segwit_addr
73
+ ext_pubkey.segwit_addr
74
+ end
75
+
76
+ # get key identifier
77
+ def identifier
78
+ Bitcoin.hash160(key.pubkey)
79
+ end
80
+
81
+ # get fingerprint
82
+ def fingerprint
83
+ identifier.slice(0..7)
84
+ end
85
+
86
+ # derive new key
87
+ def derive(number)
88
+ new_key = ExtKey.new
89
+ new_key.depth = depth + 1
90
+ new_key.number = number
91
+ new_key.parent_fingerprint = fingerprint
92
+ if number > (2**31 -1)
93
+ data = [0x00].pack('C') << key.priv_key.htb << [number].pack('N')
94
+ else
95
+ data = key.pubkey.htb << [number].pack('N')
96
+ end
97
+ l = Bitcoin.hmac_sha512(chain_code, data)
98
+ left = l[0..31].bth.to_i(16)
99
+ raise 'invalid key' if left >= CURVE_ORDER
100
+ child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
101
+ raise 'invalid key ' if child_priv >= CURVE_ORDER
102
+ new_key.key = Bitcoin::Key.new(priv_key: child_priv.to_s(16).rjust(64, '0'))
103
+ new_key.chain_code = l[32..-1]
104
+ new_key
105
+ end
106
+
107
+ # import private key from Base58 private key address
108
+ def self.from_base58(address)
109
+ data = StringIO.new(Base58.decode(address).htb)
110
+ ext_key = ExtKey.new
111
+ data.read(4).bth # version
112
+ ext_key.depth = data.read(1).unpack('C').first
113
+ ext_key.parent_fingerprint = data.read(4).bth
114
+ ext_key.number = data.read(4).unpack('N').first
115
+ ext_key.chain_code = data.read(32)
116
+ data.read(1) # 0x00
117
+ ext_key.key = Bitcoin::Key.new(priv_key: data.read(32).bth)
118
+ ext_key
119
+ end
120
+
121
+ end
122
+
123
+ # BIP-32 Extended public key
124
+ class ExtPubkey
125
+ attr_accessor :depth
126
+ attr_accessor :number
127
+ attr_accessor :chain_code
128
+ attr_accessor :pubkey # hex format
129
+ attr_accessor :parent_fingerprint
130
+
131
+ # serialize extended pubkey
132
+ def to_payload
133
+ Bitcoin.chain_params.extended_pubkey_version.htb << [depth].pack('C') <<
134
+ parent_fingerprint.htb << [number].pack('N') << chain_code << pub.htb
135
+ end
136
+
137
+ def pub
138
+ pubkey
139
+ end
140
+
141
+ # get address
142
+ def addr
143
+ Bitcoin::Key.new(pubkey: pubkey).to_p2pkh
144
+ end
145
+
146
+ # get segwit p2wpkh address
147
+ def segwit_addr
148
+ hash160 = Bitcoin.hash160(pub)
149
+ p2wpkh = [ ["00", "14", hash160].join ].pack("H*").bth
150
+ segwit_addr = Bech32::SegwitAddr.new
151
+ segwit_addr.hrp = Bitcoin.chain_params.address_version == '00' ? 'bc' : 'tb'
152
+ segwit_addr.script_pubkey = p2wpkh
153
+ segwit_addr.addr
154
+ end
155
+
156
+ # get key identifier
157
+ def identifier
158
+ Bitcoin.hash160(pub)
159
+ end
160
+
161
+ # get fingerprint
162
+ def fingerprint
163
+ identifier.slice(0..7)
164
+ end
165
+
166
+ # Base58 encoded extended pubkey
167
+ def to_base58
168
+ h = to_payload.bth
169
+ hex = h + Bitcoin.calc_checksum(h)
170
+ Base58.encode(hex)
171
+ end
172
+
173
+ # derive child key
174
+ def derive(number)
175
+ new_key = ExtPubkey.new
176
+ new_key.depth = depth + 1
177
+ new_key.number = number
178
+ new_key.parent_fingerprint = fingerprint
179
+ raise 'hardened key is not support' if number > (2**31 -1)
180
+ data = pub.htb << [number].pack('N')
181
+ l = Bitcoin.hmac_sha512(chain_code, data)
182
+ left = l[0..31].bth.to_i(16)
183
+ raise 'invalid key' if left >= CURVE_ORDER
184
+ p1 = Bitcoin::Secp256k1::GROUP.generator.multiply_by_scalar(left)
185
+ p2 = Bitcoin::Key.new(pubkey: pubkey).to_point
186
+ new_key.pubkey = ECDSA::Format::PointOctetString.encode(p1 + p2, compression: true).bth
187
+ new_key.chain_code = l[32..-1]
188
+ new_key
189
+ end
190
+
191
+ # import pub key from Base58 private key address
192
+ def self.from_base58(address)
193
+ data = StringIO.new(Base58.decode(address).htb)
194
+ ext_pubkey = ExtPubkey.new
195
+ data.read(4).bth # version
196
+ ext_pubkey.depth = data.read(1).unpack('C').first
197
+ ext_pubkey.parent_fingerprint = data.read(4).bth
198
+ ext_pubkey.number = data.read(4).unpack('N').first
199
+ ext_pubkey.chain_code = data.read(32)
200
+ ext_pubkey.pubkey = data.read(33).bth
201
+ ext_pubkey
202
+ end
203
+ end
204
+
205
+ end