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
@@ -0,0 +1,187 @@
|
|
1
|
+
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'bitcoin'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
class Bitcoin::Protocol::Parser; def log; stub=Object.new; def stub.method_missing(*a); end; stub; end; end
|
7
|
+
|
8
|
+
|
9
|
+
module SimpleNode
|
10
|
+
class Connection < EM::Connection
|
11
|
+
|
12
|
+
def on_tx(tx)
|
13
|
+
log.info { "received transaction: #{tx.hash}" }
|
14
|
+
|
15
|
+
puts tx.to_json
|
16
|
+
|
17
|
+
if tx.hash == @ask_tx
|
18
|
+
@args[:result] = tx
|
19
|
+
@args[:callback] ? (close_connection; @args[:callback].call(tx)) : EM.stop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_block(block)
|
24
|
+
log.info { "received block: #{block.hash}" }
|
25
|
+
|
26
|
+
puts block.to_json
|
27
|
+
|
28
|
+
if block.hash == @ask_block
|
29
|
+
if @ask_tx
|
30
|
+
if tx = block.tx.find{|tx| tx.hash == @ask_tx }
|
31
|
+
on_tx(tx)
|
32
|
+
else
|
33
|
+
log.info { "@ask_tx #{@ask_tx} not in @ask_block #{@ask_block}" }
|
34
|
+
@args[:result] = nil
|
35
|
+
@args[:callback] ? (close_connection; @args[:callback].call(nil)) : EM.stop
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@args[:result] = block
|
39
|
+
@args[:callback] ? (close_connection; @args[:callback].call(block)) : EM.stop
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_handshake_complete
|
45
|
+
return if @connected
|
46
|
+
@connected = true
|
47
|
+
log.info { "handshake complete" }
|
48
|
+
|
49
|
+
EM.add_timer(0.5){
|
50
|
+
if @ask_block
|
51
|
+
log.info { "ask for @ask_block: #{@ask_block}" }
|
52
|
+
send_data Bitcoin::Protocol.getdata_pkt(:block, [htb(@ask_block)])
|
53
|
+
else
|
54
|
+
if @ask_tx
|
55
|
+
log.info { "ask for @ask_tx: #{@ask_tx}" }
|
56
|
+
send_data Bitcoin::Protocol.getdata_pkt(:tx, [htb(@ask_tx)])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if @send_tx
|
60
|
+
tx = Bitcoin::P::Tx.from_json(File.read(@send_tx))
|
61
|
+
send_data(Bitcoin::Protocol.pkt('tx', tx.to_payload))
|
62
|
+
p [:sent, tx.hash]
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_get_transaction(hash); end
|
68
|
+
def on_get_block(hash); end
|
69
|
+
def on_addr(addr); end
|
70
|
+
def on_inv_transaction(hash)
|
71
|
+
log.info { "peer told us about transaction: #{hth(hash)}" }
|
72
|
+
log.info { "asking peer for transaction: #{hth(hash)}" }
|
73
|
+
send_data Bitcoin::Protocol.getdata_pkt(:tx, [hash])
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_inv_block(hash)
|
77
|
+
log.info { "peer told us about block: #{hth(hash)}" }
|
78
|
+
log.info { "asking peer for block: #{hth(hash)}" }
|
79
|
+
send_data Bitcoin::Protocol.getdata_pkt(:block, [hash])
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_handshake_begin
|
83
|
+
log.info { "handshake started" }
|
84
|
+
|
85
|
+
version = Bitcoin::Protocol::Version.new({
|
86
|
+
:user_agent => "/Satoshi:0.8.1/",
|
87
|
+
:last_block => 0,
|
88
|
+
:from => "127.0.0.1:#{Bitcoin.network[:default_port]}",
|
89
|
+
:to => "#{@host}:#{@port}",
|
90
|
+
})
|
91
|
+
|
92
|
+
log.info { "sending version: Version:%d (%s) Block:%d" % version.fields.values_at(:version, :user_agent, :last_block) }
|
93
|
+
send_data(version.to_pkt)
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_version(version)
|
97
|
+
@version ||= version
|
98
|
+
log.info { "received version: Version:%d (%s) Block:%d" % version.fields.values_at(:version, :user_agent, :last_block) }
|
99
|
+
send_data( Bitcoin::Protocol.verack_pkt )
|
100
|
+
on_handshake_complete
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize(host, port, node=nil, opts={})
|
104
|
+
set_host(host, port)
|
105
|
+
@node = node
|
106
|
+
@parser = Bitcoin::Protocol::Parser.new( self )
|
107
|
+
|
108
|
+
@args = opts
|
109
|
+
@ask_tx, @ask_block, @send_tx = opts.values_at(:ask_tx, :ask_block, :send_tx)
|
110
|
+
end
|
111
|
+
|
112
|
+
def receive_data(data); @parser.parse(data); end
|
113
|
+
def post_init; log.info { "peer connected" }; on_handshake_begin; end
|
114
|
+
def unbind; log.info { "peer disconnected" }; end
|
115
|
+
def set_host(host, port=8333); @host, @port = host, port; end
|
116
|
+
|
117
|
+
def log
|
118
|
+
return @log if @log
|
119
|
+
return (@log = (stub=Object.new; def stub.method_missing(*a); end; stub)) if @args[:nolog]
|
120
|
+
@logger ||= Bitcoin::Logger.create(:network, :info) unless @node.respond_to?(:log)
|
121
|
+
@log = Bitcoin::Logger::LogWrapper.new("#@host:#@port", @logger || @node.log)
|
122
|
+
end
|
123
|
+
|
124
|
+
def hth(h); h.unpack("H*")[0]; end
|
125
|
+
def htb(h); [h].pack("H*"); end
|
126
|
+
|
127
|
+
|
128
|
+
def self.connect(host, port, *args)
|
129
|
+
EM.connect(host, port, self, host, port, *args)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.connect_random_from_dns(seeds=[], count=1, *args)
|
133
|
+
seeds = Bitcoin.network[:dns_seeds] unless seeds.any?
|
134
|
+
if seeds.any?
|
135
|
+
seeds.sample(count).map{|dns|
|
136
|
+
host = IPSocket.getaddress(dns)
|
137
|
+
connect(host, Bitcoin.network[:default_port], *args)
|
138
|
+
}
|
139
|
+
else
|
140
|
+
raise "No DNS seeds available. Provide IP, configure seeds, or use different network."
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.connect_known_nodes(count=1)
|
145
|
+
connect_random_from_dns(Bitcoin.network[:known_nodes], count)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
if $0 == __FILE__
|
152
|
+
|
153
|
+
args = {
|
154
|
+
ask_tx: ARGV.find{|a| a[/tx=(.+)/, 1] } && $1,
|
155
|
+
ask_block: ARGV.find{|a| a[/block=(.+)/, 1] } && $1,
|
156
|
+
use_node: ARGV.find{|a| a[/node=(.+)/, 1] } && $1,
|
157
|
+
send_tx: ARGV.find{|a| a[/send_tx=(.+)/, 1] } && $1,
|
158
|
+
set_project: ARGV.find{|a| a[/project=(.+)/, 1] } && $1,
|
159
|
+
callback: proc{|i|
|
160
|
+
case i
|
161
|
+
when Bitcoin::Protocol::Block
|
162
|
+
puts "INFO network: SAVING @ask_block: #{i.hash}"
|
163
|
+
File.open("block-#{i.hash}.bin", 'wb'){|f| f.print i.payload }
|
164
|
+
File.open("block-#{i.hash}.json", 'wb'){|f| f.print i.to_json }
|
165
|
+
when Bitcoin::Protocol::Tx
|
166
|
+
puts "INFO network: SAVING @ask_tx: #{i.hash}"
|
167
|
+
File.open("tx-#{i.hash}.bin", 'wb'){|f| f.print i.payload }
|
168
|
+
File.open("tx-#{i.hash}.json", 'wb'){|f| f.print i.to_json }
|
169
|
+
end
|
170
|
+
EM.stop
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
|
175
|
+
EM.run do
|
176
|
+
if args[:set_project]
|
177
|
+
Bitcoin.network = args[:set_project].to_sym
|
178
|
+
p Bitcoin.network_project
|
179
|
+
end
|
180
|
+
if args[:use_node]
|
181
|
+
SimpleNode::Connection.connect_random_from_dns([args[:use_node]], 1, nil, args)
|
182
|
+
else
|
183
|
+
SimpleNode::Connection.connect_random_from_dns([], 1, nil, args)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
data/examples/verify_tx.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
#
|
8
8
|
# see Bitcoin::Protocol::Tx and Bitcoin::Script.
|
9
9
|
# Note: For this to work, you need to have the transactions in your storage. see NODE.
|
10
|
-
|
10
|
+
# Note: There is also Bitcoin::Validation::Tx which validates a lot more than signatures.
|
11
11
|
|
12
12
|
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
13
13
|
require 'bitcoin'
|
data/lib/bitcoin.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: ascii-8bit
|
1
2
|
# Bitcoin Utils and Network Protocol in Ruby.
|
2
3
|
|
3
4
|
require 'digest/sha2'
|
@@ -17,6 +18,9 @@ module Bitcoin
|
|
17
18
|
autoload :Key, 'bitcoin/key'
|
18
19
|
autoload :Config, 'bitcoin/config'
|
19
20
|
autoload :Builder, 'bitcoin/builder'
|
21
|
+
autoload :Validation, 'bitcoin/validation'
|
22
|
+
|
23
|
+
autoload :Namecoin, 'bitcoin/namecoin'
|
20
24
|
|
21
25
|
module Network
|
22
26
|
autoload :ConnectionHandler, 'bitcoin/network/connection_handler'
|
@@ -43,6 +47,7 @@ module Bitcoin
|
|
43
47
|
begin
|
44
48
|
require name.to_s
|
45
49
|
rescue LoadError
|
50
|
+
return false if name.to_s == "log4r"
|
46
51
|
print "Cannot load #{opts[:exit] == false ? 'optional' : 'required'} dependency '#{name}'"
|
47
52
|
(opts[:gem] == false) ? puts("") :
|
48
53
|
puts(" - install with `gem install #{opts[:gem] || name}`")
|
@@ -55,9 +60,6 @@ module Bitcoin
|
|
55
60
|
|
56
61
|
module Util
|
57
62
|
|
58
|
-
def hth(h); h.unpack("H*")[0]; end
|
59
|
-
def htb(h); [h].pack("H*"); end
|
60
|
-
|
61
63
|
def address_version; Bitcoin.network[:address_version]; end
|
62
64
|
def p2sh_version; Bitcoin.network[:p2sh_version]; end
|
63
65
|
|
@@ -206,6 +208,10 @@ module Bitcoin
|
|
206
208
|
).reverse.unpack("H*")[0]
|
207
209
|
end
|
208
210
|
|
211
|
+
def bitcoin_byte_hash(bytes)
|
212
|
+
Digest::SHA256.digest(Digest::SHA256.digest(bytes))
|
213
|
+
end
|
214
|
+
|
209
215
|
def bitcoin_mrkl(a, b); bitcoin_hash(b + a); end
|
210
216
|
|
211
217
|
def block_hash(prev_block, mrkl_root, time, bits, nonce, ver)
|
@@ -214,16 +220,39 @@ module Bitcoin
|
|
214
220
|
bitcoin_hash(h)
|
215
221
|
end
|
216
222
|
|
223
|
+
# get merkle tree for given +tx+ list.
|
217
224
|
def hash_mrkl_tree(tx)
|
225
|
+
return [nil] if tx != tx.uniq
|
218
226
|
chunks = [ tx.dup ]
|
219
227
|
while chunks.last.size >= 2
|
220
|
-
chunks << chunks.last.each_slice(2).map{|
|
221
|
-
Bitcoin.bitcoin_mrkl(
|
222
|
-
}
|
228
|
+
chunks << chunks.last.each_slice(2).map {|a, b|
|
229
|
+
Bitcoin.bitcoin_mrkl( a, b || a ) }
|
223
230
|
end
|
224
231
|
chunks.flatten
|
225
232
|
end
|
226
233
|
|
234
|
+
# get merkle branch connecting given +target+ to the merkle root of +tx+ list
|
235
|
+
def hash_mrkl_branch(tx, target)
|
236
|
+
return [ nil ] if tx != tx.uniq
|
237
|
+
branch, chunks = [], [ tx.dup ]
|
238
|
+
while chunks.last.size >= 2
|
239
|
+
chunks << chunks.last.each_slice(2).map {|a, b|
|
240
|
+
hash = Bitcoin.bitcoin_mrkl( a, b || a )
|
241
|
+
next hash unless [a, b].include?(target)
|
242
|
+
branch << (a == target ? (b || a) : a)
|
243
|
+
target = hash
|
244
|
+
}
|
245
|
+
end
|
246
|
+
branch
|
247
|
+
end
|
248
|
+
|
249
|
+
# get merkle root from +branch+ and +target+.
|
250
|
+
def mrkl_branch_root(branch, target, idx)
|
251
|
+
branch.map do |hash|
|
252
|
+
a, b = *( idx & 1 == 0 ? [target, hash] : [hash, target] )
|
253
|
+
idx >>= 1; target = Bitcoin.bitcoin_mrkl( a, b )
|
254
|
+
end.last
|
255
|
+
end
|
227
256
|
|
228
257
|
def sign_data(key, data)
|
229
258
|
key.dsa_sign_asn1(data)
|
@@ -235,7 +264,7 @@ module Bitcoin
|
|
235
264
|
key.dsa_verify_asn1(hash, signature)
|
236
265
|
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
|
237
266
|
false
|
238
|
-
end
|
267
|
+
end
|
239
268
|
|
240
269
|
def open_key(private_key, public_key=nil)
|
241
270
|
key = bitcoin_elliptic_curve
|
@@ -249,6 +278,30 @@ module Bitcoin
|
|
249
278
|
Bitcoin::OpenSSL_EC.regenerate_key(private_key)[1]
|
250
279
|
end
|
251
280
|
|
281
|
+
def bitcoin_signed_message_hash(message)
|
282
|
+
# TODO: this will fail horribly on messages with len > 255. It's a cheap implementation of Bitcoin's CDataStream.
|
283
|
+
data = "\x18Bitcoin Signed Message:\n" + [message.bytesize].pack("C") + message
|
284
|
+
Digest::SHA256.digest(Digest::SHA256.digest(data))
|
285
|
+
end
|
286
|
+
|
287
|
+
def sign_message(private_key_hex, public_key_hex, message)
|
288
|
+
hash = bitcoin_signed_message_hash(message)
|
289
|
+
signature = Bitcoin::OpenSSL_EC.sign_compact(hash, private_key_hex, public_key_hex)
|
290
|
+
{ 'address' => pubkey_to_address(public_key_hex), 'message' => message, 'signature' => [ signature ].pack("m0") }
|
291
|
+
end
|
292
|
+
|
293
|
+
def verify_message(address, signature, message)
|
294
|
+
hash = bitcoin_signed_message_hash(message)
|
295
|
+
signature = signature.unpack("m0")[0] rescue nil # decode base64
|
296
|
+
raise "invalid address" unless valid_address?(address)
|
297
|
+
raise "malformed base64 encoding" unless signature
|
298
|
+
raise "malformed signature" unless signature.bytesize == 65
|
299
|
+
pubkey = Bitcoin::OpenSSL_EC.recover_compact(hash, signature)
|
300
|
+
pubkey_to_address(pubkey) == address if pubkey
|
301
|
+
rescue Exception => ex
|
302
|
+
p [ex.message, ex.backtrace]; false
|
303
|
+
end
|
304
|
+
|
252
305
|
|
253
306
|
RETARGET_INTERVAL = 2016
|
254
307
|
|
@@ -283,6 +336,13 @@ module Bitcoin
|
|
283
336
|
block_hashes_to_win(target_nbits) / hashes_per_second
|
284
337
|
end
|
285
338
|
|
339
|
+
# average mining time (in days) using Mh/s to get btc
|
340
|
+
def block_average_mining_time(block_nbits, block_height, mega_hashes_per_second, target_btc=1.0)
|
341
|
+
seconds = block_average_hashing_time(block_nbits, mega_hashes_per_second * 1_000_000)
|
342
|
+
reward = block_creation_reward(block_height) / Bitcoin::COIN # satoshis to btc
|
343
|
+
(days = seconds / 60 / 60 / 24) * (target_btc / reward)
|
344
|
+
end
|
345
|
+
|
286
346
|
# shows the total number of Bitcoins in circulation, reward era and reward in that era.
|
287
347
|
def blockchain_total_btc(height)
|
288
348
|
reward, interval = 5000000000, 210000
|
@@ -301,6 +361,21 @@ module Bitcoin
|
|
301
361
|
end
|
302
362
|
end
|
303
363
|
|
364
|
+
extend Util
|
365
|
+
|
366
|
+
|
367
|
+
module BinaryExtensions
|
368
|
+
def hth; unpack("H*")[0]; end
|
369
|
+
def reverse_hth; reverse.hth; end
|
370
|
+
def htb; [self].pack("H*"); end
|
371
|
+
def htb_reverse; htb.reverse; end
|
372
|
+
end
|
373
|
+
|
374
|
+
class ::String
|
375
|
+
include Bitcoin::BinaryExtensions
|
376
|
+
end
|
377
|
+
|
378
|
+
|
304
379
|
module ::OpenSSL
|
305
380
|
class BN
|
306
381
|
def self.from_hex(hex); new(hex, 16); end
|
@@ -310,6 +385,7 @@ module Bitcoin
|
|
310
385
|
class PKey::EC
|
311
386
|
def private_key_hex; private_key.to_hex.rjust(64, '0'); end
|
312
387
|
def public_key_hex; public_key.to_hex.rjust(130, '0'); end
|
388
|
+
def pubkey_compressed?; public_key.group.point_conversion_form == :compressed; end
|
313
389
|
end
|
314
390
|
class PKey::EC::Point
|
315
391
|
def self.from_hex(group, hex)
|
@@ -322,49 +398,263 @@ module Bitcoin
|
|
322
398
|
|
323
399
|
autoload :OpenSSL_EC, "bitcoin/ffi/openssl"
|
324
400
|
|
325
|
-
|
326
|
-
extend Util
|
327
|
-
|
328
401
|
@network = :bitcoin
|
329
402
|
|
330
403
|
def self.network
|
331
404
|
NETWORKS[@network]
|
332
405
|
end
|
333
406
|
|
407
|
+
def self.network_name
|
408
|
+
@network
|
409
|
+
end
|
410
|
+
|
411
|
+
def self.network_project
|
412
|
+
@network_project
|
413
|
+
end
|
414
|
+
|
334
415
|
def self.network= name
|
416
|
+
raise "Network descriptor '#{name}' not found." unless NETWORKS[name.to_sym]
|
335
417
|
@network = name.to_sym
|
418
|
+
@network_project = network[:project] rescue nil
|
419
|
+
Bitcoin::Namecoin.load if namecoin?
|
420
|
+
@network
|
336
421
|
end
|
337
422
|
|
423
|
+
[:bitcoin, :namecoin, :litecoin, :freicoin].each do |n|
|
424
|
+
instance_eval "def #{n}?; network_project == :#{n}; end"
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
CENT = 1_000_000
|
429
|
+
COIN = 100_000_000
|
430
|
+
MAX_BLOCK_SIZE = 1_000_000
|
431
|
+
MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2
|
432
|
+
MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50
|
433
|
+
MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100
|
434
|
+
|
435
|
+
MIN_FEE_MODE = [ :block, :relay, :send ]
|
436
|
+
|
338
437
|
NETWORKS = {
|
438
|
+
|
339
439
|
:bitcoin => {
|
440
|
+
:project => :bitcoin,
|
340
441
|
:magic_head => "\xF9\xBE\xB4\xD9",
|
341
442
|
:address_version => "00",
|
342
443
|
:p2sh_version => "05",
|
343
444
|
:privkey_version => "80",
|
344
445
|
:default_port => 8333,
|
345
|
-
:
|
346
|
-
|
446
|
+
:protocol_version => 70001,
|
447
|
+
:coinbase_maturity => 100,
|
448
|
+
:retarget_interval => 2016,
|
449
|
+
:retarget_time => 1209600, # 2 weeks
|
450
|
+
:max_money => 21_000_000 * COIN,
|
451
|
+
:min_tx_fee => 50_000,
|
452
|
+
:min_relay_tx_fee => 10_000,
|
453
|
+
:dns_seeds => [
|
454
|
+
"seed.bitcoin.sipa.be",
|
455
|
+
"dnsseed.bluematt.me",
|
456
|
+
"dnsseed.bitcoin.dashjr.org",
|
457
|
+
"bitseed.xf2.org",
|
458
|
+
],
|
347
459
|
:genesis_hash => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
348
460
|
:proof_of_work_limit => 0x1d00ffff,
|
461
|
+
:alert_pubkeys => ["04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"],
|
349
462
|
:known_nodes => [
|
350
463
|
'relay.eligius.st',
|
351
464
|
'mining.bitcoin.cz',
|
352
|
-
'bitcoins.lc',
|
353
465
|
'blockchain.info',
|
354
466
|
'blockexplorer.com',
|
355
|
-
]
|
467
|
+
],
|
468
|
+
:checkpoints => {
|
469
|
+
11111 => "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d",
|
470
|
+
33333 => "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6",
|
471
|
+
74000 => "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20",
|
472
|
+
105000 => "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97",
|
473
|
+
134444 => "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe",
|
474
|
+
168000 => "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763",
|
475
|
+
193000 => "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317",
|
476
|
+
210000 => "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e",
|
477
|
+
216116 => "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e",
|
478
|
+
225430 => "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932",
|
479
|
+
}
|
356
480
|
},
|
481
|
+
|
357
482
|
:testnet => {
|
483
|
+
:project => :bitcoin,
|
358
484
|
:magic_head => "\xFA\xBF\xB5\xDA",
|
359
485
|
:address_version => "6f",
|
360
486
|
:p2sh_version => "c4",
|
361
487
|
:privkey_version => "ef",
|
362
488
|
:default_port => 18333,
|
363
|
-
:
|
489
|
+
:max_money => 21_000_000 * COIN,
|
490
|
+
:dns_seeds => [ "testseed.bitcoin.interesthings.de" ],
|
364
491
|
:genesis_hash => "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
|
365
492
|
:proof_of_work_limit => 0x1d07fff8,
|
366
|
-
:
|
367
|
-
|
493
|
+
:alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
|
494
|
+
:known_nodes => [],
|
495
|
+
:checkpoints => {}
|
496
|
+
},
|
497
|
+
|
498
|
+
:testnet3 => {
|
499
|
+
:project => :bitcoin,
|
500
|
+
:magic_head => "\x0b\x11\x09\x07",
|
501
|
+
:address_version => "6f",
|
502
|
+
:p2sh_version => "c4",
|
503
|
+
:privkey_version => "ef",
|
504
|
+
:default_port => 18333,
|
505
|
+
:protocol_version => 70001,
|
506
|
+
:coinbase_maturity => 100,
|
507
|
+
:retarget_interval => 2016,
|
508
|
+
:retarget_time => 1209600, # 2 weeks
|
509
|
+
:max_money => 21_000_000 * COIN,
|
510
|
+
:min_tx_fee => 50_000,
|
511
|
+
:min_relay_tx_fee => 10_000,
|
512
|
+
:dns_seeds => [
|
513
|
+
"testnet-seed.bitcoin.petertodd.org",
|
514
|
+
"testnet-seed.bluematt.me",
|
515
|
+
],
|
516
|
+
:genesis_hash => "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
|
517
|
+
:proof_of_work_limit => 0x1d07fff8,
|
518
|
+
:alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
|
519
|
+
:known_nodes => [],
|
520
|
+
:checkpoints => {
|
521
|
+
# 542 contains invalid transaction
|
522
|
+
542 => "0000000083c1f82cf72c6724f7a317325806384b06408bce7a4327f418dfd5ad",
|
523
|
+
71018 => "000000000010dd93dc55541116b2744eb8f4c3b706df6e8512d231a03fb9e435",
|
524
|
+
}
|
525
|
+
},
|
526
|
+
|
527
|
+
:litecoin => {
|
528
|
+
:project => :litecoin,
|
529
|
+
:magic_head => "\xfb\xc0\xb6\xdb",
|
530
|
+
:address_version => "30",
|
531
|
+
:p2sh_version => "05",
|
532
|
+
:privkey_version => "ef",
|
533
|
+
:default_port => 9333,
|
534
|
+
:protocol_version => 60002,
|
535
|
+
:max_money => 84_000_000 * COIN,
|
536
|
+
:min_tx_fee => 2_000_000,
|
537
|
+
:coinbase_maturity => 100,
|
538
|
+
:retarget_interval => 2016,
|
539
|
+
:retarget_time => 302400, # 3.5 days
|
540
|
+
:min_relay_tx_fee => 1_000_000,
|
541
|
+
:dns_seeds => [
|
542
|
+
"dnsseed.litecointools.com",
|
543
|
+
"dnsseed.litecoinpool.org",
|
544
|
+
"dnsseed.ltc.xurious.com",
|
545
|
+
"dnsseed.koin-project.com",
|
546
|
+
"dnsseed.weminemnc.com",
|
547
|
+
],
|
548
|
+
:genesis_hash => "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2",
|
549
|
+
:proof_of_work_limit => 0,
|
550
|
+
:alert_pubkeys => [],
|
551
|
+
:known_nodes => [],
|
552
|
+
:checkpoints => {
|
553
|
+
1 => "80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f",
|
554
|
+
2 => "13957807cdd1d02f993909fa59510e318763f99a506c4c426e3b254af09f40d7",
|
555
|
+
1500 => "841a2965955dd288cfa707a755d05a54e45f8bd476835ec9af4402a2b59a2967",
|
556
|
+
4032 => "9ce90e427198fc0ef05e5905ce3503725b80e26afd35a987965fd7e3d9cf0846",
|
557
|
+
8064 => "eb984353fc5190f210651f150c40b8a4bab9eeeff0b729fcb3987da694430d70",
|
558
|
+
16128 => "602edf1859b7f9a6af809f1d9b0e6cb66fdc1d4d9dcd7a4bec03e12a1ccd153d",
|
559
|
+
23420 => "d80fdf9ca81afd0bd2b2a90ac3a9fe547da58f2530ec874e978fce0b5101b507",
|
560
|
+
50000 => "69dc37eb029b68f075a5012dcc0419c127672adb4f3a32882b2b3e71d07a20a6",
|
561
|
+
80000 => "4fcb7c02f676a300503f49c764a89955a8f920b46a8cbecb4867182ecdb2e90a",
|
562
|
+
120000 => "bd9d26924f05f6daa7f0155f32828ec89e8e29cee9e7121b026a7a3552ac6131",
|
563
|
+
161500 => "dbe89880474f4bb4f75c227c77ba1cdc024991123b28b8418dbbf7798471ff43",
|
564
|
+
179620 => "2ad9c65c990ac00426d18e446e0fd7be2ffa69e9a7dcb28358a50b2b78b9f709",
|
565
|
+
240000 => "7140d1c4b4c2157ca217ee7636f24c9c73db39c4590c4e6eab2e3ea1555088aa",
|
566
|
+
383640 => "2b6809f094a9215bafc65eb3f110a35127a34be94b7d0590a096c3f126c6f364",
|
567
|
+
}
|
568
|
+
},
|
569
|
+
|
570
|
+
:litecoin_testnet => {
|
571
|
+
:project => :litecoin,
|
572
|
+
:magic_head => "\xfc\xc1\xb7\xdc",
|
573
|
+
:address_version => "6f",
|
574
|
+
:p2sh_version => "c4",
|
575
|
+
:privkey_version => "ef",
|
576
|
+
:default_port => 19333,
|
577
|
+
:protocol_version => 60002,
|
578
|
+
:min_tx_fee => 2_000_000,
|
579
|
+
:min_relay_tx_fee => 1_000_000,
|
580
|
+
:coinbase_maturity => 100,
|
581
|
+
:retarget_interval => 2016,
|
582
|
+
:retarget_time => 302400, # 3.5 days
|
583
|
+
:max_money => 84_000_000 * COIN,
|
584
|
+
:dns_seeds => [
|
585
|
+
"testnet-seed.litecointools.com",
|
586
|
+
"testnet-seed.weminemnc.com",
|
587
|
+
],
|
588
|
+
:genesis_hash => "f5ae71e26c74beacc88382716aced69cddf3dffff24f384e1808905e0188f68f",
|
589
|
+
:proof_of_work_limit => 0,
|
590
|
+
:alert_pubkeys => [],
|
591
|
+
:known_nodes => [],
|
592
|
+
:checkpoints => {}
|
593
|
+
},
|
594
|
+
|
595
|
+
|
596
|
+
:freicoin => {
|
597
|
+
:project => :freicoin,
|
598
|
+
:magic_head => "\x2c\xfe\x7e\x6d",
|
599
|
+
:address_version => "00",
|
600
|
+
:p2sh_version => "05",
|
601
|
+
:privkey_version => "80",
|
602
|
+
:default_port => 8639,
|
603
|
+
:protocol_version => 60002,
|
604
|
+
:max_money => 21_000_000 * COIN,
|
605
|
+
:min_tx_fee => 50_000,
|
606
|
+
:min_relay_tx_fee => 10_000,
|
607
|
+
:dns_seeds => [ "seed.freico.in", "fledge.freico.in" ],
|
608
|
+
:genesis_hash => "000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c",
|
609
|
+
:proof_of_work_limit => 0,
|
610
|
+
:alert_pubkeys => [],
|
611
|
+
:known_nodes => [],
|
612
|
+
:checkpoints => {
|
613
|
+
10080 => "00000000003ff9c4b806639ec4376cc9acafcdded0e18e9dbcc2fc42e8e72331",
|
614
|
+
15779 => "000000000003eb31742b35f5efd8ffb5cdd19dcd8e82cdaad90e592c450363b6",
|
615
|
+
}
|
616
|
+
},
|
617
|
+
|
618
|
+
:namecoin => {
|
619
|
+
:project => :namecoin,
|
620
|
+
:magic_head => "\xF9\xBE\xB4\xFE",
|
621
|
+
:address_version => "34",
|
622
|
+
:default_port => 8334,
|
623
|
+
:protocol_version => 35000,
|
624
|
+
:max_money => 21_000_000 * COIN,
|
625
|
+
:min_tx_fee => 50_000,
|
626
|
+
:min_relay_tx_fee => 10_000,
|
627
|
+
:dns_seeds => [],
|
628
|
+
:genesis_hash => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
|
629
|
+
:proof_of_work_limit => 0x1d00ffff,
|
630
|
+
:known_nodes => ["bitcoin.tunl.in", "webbtc.com", "178.32.31.41",
|
631
|
+
"78.47.86.43", "69.164.206.88", ""],
|
632
|
+
:checkpoints => {
|
633
|
+
0 => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
|
634
|
+
19200 => "d8a7c3e01e1e95bcee015e6fcc7583a2ca60b79e5a3aa0a171eddd344ada903d",
|
635
|
+
24000 => "425ab0983cf04f43f346a4ca53049d0dc2db952c0a68eb0b55c3bb64108d5371",
|
636
|
+
97778 => "7553b1e43da01cfcda4335de1caf623e941d43894bd81c2af27b6582f9d83c6f",
|
637
|
+
}
|
638
|
+
},
|
639
|
+
|
640
|
+
:namecoin_testnet => {
|
641
|
+
:project => :namecoin,
|
642
|
+
:magic_head => "\xFA\xBF\xB5\xFE",
|
643
|
+
:address_version => "34",
|
644
|
+
:default_port => 18334,
|
645
|
+
:protocol_version => 35000,
|
646
|
+
:min_tx_fee => 50_000,
|
647
|
+
:min_relay_tx_fee => 10_000,
|
648
|
+
:max_money => 21_000_000 * COIN,
|
649
|
+
:dns_seeds => [],
|
650
|
+
:genesis_hash => "00000001f8ab0d14bceaeb50d163b0bef15aecf62b87bd5f5c864d37f201db97",
|
651
|
+
:proof_of_work_limit => 0x1d00ffff,
|
652
|
+
:known_nodes => ["178.32.31.41"],
|
653
|
+
:checkpoints => {
|
654
|
+
0 => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
|
655
|
+
|
656
|
+
}
|
657
|
+
},
|
368
658
|
}
|
369
|
-
|
659
|
+
|
370
660
|
end
|