bitcoin-ruby 0.0.1 → 0.0.2

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 (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui
2
4
  class AddrView < TreeView
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui
2
4
  class ConnView < TreeView
3
5
  def initialize gui
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui
2
4
 
3
5
  class Bitcoin::Network::CommandClient
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require "monitor"
2
4
 
3
5
  module Gtk
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require_relative "em_gtk.rb"
2
4
  require_relative "helpers.rb"
3
5
  require_relative "tree_view.rb"
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui::Helpers
2
4
 
3
5
  def display_tx tx_hash
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui
2
4
  class TreeView
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin::Gui
2
4
  class TxView < TreeView
3
5
 
data/lib/bitcoin/key.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
 
3
5
  # Elliptic Curve key as used in bitcoin.
@@ -15,10 +17,11 @@ module Bitcoin
15
17
  # See also #to_base58
16
18
  def self.from_base58(str)
17
19
  hex = Bitcoin.decode_base58(str)
18
- version, key, checksum = hex.unpack("a2a64a8")
20
+ compressed = hex.size == 76
21
+ version, key, flag, checksum = hex.unpack("a2a64a#{compressed ? 2 : 0}a8")
19
22
  raise "Invalid version" unless version == Bitcoin.network[:privkey_version]
20
- raise "Invalid checksum" unless Bitcoin.checksum(version + key) == checksum
21
- new(key)
23
+ raise "Invalid checksum" unless Bitcoin.checksum(version + key + flag) == checksum
24
+ key = new(key, nil, compressed)
22
25
  end
23
26
 
24
27
  def == other
@@ -29,8 +32,9 @@ module Bitcoin
29
32
  # Bitcoin::Key.new
30
33
  # Bitcoin::Key.new(privkey)
31
34
  # Bitcoin::Key.new(nil, pubkey)
32
- def initialize privkey = nil, pubkey = nil
35
+ def initialize privkey = nil, pubkey = nil, compressed = false
33
36
  @key = Bitcoin.bitcoin_elliptic_curve
37
+ @pubkey_compressed = pubkey ? self.class.is_compressed_pubkey?(pubkey) : compressed
34
38
  set_priv(privkey) if privkey
35
39
  set_pub(pubkey) if pubkey
36
40
  end
@@ -55,16 +59,29 @@ module Bitcoin
55
59
  # In case the key was initialized with only
56
60
  # a private key, the public key is regenerated.
57
61
  def pub
58
- unless @key.public_key
59
- if @key.private_key
60
- set_pub(Bitcoin::OpenSSL_EC.regenerate_key(priv)[1])
61
- else
62
- return nil
63
- end
64
- end
62
+ @pubkey_compressed ? pub_compressed : pub_uncompressed
63
+ end
64
+
65
+ def pub_compressed
66
+ regenerate_pubkey unless @key.public_key
67
+ return nil unless @key.public_key
68
+ @key.public_key.group.point_conversion_form = :compressed
69
+ hex = @key.public_key.to_hex.rjust(66, '0')
70
+ @key.public_key.group.point_conversion_form = :uncompressed
71
+ hex
72
+ end
73
+
74
+ def pub_uncompressed
75
+ regenerate_pubkey unless @key.public_key
76
+ return nil unless @key.public_key
77
+ @key.public_key.group.point_conversion_form = :uncompressed
65
78
  @key.public_key.to_hex.rjust(130, '0')
66
79
  end
67
80
 
81
+ def compressed
82
+ @pubkey_compressed
83
+ end
84
+
68
85
  # Set the public key (in hex).
69
86
  def pub= pub
70
87
  set_pub(pub)
@@ -94,10 +111,53 @@ module Bitcoin
94
111
  @key.dsa_verify_asn1(data, sig)
95
112
  end
96
113
 
114
+
115
+ def sign_message(message)
116
+ Bitcoin.sign_message(priv, pub, message)['signature']
117
+ end
118
+
119
+ def verify_message(signature, message)
120
+ Bitcoin.verify_message(addr, signature, message)
121
+ end
122
+
123
+ def self.verify_message(address, signature, message)
124
+ Bitcoin.verify_message(address, signature, message)
125
+ end
126
+
127
+ # Thanks to whoever wrote http://pastebin.com/bQtdDzHx
128
+ # for help with compact signatures
129
+ #
130
+ # Given +data+ and a compact signature (65 bytes, base64-encoded to
131
+ # a larger string), recover the public components of the key whose
132
+ # private counterpart validly signed +data+.
133
+ #
134
+ # If the signature validly signed +data+, create a new Key
135
+ # having the signing public key and address. Otherwise return nil.
136
+ #
137
+ # Be sure to check that the returned Key matches the one you were
138
+ # expecting! Otherwise you are merely checking that *someone* validly
139
+ # signed the data.
140
+ def self.recover_compact_signature_to_key(data, signature_base64)
141
+ signature = signature_base64.unpack("m0")[0]
142
+ return nil if signature.size != 65
143
+
144
+ version = signature.unpack('C')[0]
145
+ return nil if version < 27 or version > 34
146
+
147
+ compressed = (version >= 31) ? (version -= 4; true) : false
148
+
149
+ hash = Bitcoin.bitcoin_signed_message_hash(data)
150
+ pub_hex = Bitcoin::OpenSSL_EC.recover_public_key_from_signature(hash, signature, version-27, compressed)
151
+ return nil unless pub_hex
152
+
153
+ Key.new(nil, pub_hex)
154
+ end
155
+
97
156
  # Export private key to base58 format.
98
157
  # See also Key.from_base58
99
158
  def to_base58
100
159
  data = Bitcoin.network[:privkey_version] + priv
160
+ data += "01" if @pubkey_compressed
101
161
  hex = data + Bitcoin.checksum(data)
102
162
  Bitcoin.int_to_base58( hex.to_i(16) )
103
163
  end
@@ -106,6 +166,7 @@ module Bitcoin
106
166
 
107
167
  # Regenerate public key from the private key.
108
168
  def regenerate_pubkey
169
+ return nil unless @key.private_key
109
170
  set_pub(Bitcoin::OpenSSL_EC.regenerate_key(priv)[1])
110
171
  end
111
172
 
@@ -116,9 +177,14 @@ module Bitcoin
116
177
 
117
178
  # Set +pub+ as the new public key (converting from hex).
118
179
  def set_pub(pub)
180
+ @pubkey_compressed ||= self.class.is_compressed_pubkey?(pub)
119
181
  @key.public_key = OpenSSL::PKey::EC::Point.from_hex(@key.group, pub)
120
182
  end
121
183
 
184
+ def self.is_compressed_pubkey?(pub)
185
+ ["02","03"].include?(pub[0..1])
186
+ end
187
+
122
188
  end
123
189
 
124
190
  end
@@ -0,0 +1,81 @@
1
+ require 'openssl'
2
+
3
+ module Litecoin
4
+ module Scrypt
5
+
6
+ def scrypt_1024_1_1_256(input)
7
+ input = [input].pack("H*") if input.bytesize == 160
8
+ #scrypt_1024_1_1_256_sp(input, scratchpad = Array.new(131072 + 63){ 0 })
9
+ scrypt_1024_1_1_256_sp(input)
10
+ end
11
+
12
+ def scrypt_1024_1_1_256_sp(input, scratchpad=[])
13
+ b = pbkdf2_sha256(input, input, 1, 128)
14
+ x = b.unpack("V*")
15
+ v = scratchpad
16
+
17
+ 1024.times{|i|
18
+ v[(i*32)...((i*32)+32)] = x.dup
19
+ xor_salsa8(x, x, 0, 16)
20
+ xor_salsa8(x, x, 16, 0)
21
+ }
22
+
23
+ 1024.times{|i|
24
+ j = 32 * (x[16] & 1023)
25
+ 32.times{|k| x[k] ^= v[j+k] }
26
+ xor_salsa8(x, x, 0, 16)
27
+ xor_salsa8(x, x, 16, 0)
28
+ }
29
+
30
+ pbkdf2_sha256(input, x.pack("V*"), 1, 32).reverse.unpack("H*")[0]
31
+ end
32
+
33
+ def pbkdf2_sha256(pass, salt, c=1, dk_len=128)
34
+ raise "pbkdf2_sha256: wrong length." if pass.bytesize != 80 or ![80,128].include?(salt.bytesize)
35
+ raise "pbkdf2_sha256: wrong dk length." if ![128,32].include?(dk_len)
36
+ OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter=c, dk_len, OpenSSL::Digest::SHA256.new)
37
+ end
38
+
39
+ def rotl(a, b)
40
+ a &= 0xffffffff; ((a << b) | (a >> (32 - b))) & 0xffffffff
41
+ end
42
+
43
+ def xor_salsa8(a, b, a_offset, b_offset)
44
+ x = 16.times.map{|n| a[a_offset+n] ^= b[b_offset+n] }
45
+
46
+ 4.times{
47
+ [
48
+ [4, 0, 12, 7], [9, 5, 1, 7], [14, 10, 6, 7], [3, 15, 11, 7],
49
+ [8, 4, 0, 9], [13, 9, 5, 9], [2, 14, 10, 9], [7, 3, 15, 9],
50
+ [12, 8, 4, 13], [1, 13, 9, 13], [6, 2, 14, 13], [11, 7, 3, 13],
51
+ [0, 12, 8, 18], [5, 1, 13, 18], [10, 6, 2, 18], [15, 11, 7, 18],
52
+
53
+ [1, 0, 3, 7], [6, 5, 4, 7], [11, 10, 9, 7], [12, 15, 14, 7],
54
+ [2, 1, 0, 9], [7, 6, 5, 9], [8, 11, 10, 9], [13, 12, 15, 9],
55
+ [3, 2, 1, 13], [4, 7, 6, 13], [9, 8, 11, 13], [14, 13, 12, 13],
56
+ [0, 3, 2, 18], [5, 4, 7, 18], [10, 9, 8, 18], [15, 14, 13, 18]
57
+ ].each{|i|
58
+ x[ i[0] ] ^= rotl(x[ i[1] ] + x[ i[2] ], i[3])
59
+ }
60
+ }
61
+
62
+ 16.times{|n| a[a_offset+n] = (a[a_offset+n] + x[n]) & 0xffffffff }
63
+ true
64
+ end
65
+
66
+ extend self
67
+ end
68
+ end
69
+
70
+
71
+ if $0 == __FILE__
72
+ secret_hex = "020000004c1271c211717198227392b029a64a7971931d351b387bb80db027f270411e398a07046f7d4a08dd815412a8712f874a7ebf0507e3878bd24e20a3b73fd750a667d2f451eac7471b00de6659"
73
+ p Litecoin::Scrypt.scrypt_1024_1_1_256(secret_hex) == "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806"
74
+
75
+ require 'benchmark'
76
+ secret_bytes = [secret_hex].pack("H*")
77
+ Benchmark.bmbm{|x|
78
+ #x.report("v1"){ p Litecoin::Scrypt.scrypt_1024_1_1_256_sp_old(secret_bytes) == "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806" }
79
+ x.report("v2"){ p Litecoin::Scrypt.scrypt_1024_1_1_256_sp(secret_bytes) == "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806" }
80
+ }
81
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  if Bitcoin.require_dependency :log4r, exit: false
2
4
  # monkey-patch Log4r to accept level names as symbols
