bitcoin-ruby 0.0.1 → 0.0.2

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