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/builder.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
|
|
3
5
|
# Optional DSL to help create blocks and transactions.
|
|
@@ -7,19 +9,21 @@ module Bitcoin
|
|
|
7
9
|
|
|
8
10
|
# build a Bitcoin::Protocol::Block matching the given +target+.
|
|
9
11
|
# see BlockBuilder for details.
|
|
10
|
-
def
|
|
12
|
+
def build_block(target = "00".ljust(64, 'f'))
|
|
11
13
|
c = BlockBuilder.new
|
|
12
14
|
yield c
|
|
13
15
|
c.block(target)
|
|
14
16
|
end
|
|
17
|
+
alias :blk :build_block
|
|
15
18
|
|
|
16
19
|
# build a Bitcoin::Protocol::Tx.
|
|
17
20
|
# see TxBuilder for details.
|
|
18
|
-
def
|
|
21
|
+
def build_tx
|
|
19
22
|
c = TxBuilder.new
|
|
20
23
|
yield c
|
|
21
24
|
c.tx
|
|
22
25
|
end
|
|
26
|
+
alias :tx :build_tx
|
|
23
27
|
|
|
24
28
|
# build a Bitcoin::Script.
|
|
25
29
|
# see ScriptBuilder for details.
|
|
@@ -29,7 +33,7 @@ module Bitcoin
|
|
|
29
33
|
c.script
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
# DSL to create a Bitcoin::Protocol::Block used by Builder#
|
|
36
|
+
# DSL to create a Bitcoin::Protocol::Block used by Builder#create_block.
|
|
33
37
|
# block = blk("00".ljust(32, 'f')) do |b|
|
|
34
38
|
# b.prev_block "\x00"*32
|
|
35
39
|
# b.tx do |t|
|
|
@@ -46,7 +50,7 @@ module Bitcoin
|
|
|
46
50
|
class BlockBuilder
|
|
47
51
|
|
|
48
52
|
def initialize
|
|
49
|
-
@block =
|
|
53
|
+
@block = P::Block.new(nil)
|
|
50
54
|
end
|
|
51
55
|
|
|
52
56
|
# specify block version. this is usually not necessary. defaults to 1.
|
|
@@ -59,6 +63,11 @@ module Bitcoin
|
|
|
59
63
|
@prev_block = hash
|
|
60
64
|
end
|
|
61
65
|
|
|
66
|
+
# set the block timestamp (defaults to current time).
|
|
67
|
+
def time time
|
|
68
|
+
@time = time
|
|
69
|
+
end
|
|
70
|
+
|
|
62
71
|
# add transactions to the block (see TxBuilder).
|
|
63
72
|
def tx
|
|
64
73
|
c = TxBuilder.new
|
|
@@ -69,14 +78,15 @@ module Bitcoin
|
|
|
69
78
|
# create the block according to values specified via DSL.
|
|
70
79
|
def block target
|
|
71
80
|
@block.ver = @version || 1
|
|
72
|
-
@block.prev_block =
|
|
81
|
+
@block.prev_block = @prev_block.htb.reverse
|
|
73
82
|
@block.mrkl_root = @mrkl_root
|
|
74
|
-
@block.time = Time.now.to_i
|
|
83
|
+
@block.time = @time || Time.now.to_i
|
|
75
84
|
@block.nonce = 0
|
|
76
|
-
@block.mrkl_root =
|
|
77
|
-
t.hash }).last].pack("H*")
|
|
85
|
+
@block.mrkl_root = Bitcoin.hash_mrkl_tree(@block.tx.map(&:hash)).last.htb.reverse
|
|
78
86
|
find_hash(target)
|
|
79
|
-
|
|
87
|
+
block = P::Block.new(@block.to_payload)
|
|
88
|
+
raise "Payload Error" unless block.to_payload == @block.to_payload
|
|
89
|
+
block
|
|
80
90
|
end
|
|
81
91
|
|
|
82
92
|
private
|
|
@@ -104,22 +114,25 @@ module Bitcoin
|
|
|
104
114
|
|
|
105
115
|
end
|
|
106
116
|
|
|
107
|
-
# DSL to create Bitcoin::Protocol::Tx used by Builder#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
117
|
+
# DSL to create Bitcoin::Protocol::Tx used by Builder#build_tx.
|
|
118
|
+
# tx = tx do |t|
|
|
119
|
+
# t.input do |i|
|
|
120
|
+
# i.prev_out prev_tx # previous transaction
|
|
121
|
+
# i.prev_out_index 0 # index of previous output
|
|
122
|
+
# i.signature_key key # Bitcoin::Key used to sign the input
|
|
123
|
+
# end
|
|
124
|
+
# t.output do |o|
|
|
125
|
+
# o.value 12345 # 0.00012345 BTC
|
|
126
|
+
# o.script {|s| s.type :address; s.recipient key.addr }
|
|
127
|
+
# end
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# signs every input that has a signature key. if the signature key is
|
|
131
|
+
# not specified, the input will include the #sig_hash that needs to be signed.
|
|
119
132
|
class TxBuilder
|
|
120
133
|
|
|
121
134
|
def initialize
|
|
122
|
-
@tx =
|
|
135
|
+
@tx = P::Tx.new(nil)
|
|
123
136
|
@tx.ver, @tx.lock_time = 1, 0
|
|
124
137
|
@ins, @outs = [], []
|
|
125
138
|
end
|
|
@@ -148,7 +161,10 @@ module Bitcoin
|
|
|
148
161
|
@outs << c
|
|
149
162
|
end
|
|
150
163
|
|
|
151
|
-
# create the transaction according to values specified via DSL
|
|
164
|
+
# create the transaction according to values specified via DSL.
|
|
165
|
+
# sign each input that has a signature key specified. if there is
|
|
166
|
+
# no key, store the sig_hash in the input, so it can easily be
|
|
167
|
+
# signed later.
|
|
152
168
|
def tx
|
|
153
169
|
@ins.each {|i| @tx.add_in(i.txin) }
|
|
154
170
|
@outs.each {|o| @tx.add_out(o.txout) }
|
|
@@ -160,26 +176,36 @@ module Bitcoin
|
|
|
160
176
|
next
|
|
161
177
|
end
|
|
162
178
|
prev_tx = inc.instance_variable_get(:@prev_out)
|
|
163
|
-
sig_hash = @tx.signature_hash_for_input(i, prev_tx)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
@sig_hash = @tx.signature_hash_for_input(i, prev_tx)
|
|
180
|
+
if inc.key && inc.key.priv
|
|
181
|
+
sig = inc.key.sign(@sig_hash)
|
|
182
|
+
script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
|
|
183
|
+
@tx.in[i].script_sig_length = script_sig.bytesize
|
|
184
|
+
@tx.in[i].script_sig = script_sig
|
|
185
|
+
raise "Signature error" unless @tx.verify_input_signature(i, prev_tx)
|
|
186
|
+
else
|
|
187
|
+
@tx.in[i].script_sig_length = 0
|
|
188
|
+
@tx.in[i].script_sig = ""
|
|
189
|
+
@tx.in[i].sig_hash = @sig_hash
|
|
190
|
+
@tx.in[i].sig_address = Script.new(prev_tx.out[@tx.in[i].prev_out_index].pk_script).get_address
|
|
191
|
+
end
|
|
169
192
|
end
|
|
170
|
-
|
|
193
|
+
data = @tx.in.map {|i| [i.sig_hash, i.sig_address] }
|
|
194
|
+
tx = P::Tx.new(@tx.to_payload)
|
|
195
|
+
data.each.with_index {|d, i| i = tx.in[i]; i.sig_hash = d[0]; i.sig_address = d[1] }
|
|
196
|
+
raise "Payload Error" unless tx.to_payload == @tx.to_payload
|
|
197
|
+
tx
|
|
171
198
|
end
|
|
172
199
|
end
|
|
173
200
|
|
|
174
201
|
# create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
|
|
175
202
|
#
|
|
176
|
-
# inputs
|
|
177
|
-
# or they have to define a #prev_out, #prev_out_index and #signature key.
|
|
203
|
+
# inputs need a #prev_out tx and #prev_out_index of the output they spend.
|
|
178
204
|
class TxInBuilder
|
|
179
205
|
attr_reader :key, :coinbase_data
|
|
180
206
|
|
|
181
207
|
def initialize
|
|
182
|
-
@txin =
|
|
208
|
+
@txin = P::TxIn.new
|
|
183
209
|
end
|
|
184
210
|
|
|
185
211
|
# previous transaction that contains the output we want to use.
|
|
@@ -224,7 +250,7 @@ module Bitcoin
|
|
|
224
250
|
attr_reader :script
|
|
225
251
|
|
|
226
252
|
def initialize
|
|
227
|
-
@type =
|
|
253
|
+
@type = :address
|
|
228
254
|
@script = nil
|
|
229
255
|
end
|
|
230
256
|
|
|
@@ -236,7 +262,7 @@ module Bitcoin
|
|
|
236
262
|
# recipient(s) of the script.
|
|
237
263
|
# depending on the #type, either an address, hash160 pubkey, etc.
|
|
238
264
|
def recipient *data
|
|
239
|
-
@script =
|
|
265
|
+
@script = Script.send("to_#{@type}_script", *data)
|
|
240
266
|
end
|
|
241
267
|
end
|
|
242
268
|
|
|
@@ -245,7 +271,7 @@ module Bitcoin
|
|
|
245
271
|
attr_reader :txout
|
|
246
272
|
|
|
247
273
|
def initialize
|
|
248
|
-
@txout =
|
|
274
|
+
@txout = P::TxOut.new
|
|
249
275
|
end
|
|
250
276
|
|
|
251
277
|
# set output value (in base units / "satoshis")
|
data/lib/bitcoin/config.rb
CHANGED
data/lib/bitcoin/connection.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
require 'socket'
|
|
2
4
|
require 'eventmachine'
|
|
3
5
|
require 'bitcoin'
|
|
@@ -5,27 +7,24 @@ require 'bitcoin'
|
|
|
5
7
|
module Bitcoin
|
|
6
8
|
|
|
7
9
|
module ConnectionHandler
|
|
8
|
-
def hth(h); h.unpack("H*")[0]; end
|
|
9
|
-
def htb(h); [h].pack("H*"); end
|
|
10
|
-
|
|
11
10
|
def on_inv_transaction(hash)
|
|
12
|
-
p ['inv transaction', hth
|
|
11
|
+
p ['inv transaction', hash.hth]
|
|
13
12
|
pkt = Protocol.getdata_pkt(:tx, [hash])
|
|
14
13
|
send_data(pkt)
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def on_inv_block(hash)
|
|
18
|
-
p ['inv block', hth
|
|
17
|
+
p ['inv block', hash.hth]
|
|
19
18
|
pkt = Protocol.getdata_pkt(:block, [hash])
|
|
20
19
|
send_data(pkt)
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def on_get_transaction(hash)
|
|
24
|
-
p ['get transaction', hth
|
|
23
|
+
p ['get transaction', hash.hth]
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
def on_get_block(hash)
|
|
28
|
-
p ['get block', hth
|
|
27
|
+
p ['get block', hash.hth]
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
def on_addr(addr)
|
|
@@ -43,10 +42,14 @@ module Bitcoin
|
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def on_version(version)
|
|
46
|
-
p [@sockaddr, 'version', version, version.
|
|
45
|
+
p [@sockaddr, 'version', version, version.time - Time.now.to_i]
|
|
47
46
|
send_data( Protocol.verack_pkt )
|
|
48
47
|
end
|
|
49
48
|
|
|
49
|
+
def on_verack
|
|
50
|
+
on_handshake_complete
|
|
51
|
+
end
|
|
52
|
+
|
|
50
53
|
def on_handshake_complete
|
|
51
54
|
p [@sockaddr, 'handshake complete']
|
|
52
55
|
@connected = true
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
class Mnemonic
|
|
4
|
+
# ruby version of: https://github.com/spesmilo/electrum/blob/master/lib/mnemonic.py
|
|
5
|
+
|
|
6
|
+
# list of words from http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/Contemporary_poetry
|
|
7
|
+
Words = (<<-TEXT).split
|
|
8
|
+
like just love know never want time out there make look eye down only think
|
|
9
|
+
heart back then into about more away still them take thing even through long always
|
|
10
|
+
world too friend tell try hand thought over here other need smile again much cry
|
|
11
|
+
been night ever little said end some those around mind people girl leave dream left
|
|
12
|
+
turn myself give nothing really off before something find walk wish good once place ask
|
|
13
|
+
stop keep watch seem everything wait got yet made remember start alone run hope maybe
|
|
14
|
+
believe body hate after close talk stand own each hurt help home god soul new
|
|
15
|
+
many two inside should true first fear mean better play another gone change use wonder
|
|
16
|
+
someone hair cold open best any behind happen water dark laugh stay forever name work
|
|
17
|
+
show sky break came deep door put black together upon happy such great white matter
|
|
18
|
+
fill past please burn cause enough touch moment soon voice scream anything stare sound red
|
|
19
|
+
everyone hide kiss truth death beautiful mine blood broken very pass next forget tree wrong
|
|
20
|
+
air mother understand lip hit wall memory sleep free high realize school might skin sweet
|
|
21
|
+
perfect blue kill breath dance against fly between grow strong under listen bring sometimes speak
|
|
22
|
+
pull person become family begin ground real small father sure feet rest young finally land
|
|
23
|
+
across today different guy line fire reason reach second slowly write eat smell mouth step
|
|
24
|
+
learn three floor promise breathe darkness push earth guess save song above along both color
|
|
25
|
+
house almost sorry anymore brother okay dear game fade already apart warm beauty heard notice
|
|
26
|
+
question shine began piece whole shadow secret street within finger point morning whisper child moon
|
|
27
|
+
green story glass kid silence since soft yourself empty shall angel answer baby bright dad
|
|
28
|
+
path worry hour drop follow power war half flow heaven act chance fact least tired
|
|
29
|
+
children near quite afraid rise sea taste window cover nice trust lot sad cool force
|
|
30
|
+
peace return blind easy ready roll rose drive held music beneath hang mom paint emotion
|
|
31
|
+
quiet clear cloud few pretty bird outside paper picture front rock simple anyone meant reality
|
|
32
|
+
road sense waste bit leaf thank happiness meet men smoke truly decide self age book
|
|
33
|
+
form alive carry escape damn instead able ice minute throw catch leg ring course goodbye
|
|
34
|
+
lead poem sick corner desire known problem remind shoulder suppose toward wave drink jump woman
|
|
35
|
+
pretend sister week human joy crack grey pray surprise dry knee less search bleed caught
|
|
36
|
+
clean embrace future king son sorrow chest hug remain sat worth blow daddy final parent
|
|
37
|
+
tight also create lonely safe cross dress evil silent bone fate perhaps anger class scar
|
|
38
|
+
snow tiny tonight continue control dog edge mirror month suddenly comfort given loud quickly gaze
|
|
39
|
+
plan rush stone town battle ignore spirit stood stupid yours brown build dust hey kept
|
|
40
|
+
pay phone twist although ball beyond hidden nose taken fail float pure somehow wash wrap
|
|
41
|
+
angry cheek creature forgotten heat rip single space special weak whatever yell anyway blame job
|
|
42
|
+
choose country curse drift echo figure grew laughter neck suffer worse yeah disappear foot forward
|
|
43
|
+
knife mess somewhere stomach storm beg idea lift offer breeze field five often simply stuck
|
|
44
|
+
win allow confuse enjoy except flower seek strength calm grin gun heavy hill large ocean
|
|
45
|
+
shoe sigh straight summer tongue accept crazy everyday exist grass mistake sent shut surround table
|
|
46
|
+
ache brain destroy heal nature shout sign stain choice doubt glance glow mountain queen stranger
|
|
47
|
+
throat tomorrow city either fish flame rather shape spin spread ash distance finish image imagine
|
|
48
|
+
important nobody shatter warmth became feed flesh funny lust shirt trouble yellow attention bare bite
|
|
49
|
+
money protect amaze appear born choke completely daughter fresh friendship gentle probably six deserve expect
|
|
50
|
+
grab middle nightmare river thousand weight worst wound barely bottle cream regret relationship stick test
|
|
51
|
+
crush endless fault itself rule spill art circle join kick mask master passion quick raise
|
|
52
|
+
smooth unless wander actually broke chair deal favorite gift note number sweat box chill clothes
|
|
53
|
+
lady mark park poor sadness tie animal belong brush consume dawn forest innocent pen pride
|
|
54
|
+
stream thick clay complete count draw faith press silver struggle surface taught teach wet bless
|
|
55
|
+
chase climb enter letter melt metal movie stretch swing vision wife beside crash forgot guide
|
|
56
|
+
haunt joke knock plant pour prove reveal steal stuff trip wood wrist bother bottom crawl
|
|
57
|
+
crowd fix forgive frown grace loose lucky party release surely survive teacher gently grip speed
|
|
58
|
+
suicide travel treat vein written cage chain conversation date enemy however interest million page pink
|
|
59
|
+
proud sway themselves winter church cruel cup demon experience freedom pair pop purpose respect shoot
|
|
60
|
+
softly state strange bar birth curl dirt excuse lord lovely monster order pack pants pool
|
|
61
|
+
scene seven shame slide ugly among blade blonde closet creek deny drug eternity gain grade
|
|
62
|
+
handle key linger pale prepare swallow swim tremble wheel won cast cigarette claim college direction
|
|
63
|
+
dirty gather ghost hundred loss lung orange present swear swirl twice wild bitter blanket doctor
|
|
64
|
+
everywhere flash grown knowledge numb pressure radio repeat ruin spend unknown buy clock devil early
|
|
65
|
+
false fantasy pound precious refuse sheet teeth welcome add ahead block bury caress content depth
|
|
66
|
+
despite distant marry purple threw whenever bomb dull easily grasp hospital innocence normal receive reply
|
|
67
|
+
rhyme shade someday sword toe visit asleep bought center consider flat hero history ink insane
|
|
68
|
+
muscle mystery pocket reflection shove silently smart soldier spot stress train type view whether bus
|
|
69
|
+
energy explain holy hunger inch magic mix noise nowhere prayer presence shock snap spider study
|
|
70
|
+
thunder trail admit agree bag bang bound butterfly cute exactly explode familiar fold further pierce
|
|
71
|
+
reflect scent selfish sharp sink spring stumble universe weep women wonderful action ancient attempt avoid
|
|
72
|
+
birthday branch chocolate core depress drunk especially focus fruit honest match palm perfectly pillow pity
|
|
73
|
+
poison roar shift slightly thump truck tune twenty unable wipe wrote coat constant dinner drove
|
|
74
|
+
egg eternal flight flood frame freak gasp glad hollow motion peer plastic root screen season
|
|
75
|
+
sting strike team unlike victim volume warn weird attack await awake built charm crave despair
|
|
76
|
+
fought grant grief horse limit message ripple sanity scatter serve split string trick annoy blur
|
|
77
|
+
boat brave clearly cling connect fist forth imagination iron jock judge lesson milk misery nail
|
|
78
|
+
naked ourselves poet possible princess sail size snake society stroke torture toss trace wise bloom
|
|
79
|
+
bullet cell check cost darling during footstep fragile hallway hardly horizon invisible journey midnight mud
|
|
80
|
+
nod pause relax shiver sudden value youth abuse admire blink breast bruise constantly couple creep
|
|
81
|
+
curve difference dumb emptiness gotta honor plain planet recall rub ship slam soar somebody tightly
|
|
82
|
+
weather adore approach bond bread burst candle coffee cousin crime desert flutter frozen grand heel
|
|
83
|
+
hello language level movement pleasure powerful random rhythm settle silly slap sort spoken steel threaten
|
|
84
|
+
tumble upset aside awkward bee blank board button card carefully complain crap deeply discover drag
|
|
85
|
+
dread effort entire fairy giant gotten greet illusion jeans leap liquid march mend nervous nine
|
|
86
|
+
replace rope spine stole terror accident apple balance boom childhood collect demand depression eventually faint
|
|
87
|
+
glare goal group honey kitchen laid limb machine mere mold murder nerve painful poetry prince
|
|
88
|
+
rabbit shelter shore shower soothe stair steady sunlight tangle tease treasure uncle begun bliss canvas
|
|
89
|
+
cheer claw clutch commit crimson crystal delight doll existence express fog football gay goose guard
|
|
90
|
+
hatred illuminate mass math mourn rich rough skip stir student style support thorn tough yard
|
|
91
|
+
yearn yesterday advice appreciate autumn bank beam bowl capture carve collapse confusion creation dove feather
|
|
92
|
+
girlfriend glory government harsh hop inner loser moonlight neighbor neither peach pig praise screw shield
|
|
93
|
+
shimmer sneak stab subject throughout thrown tower twirl wow army arrive bathroom bump cease cookie
|
|
94
|
+
couch courage dim guilt howl hum husband insult led lunch mock mostly natural nearly needle
|
|
95
|
+
nerd peaceful perfection pile price remove roam sanctuary serious shiny shook sob stolen tap vain
|
|
96
|
+
void warrior wrinkle affection apologize blossom bounce bridge cheap crumble decision descend desperately dig dot
|
|
97
|
+
flip frighten heartbeat huge lazy lick odd opinion process puzzle quietly retreat score sentence separate
|
|
98
|
+
situation skill soak square stray taint task tide underneath veil whistle anywhere bedroom bid bloody
|
|
99
|
+
burden careful compare concern curtain decay defeat describe double dreamer driver dwell evening flare flicker
|
|
100
|
+
grandma guitar harm horrible hungry indeed lace melody monkey nation object obviously rainbow salt scratch
|
|
101
|
+
shown shy stage stun third tickle useless weakness worship worthless afternoon beard boyfriend bubble busy
|
|
102
|
+
certain chin concrete desk diamond doom drawn due felicity freeze frost garden glide harmony hopefully
|
|
103
|
+
hunt jealous lightning mama mercy peel physical position pulse punch quit rant respond salty sane
|
|
104
|
+
satisfy savior sheep slept social sport tuck utter valley wolf aim alas alter arrow awaken
|
|
105
|
+
beaten belief brand ceiling cheese clue confidence connection daily disguise eager erase essence everytime expression
|
|
106
|
+
fan flag flirt foul fur giggle glorious ignorance law lifeless measure mighty muse north opposite
|
|
107
|
+
paradise patience patient pencil petal plate ponder possibly practice slice spell stock strife strip suffocate
|
|
108
|
+
suit tender tool trade velvet verse waist witch aunt bench bold cap certainly click companion
|
|
109
|
+
creator dart delicate determine dish dragon drama drum dude everybody feast forehead former fright fully
|
|
110
|
+
gas hook hurl invite juice manage moral possess raw rebel royal scale scary several slight
|
|
111
|
+
stubborn swell talent tea terrible thread torment trickle usually vast violence weave acid agony ashamed
|
|
112
|
+
awe belly blend blush character cheat common company coward creak danger deadly defense define depend
|
|
113
|
+
desperate destination dew duck dusty embarrass engine example explore foe freely frustrate generation glove guilty
|
|
114
|
+
health hurry idiot impossible inhale jaw kingdom mention mist moan mumble mutter observe ode pathetic
|
|
115
|
+
pattern pie prefer puff rape rare revenge rude scrape spiral squeeze strain sunset suspend sympathy
|
|
116
|
+
thigh throne total unseen weapon weary
|
|
117
|
+
TEXT
|
|
118
|
+
|
|
119
|
+
def self.encode(hex, words=Words)
|
|
120
|
+
n = words.size
|
|
121
|
+
[hex].pack("H*").unpack("N*").map{|x|
|
|
122
|
+
w1 = x % n
|
|
123
|
+
w2 = ((x / n) + w1) % n
|
|
124
|
+
w3 = ((x / n / n) + w2) % n
|
|
125
|
+
[ words[w1], words[w2], words[w3] ]
|
|
126
|
+
}.flatten
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.decode(word_list, words=Words)
|
|
130
|
+
n = words.size
|
|
131
|
+
word_list.each_slice(3).map{|three_words|
|
|
132
|
+
w1, w2, w3 = three_words.map{|word| words.index(word) % n }
|
|
133
|
+
'%08x' % ( w1 + n*((w2-w1)%n) + n*n*((w3-w2)%n) )
|
|
134
|
+
}.join
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if $0 == __FILE__
|
|
142
|
+
hex = "4c7d10656aa55383a5d88e3f63300af5e169918f4058bf349d99b20239909b61"
|
|
143
|
+
expected_words = ['horse', 'love', 'nose', 'speak', 'diamond', 'gaze', 'wash', 'drag', 'glance',
|
|
144
|
+
'money', 'cease', 'soft', 'complete', 'huge', 'aside', 'confusion', 'touch',
|
|
145
|
+
'grass', 'pie', 'play', 'bread', 'exactly', 'bubble', 'great']
|
|
146
|
+
|
|
147
|
+
# from http://brainwallet.org/#chains
|
|
148
|
+
hex = "ff64b72c431f75799f0c5ebe438e46dd"
|
|
149
|
+
expected_words = %w[muscle lot sea got revenge crack wait yeah gas study embrace spend]
|
|
150
|
+
hex = "18cfaf96961750c7a4e4e39c861d1415"
|
|
151
|
+
expected_words = %w[example poor twice expect decision master blame rub forward easy jump carve]
|
|
152
|
+
|
|
153
|
+
# from https://en.bitcoin.it/wiki/Electrum#Brain_Wallet
|
|
154
|
+
hex = "431a62f1c86555d3c45e5c4d9e10c8c7"
|
|
155
|
+
expected_words = %w[constant forest adore false green weave stop guy fur freeze giggle clock]
|
|
156
|
+
|
|
157
|
+
#p Mnemonic.encode(hex)
|
|
158
|
+
p Mnemonic.encode(hex) == expected_words
|
|
159
|
+
#p Mnemonic.decode(expected_words)
|
|
160
|
+
p Mnemonic.decode(expected_words) == hex
|
|
161
|
+
end
|
|
162
|
+
|
data/lib/bitcoin/ffi/openssl.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
# autoload when you need to re-generate a public_key from only its private_key.
|
|
2
4
|
# ported from: https://github.com/sipa/bitcoin/blob/2d40fe4da9ea82af4b652b691a4185431d6e47a8/key.h
|
|
3
5
|
|
|
4
|
-
Bitcoin.require_dependency :ffi, exit: false, message:
|
|
5
|
-
"Skipping FFI needed for OpenSSL_EC.regenerate_key."
|
|
6
|
+
Bitcoin.require_dependency :ffi, exit: false, message: "Skipping FFI needed for OpenSSL_EC methods."
|
|
6
7
|
|
|
7
8
|
module Bitcoin
|
|
8
9
|
module OpenSSL_EC
|
|
@@ -10,32 +11,54 @@ module OpenSSL_EC
|
|
|
10
11
|
ffi_lib 'ssl'
|
|
11
12
|
|
|
12
13
|
NID_secp256k1 = 714
|
|
14
|
+
POINT_CONVERSION_COMPRESSED = 2
|
|
15
|
+
POINT_CONVERSION_UNCOMPRESSED = 4
|
|
13
16
|
|
|
14
17
|
attach_function :SSL_library_init, [], :int
|
|
15
18
|
attach_function :ERR_load_crypto_strings, [], :void
|
|
16
19
|
attach_function :SSL_load_error_strings, [], :void
|
|
17
20
|
attach_function :RAND_poll, [], :int
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
attach_function :BN_CTX_free, [:pointer], :int
|
|
23
|
+
attach_function :BN_CTX_new, [], :pointer
|
|
24
|
+
attach_function :BN_add, [:pointer, :pointer, :pointer], :int
|
|
20
25
|
attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
|
|
21
|
-
attach_function :
|
|
22
|
-
attach_function :
|
|
26
|
+
attach_function :BN_bn2bin, [:pointer, :pointer], :void
|
|
27
|
+
attach_function :BN_cmp, [:pointer, :pointer], :int
|
|
28
|
+
attach_function :BN_copy, [:pointer, :pointer], :pointer
|
|
29
|
+
attach_function :BN_dup, [:pointer], :pointer
|
|
30
|
+
attach_function :BN_free, [:pointer], :int
|
|
31
|
+
attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
|
|
32
|
+
attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
|
33
|
+
attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
|
34
|
+
attach_function :BN_mul_word, [:pointer, :int], :int
|
|
23
35
|
attach_function :BN_new, [], :pointer
|
|
24
|
-
attach_function :
|
|
36
|
+
attach_function :BN_rshift, [:pointer, :pointer, :int], :int
|
|
37
|
+
attach_function :BN_set_word, [:pointer, :int], :int
|
|
38
|
+
attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
|
39
|
+
attach_function :EC_GROUP_get_degree, [:pointer], :int
|
|
25
40
|
attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
|
|
26
|
-
attach_function :
|
|
27
|
-
attach_function :
|
|
41
|
+
attach_function :EC_KEY_free, [:pointer], :int
|
|
42
|
+
attach_function :EC_KEY_get0_group, [:pointer], :pointer
|
|
43
|
+
attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
|
|
44
|
+
attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
|
|
45
|
+
attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
|
|
28
46
|
attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
|
|
29
47
|
attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
|
|
30
|
-
attach_function :BN_free, [:pointer], :int
|
|
31
48
|
attach_function :EC_POINT_free, [:pointer], :int
|
|
32
|
-
attach_function :
|
|
33
|
-
attach_function :
|
|
34
|
-
attach_function :
|
|
35
|
-
attach_function :
|
|
49
|
+
attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
|
|
50
|
+
attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
|
|
51
|
+
attach_function :EC_POINT_new, [:pointer], :pointer
|
|
52
|
+
attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
|
|
36
53
|
attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
|
|
37
|
-
attach_function :
|
|
38
|
-
attach_function :
|
|
54
|
+
attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
|
|
55
|
+
attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
|
|
56
|
+
attach_function :EC_KEY_check_key, [:pointer], :uint
|
|
57
|
+
attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
|
|
58
|
+
attach_function :BN_num_bits, [:pointer], :int
|
|
59
|
+
attach_function :ECDSA_SIG_free, [:pointer], :void
|
|
60
|
+
|
|
61
|
+
def self.BN_num_bytes(ptr); (BN_num_bits(ptr) + 7) / 8; end
|
|
39
62
|
|
|
40
63
|
|
|
41
64
|
# resolve public from private key, using ffi and libssl.so
|
|
@@ -48,7 +71,7 @@ module OpenSSL_EC
|
|
|
48
71
|
#private_key = FFI::MemoryPointer.new(:uint8, private_key.bytesize)
|
|
49
72
|
# .put_bytes(0, private_key, 0, private_key.bytesize)
|
|
50
73
|
private_key = FFI::MemoryPointer.from_string(private_key)
|
|
51
|
-
|
|
74
|
+
|
|
52
75
|
init_ffi_ssl
|
|
53
76
|
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
|
54
77
|
#priv_key = BN_bin2bn(private_key, private_key.size, BN_new())
|
|
@@ -69,10 +92,11 @@ module OpenSSL_EC
|
|
|
69
92
|
|
|
70
93
|
|
|
71
94
|
length = i2d_ECPrivateKey(eckey, nil)
|
|
72
|
-
|
|
95
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
|
96
|
+
ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
|
|
73
97
|
priv_hex = if i2d_ECPrivateKey(eckey, ptr) == length
|
|
74
|
-
size =
|
|
75
|
-
|
|
98
|
+
size = buf.get_array_of_uint8(8, 1)[0]
|
|
99
|
+
buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00").unpack("H*")[0]
|
|
76
100
|
#der_to_private_key( ptr.read_pointer.read_string(length).unpack("H*")[0] )
|
|
77
101
|
end
|
|
78
102
|
|
|
@@ -82,9 +106,10 @@ module OpenSSL_EC
|
|
|
82
106
|
|
|
83
107
|
|
|
84
108
|
length = i2o_ECPublicKey(eckey, nil)
|
|
85
|
-
|
|
109
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
|
110
|
+
ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
|
|
86
111
|
pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
|
|
87
|
-
|
|
112
|
+
buf.read_string(length).unpack("H*")[0]
|
|
88
113
|
end
|
|
89
114
|
|
|
90
115
|
EC_KEY_free(eckey)
|
|
@@ -109,6 +134,147 @@ module OpenSSL_EC
|
|
|
109
134
|
buf.read_string(32).unpack("H*")[0]
|
|
110
135
|
end
|
|
111
136
|
|
|
137
|
+
# Given the components of a signature and a selector value, recover and
|
|
138
|
+
# return the public key that generated the signature according to the
|
|
139
|
+
# algorithm in SEC1v2 section 4.1.6.
|
|
140
|
+
#
|
|
141
|
+
# rec_id is an index from 0 to 3 that indicates which of the 4 possible
|
|
142
|
+
# keys is the correct one. Because the key recovery operation yields
|
|
143
|
+
# multiple potential keys, the correct key must either be stored alongside
|
|
144
|
+
# the signature, or you must be willing to try each rec_id in turn until
|
|
145
|
+
# you find one that outputs the key you are expecting.
|
|
146
|
+
#
|
|
147
|
+
# If this method returns nil, it means recovery was not possible and rec_id
|
|
148
|
+
# should be iterated.
|
|
149
|
+
#
|
|
150
|
+
# Given the above two points, a correct usage of this method is inside a
|
|
151
|
+
# for loop from 0 to 3, and if the output is nil OR a key that is not the
|
|
152
|
+
# one you expect, you try again with the next rec_id.
|
|
153
|
+
#
|
|
154
|
+
# message_hash = hash of the signed message.
|
|
155
|
+
# signature = the R and S components of the signature, wrapped.
|
|
156
|
+
# rec_id = which possible key to recover.
|
|
157
|
+
# is_compressed = whether or not the original pubkey was compressed.
|
|
158
|
+
def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
|
|
159
|
+
return nil if rec_id < 0 or signature.bytesize != 65
|
|
160
|
+
init_ffi_ssl
|
|
161
|
+
|
|
162
|
+
signature = FFI::MemoryPointer.from_string(signature)
|
|
163
|
+
#signature_bn = BN_bin2bn(signature, 65, BN_new())
|
|
164
|
+
r = BN_bin2bn(signature[1], 32, BN_new())
|
|
165
|
+
s = BN_bin2bn(signature[33], 32, BN_new())
|
|
166
|
+
|
|
167
|
+
n, i = 0, rec_id / 2
|
|
168
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
|
169
|
+
|
|
170
|
+
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
|
|
171
|
+
|
|
172
|
+
group = EC_KEY_get0_group(eckey)
|
|
173
|
+
order = BN_new()
|
|
174
|
+
EC_GROUP_get_order(group, order, nil)
|
|
175
|
+
x = BN_dup(order)
|
|
176
|
+
BN_mul_word(x, i)
|
|
177
|
+
BN_add(x, x, r)
|
|
178
|
+
|
|
179
|
+
field = BN_new()
|
|
180
|
+
EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
|
|
181
|
+
|
|
182
|
+
if BN_cmp(x, field) >= 0
|
|
183
|
+
[r, s, order, x, field].each{|i| BN_free(i) }
|
|
184
|
+
EC_KEY_free(eckey)
|
|
185
|
+
return nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
big_r = EC_POINT_new(group)
|
|
189
|
+
EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
|
|
190
|
+
|
|
191
|
+
big_q = EC_POINT_new(group)
|
|
192
|
+
n = EC_GROUP_get_degree(group)
|
|
193
|
+
e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
|
|
194
|
+
BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
|
|
195
|
+
|
|
196
|
+
ctx = BN_CTX_new()
|
|
197
|
+
zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
|
|
198
|
+
BN_set_word(zero, 0)
|
|
199
|
+
BN_mod_sub(e, zero, e, order, ctx)
|
|
200
|
+
BN_mod_inverse(rr, r, order, ctx)
|
|
201
|
+
BN_mod_mul(sor, s, rr, order, ctx)
|
|
202
|
+
BN_mod_mul(eor, e, rr, order, ctx)
|
|
203
|
+
EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
|
|
204
|
+
EC_KEY_set_public_key(eckey, big_q)
|
|
205
|
+
BN_CTX_free(ctx)
|
|
206
|
+
|
|
207
|
+
[r, s, order, x, field, e, zero, rr, sor, eor].each{|i| BN_free(i) }
|
|
208
|
+
[big_r, big_q].each{|i| EC_POINT_free(i) }
|
|
209
|
+
|
|
210
|
+
length = i2o_ECPublicKey(eckey, nil)
|
|
211
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
|
212
|
+
ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
|
|
213
|
+
pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
|
|
214
|
+
buf.read_string(length).unpack("H*")[0]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
EC_KEY_free(eckey)
|
|
218
|
+
|
|
219
|
+
pub_hex
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
|
|
223
|
+
private_key = [private_key].pack("H*") if private_key.bytesize >= 64
|
|
224
|
+
private_key_hex = private_key.unpack("H*")[0]
|
|
225
|
+
|
|
226
|
+
public_key_hex = regenerate_key(private_key_hex).last unless public_key_hex
|
|
227
|
+
pubkey_compressed = (public_key_hex[0..1] == "04" ? false : true) unless pubkey_compressed
|
|
228
|
+
|
|
229
|
+
init_ffi_ssl
|
|
230
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
|
231
|
+
priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
|
|
232
|
+
|
|
233
|
+
group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
|
|
234
|
+
EC_GROUP_get_order(group, order, ctx)
|
|
235
|
+
|
|
236
|
+
pub_key = EC_POINT_new(group)
|
|
237
|
+
EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
|
|
238
|
+
EC_KEY_set_private_key(eckey, priv_key)
|
|
239
|
+
EC_KEY_set_public_key(eckey, pub_key)
|
|
240
|
+
|
|
241
|
+
signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
|
|
242
|
+
|
|
243
|
+
BN_free(order)
|
|
244
|
+
BN_CTX_free(ctx)
|
|
245
|
+
EC_POINT_free(pub_key)
|
|
246
|
+
BN_free(priv_key)
|
|
247
|
+
EC_KEY_free(eckey)
|
|
248
|
+
|
|
249
|
+
buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
|
|
250
|
+
r, s = signature.get_array_of_pointer(0, 2).map{|i| BN_bn2bin(i, buf); buf.read_string(BN_num_bytes(i)).rjust(32, "\x00") }
|
|
251
|
+
|
|
252
|
+
if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
|
|
253
|
+
4.times{|i|
|
|
254
|
+
head = [ 27 + i + (pubkey_compressed ? 4 : 0) ].pack("C")
|
|
255
|
+
if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
|
|
256
|
+
rec_id = i; break
|
|
257
|
+
end
|
|
258
|
+
}
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
ECDSA_SIG_free(signature)
|
|
262
|
+
|
|
263
|
+
[ head, [r,s] ].join if rec_id
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def self.recover_compact(hash, signature)
|
|
267
|
+
return false if signature.bytesize != 65
|
|
268
|
+
#i = signature.unpack("C")[0] - 27
|
|
269
|
+
#pubkey = recover_public_key_from_signature(hash, signature, (i & ~4), i >= 4)
|
|
270
|
+
|
|
271
|
+
version = signature.unpack('C')[0]
|
|
272
|
+
return false if version < 27 or version > 34
|
|
273
|
+
|
|
274
|
+
compressed = (version >= 31) ? (version -= 4; true) : false
|
|
275
|
+
pubkey = recover_public_key_from_signature(hash, signature, version-27, compressed)
|
|
276
|
+
end
|
|
277
|
+
|
|
112
278
|
def self.init_ffi_ssl
|
|
113
279
|
return if @ssl_loaded
|
|
114
280
|
SSL_library_init()
|