3
5
  class Log4r::Logger
@@ -16,9 +18,22 @@ module Bitcoin
16
18
  # this is a very simple logger that is used if log4r is not available
17
19
  module Logger
18
20
 
21
+ module TimeLogger
22
+
23
+ def time message
24
+ time = Time.now
25
+ res = yield
26
+ debug { message % (Time.now - time) }
27
+ res
28
+ end
29
+
30
+ end
31
+
19
32
  class Logger
20
33
  LEVELS = [:debug, :info, :warn, :error, :fatal]
21
34
 
35
+ include TimeLogger
36
+
22
37
  attr_accessor :level
23
38
 
24
39
  def initialize(name)
@@ -36,6 +51,7 @@ module Bitcoin
36
51
  puts "#{level.to_s.upcase.ljust(5)} #{@name}: #{msg}"
37
52
  end
38
53
  end
54
+
39
55
  end
40
56
 
41
57
  # wrap a logger and prepend a special name in front of the messages
@@ -51,10 +67,13 @@ module Bitcoin
51
67
  # otherwise, the internal dummy logger is used which only logs to stdout.
52
68
  def self.create name, level = :info
53
69
  if defined?(Log4r)
70
+ dir = "log"
71
+ FileUtils.mkdir_p(dir) rescue dir = nil
54
72
  @log = Log4r::Logger.new(name.to_s)
