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