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.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
data/lib/bitcoin/gui/em_gtk.rb
CHANGED
data/lib/bitcoin/gui/gui.rb
CHANGED
data/lib/bitcoin/gui/helpers.rb
CHANGED
data/lib/bitcoin/gui/tx_view.rb
CHANGED
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
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
data/lib/bitcoin/logger.rb
CHANGED
@@ -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 => "
|
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
|