73
+ @log.extend(TimeLogger)
55
74
  @log.level = level
56
75
  @log.outputters << Log4r::Outputter.stdout
57
- @log.outputters << Log4r::FileOutputter.new("fout", :filename => "log/#{name}.log")
76
+ @log.outputters << Log4r::FileOutputter.new("fout", :filename => "#{dir}/#{name}.log") if dir
58
77
  else
59
78
  @log = Bitcoin::Logger::Logger.new(name)
60
79
  end
@@ -0,0 +1,279 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # This module includes (almost) everything necessary to add namecoin support
4
+ # to bitcoin-ruby. When switching to a :namecoin network, it will load its
5
+ # functionality into the Script class and the Storage backend.
6
+ # The only things not included here should be parsing the AuxPow, which is
7
+ # done in Protocol::Block directly, and passing the txout to #store_name from
8
+ # the storage backend.
9
+ module Bitcoin::Namecoin
10
+
11
+ def self.load
12
+ Bitcoin::Script.class_eval { include Script }
13
+ Bitcoin::Storage::Backends::StoreBase.class_eval { include Storage::Backend }
14
+ Bitcoin::Storage::Models.class_eval { include Storage::Models }
15
+ end
16
+
17
+ # name_new must have 12 confirmations before corresponding name_firstupdate is valid.
18
+ FIRSTUPDATE_LIMIT = 12
19
+
20
+ # number of blocks after which a name expires.
21
+ EXPIRATION_DEPTH = 36000
22
+
23
+ # Namecoin-specific Script methods for parsing and creating of namecoin scripts,
24
+ # as well as methods to extract address, name_hash, name and value.
25
+ module Script
26
+
27
+ def self.included(base)
28
+ base.constants.each {|c| const_set(c, base.const_get(c)) unless constants.include?(c) }
29
+ base.class_eval do
30
+
31
+ # get the hash160 for this hash160, namecoin or pubkey script
32
+ def get_hash160
33
+ return @chunks[2..-3][0].unpack("H*")[0] if is_hash160?
34
+ return @chunks[-3].unpack("H*")[0] if is_namecoin?
35
+ return Bitcoin.hash160(get_pubkey) if is_pubkey?
36
+ end
37
+
38
+ # get all addresses this script corresponds to (if possible)
39
+ def get_addresses
40
+ return [get_pubkey_address] if is_pubkey?
41
+ return [get_hash160_address] if is_hash160? || is_namecoin?
42
+ return get_multisig_addresses if is_multisig?
43
+ end
44
+
45
+ # get type of this tx
46
+ def type
47
+ if is_name_new?; :name_new
48
+ elsif is_name_firstupdate?; :name_firstupdate
49
+ elsif is_name_update?; :name_update
50
+ elsif is_hash160?; :hash160
51
+ elsif is_pubkey?; :pubkey
52
+ elsif is_multisig?; :multisig
53
+ elsif is_p2sh?; :p2sh
54
+ else; :unknown
55
+ end
56
+ end
57
+
58
+ # is namecoin name_new script
59
+ # OP_1 name_hash OP_2DROP <hash160_script>
60
+ def is_name_new?
61
+ return false if @chunks.size < 8
62
+ [-8, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
63
+ [OP_1, OP_2DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
64
+ end
65
+
66
+ # is namecoin name_firstupdate script
67
+ # OP_2 name rand value OP_2DROP OP_2DROP <hash160_script>
68
+ def is_name_firstupdate?
69
+ return false if @chunks.size < 11
70
+ [-11, -7, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
71
+ [82, OP_2DROP, OP_2DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
72
+ end
73
+
74
+ # is namecoin name_update script
75
+ # OP_3 name value OP_2DROP OP_DROP <hash160_script>
76
+ def is_name_update?
77
+ return false if @chunks.size < 10
78
+ [-10, -7, -6, -5, -4, -2, -1].map {|i| @chunks[i] } ==
79
+ [83, OP_2DROP, OP_DROP, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG]
80
+ end
81
+
82
+ # is any kind of namecoin script
83
+ def is_namecoin?
84
+ is_name_new? || is_name_firstupdate? || is_name_update?
85
+ end
86
+
87
+ # get the name_hash of a namecoin name_new script
88
+ def get_namecoin_hash
89
+ return @chunks[-7].hth if is_name_new?
90
+ if is_name_firstupdate?
91
+ name = @chunks[-10].to_s.hth
92
+ rand = @chunks[-9].to_s.hth
93
+ return Bitcoin.hash160(rand + name)
94
+ end
95
+ rescue
96
+ nil
97
+ end
98
+
99
+ # get the name of a namecoin name_firstupdate or name_update script
100
+ def get_namecoin_name
101
+ return @chunks[-10] if is_name_firstupdate?
102
+ return @chunks[-9] if is_name_update?
103
+ end
104
+
105
+ # get the value of a namecoin name_firstupdate or name_update script
106
+ def get_namecoin_value
107
+ @chunks[-8] if is_name_firstupdate? || is_name_update?
108
+ end
109
+
110
+ # generate name_new tx for given +name+ and +address+.
111
+ # the +caller+ should be the object that creates the script.
112
+ # it gets the used random value passed to #set_rand.
113
+ # OP_1 name_hash OP_2DROP <hash160_script>
114
+ def self.to_name_new_script(caller, name, address)
115
+ rand = rand(2**64).to_s(16).rjust(16, '0')
116
+ name_hash = Bitcoin.hash160(rand + name.unpack("H*")[0])
117
+ caller.set_rand rand # TODO: this is still ugly
118
+ [ [ "51", "14", name_hash, "6d" ].join ].pack("H*") + to_address_script(address)
119
+ end
120
+
121
+ # generate name_firstupdate tx for given +name+, +rand+, +value+ and +address+.
122
+ # OP_2 name rand value OP_2DROP OP_2DROP <hash160_script>
123
+ def self.to_name_firstupdate_script(name, rand, value, address)
124
+ [ [ "52", name.bytesize.to_s(16).rjust(2, '0'), name.hth,
125
+ rand.htb.bytesize.to_s(16).rjust(2, '0'), rand,
126
+ value.bytesize.to_s(16).rjust(2, '0'), value.hth,
127
+ "6d", "6d" ].join ].pack("H*") + to_address_script(address)
128
+ end
129
+
130
+ # generate name_update script for given +name+, +value+ and +address+.
131
+ # OP_3 name value OP_2DROP OP_DROP <hash160_script>
132
+ def self.to_name_update_script(name, value, address)
133
+ [ [ "53", name.bytesize.to_s(16).rjust(2, '0'), name.hth,
134
+ value.bytesize.to_s(16).rjust(2, '0'), value.hth,
135
+ "6d", "75" ].join ].pack("H*") + to_address_script(address)
136
+ end
137
+
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ # Namecoin-specific storage methods.
144
+ # The storage backend only needs to check txout scripts with #is_namecoin? and
145
+ # pass them to #store_name.
146
+ # TODO: move rules into Validation
147
+ module Storage
148
+
149
+ module Backend
150
+
151
+ def self.included(base)
152
+ base.constants.each {|c| const_set(c, base.const_get(c)) unless constants.include?(c) }
153
+ base.class_eval do
154
+
155
+ # if this is a namecoin script, update the names index
156
+ def store_name(script, txout_id)
157
+ if script.type == :name_new
158
+ log.debug { "name_new #{script.get_namecoin_hash}" }
159
+ @db[:names].insert({
160
+ :txout_id => txout_id,
161
+ :hash => script.get_namecoin_hash })
162
+ elsif script.type == :name_firstupdate
163
+ name_hash = script.get_namecoin_hash
164
+ name_new = @db[:names].where(:hash => name_hash).order(:txout_id).first
165
+ if self.class.name =~ /UtxoStore/
166
+ txout = @db[:utxo][id: name_new[:txout_id]] if name_new
167
+ blk = @db[:blk][id: txout[:blk_id]] if txout
168
+ else
169
+ txout = @db[:txout][id: name_new[:txout_id]] if name_new
170
+ tx = @db[:tx][id: txout[:tx_id]] if txout
171
+ blk_tx = @db[:blk_tx][tx_id: tx[:id]] if tx
172
+ blk = @db[:blk][id: blk_tx[:blk_id]] if blk_tx
173
+ end
174
+
175
+ unless name_new && blk && blk[:chain] == 0
176
+ log.debug { "name_new not found: #{name_hash}" }
177
+ return nil
178
+ end
179
+
180
+ unless blk[:depth] <= get_depth - Bitcoin::Namecoin::FIRSTUPDATE_LIMIT
181
+ log.debug { "name_new not yet valid: #{name_hash}" }
182
+ return nil
183
+ end
184
+
185
+ log.debug { "#{script.type}: #{script.get_namecoin_name}" }
186
+ @db[:names].where(:txout_id => name_new[:txout_id], :name => nil).update({
187
+ :name => script.get_namecoin_name.to_s.to_sequel_blob })
188
+ @db[:names].insert({
189
+ :txout_id => txout_id,
190
+ :hash => name_hash,
191
+ :name => script.get_namecoin_name.to_s.to_sequel_blob,
192
+ :value => script.get_namecoin_value.to_s.to_sequel_blob })
193
+ elsif script.type == :name_update
194
+ log.debug { "#{script.type}: #{script.get_namecoin_name}" }
195
+ @db[:names].insert({
196
+ :txout_id => txout_id,
197
+ :name => script.get_namecoin_name.to_s.to_sequel_blob,
198
+ :value => script.get_namecoin_value.to_s.to_sequel_blob })
199
+ end
200
+ end
201
+
202
+ def name_show name
203
+ names = @db[:names].where(:name => name.to_sequel_blob).order(:txout_id).reverse
204
+ return nil unless names.any?
205
+ wrap_name(names.first)
206
+ end
207
+ alias :get_name :name_show
208
+
209
+ def name_history name
210
+ history = @db[:names].where(:name => name.to_sequel_blob)
211
+ .where("value IS NOT NULL").order(:txout_id).map {|n| wrap_name(n) }
212
+ history.select! {|n| n.get_tx.blk_id } unless self.class.name =~ /Utxo/
213
+ history
214
+ end
215
+
216
+ def get_name_by_txout_id txout_id
217
+ wrap_name(@db[:names][:txout_id => txout_id])
218
+ end
219
+
220
+ def wrap_name(data)
221
+ return nil unless data
222
+ Bitcoin::Storage::Models::Name.new(self, data)
223
+ end
224
+
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ module Models
231
+
232
+ class Name
233
+
234
+ attr_reader :store, :txout_id, :hash, :name, :value
235
+
236
+ def initialize store, data
237
+ @store = store
238
+ @txout_id = data[:txout_id]
239
+ @hash = data[:hash]
240
+ @name = data[:name]
241
+ @value = data[:value]
242
+ end
243
+
244
+ def get_txout
245
+ if @txout_id.is_a?(Array)
246
+ @store.get_tx(@txout_id[0]).out[@txout_id[1]]
247
+ else
248
+ @store.get_txout_by_id(@txout_id)
249
+ end
250
+ end
251
+
252
+ def get_address
253
+ get_txout.get_address
254
+ end
255
+
256
+ def get_tx
257
+ get_txout.get_tx rescue nil
258
+ end
259
+
260
+ def get_block
261
+ get_tx.get_block rescue nil
262
+ end
263
+
264
+ def expires_in
265
+ Namecoin::EXPIRATION_DEPTH - (@store.get_depth - get_block.depth) rescue nil
266
+ end
267
+
268
+ def to_json(opts = {})
269
+ JSON.pretty_generate({ name: @name, value: @value, txid: get_tx.hash,
270
+ address: get_address, expires_in: expires_in }, opts)
271
+ end
272
+
273
+ end
274
+
275
+ end
276
+
277
+ end
278
+
279
+ end