bitcoin-ruby 0.0.1
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 +12 -0
- data/COPYING +18 -0
- data/Gemfile +4 -0
- data/README.rdoc +189 -0
- data/Rakefile +104 -0
- data/bin/bitcoin_dns_seed +130 -0
- data/bin/bitcoin_gui +80 -0
- data/bin/bitcoin_node +174 -0
- data/bin/bitcoin_shell +12 -0
- data/bin/bitcoin_wallet +323 -0
- data/bitcoin-ruby.gemspec +27 -0
- data/concept-examples/blockchain-pow.rb +151 -0
- data/doc/CONFIG.rdoc +66 -0
- data/doc/EXAMPLES.rdoc +9 -0
- data/doc/NODE.rdoc +35 -0
- data/doc/STORAGE.rdoc +21 -0
- data/doc/WALLET.rdoc +102 -0
- data/examples/balance.rb +60 -0
- data/examples/bbe_verify_tx.rb +55 -0
- data/examples/connect.rb +36 -0
- data/examples/relay_tx.rb +22 -0
- data/examples/verify_tx.rb +57 -0
- data/lib/bitcoin.rb +370 -0
- data/lib/bitcoin/builder.rb +266 -0
- data/lib/bitcoin/config.rb +56 -0
- data/lib/bitcoin/connection.rb +126 -0
- data/lib/bitcoin/ffi/openssl.rb +121 -0
- data/lib/bitcoin/gui/addr_view.rb +42 -0
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
- data/lib/bitcoin/gui/conn_view.rb +36 -0
- data/lib/bitcoin/gui/connection.rb +68 -0
- data/lib/bitcoin/gui/em_gtk.rb +28 -0
- data/lib/bitcoin/gui/gui.builder +1643 -0
- data/lib/bitcoin/gui/gui.rb +290 -0
- data/lib/bitcoin/gui/helpers.rb +113 -0
- data/lib/bitcoin/gui/tree_view.rb +82 -0
- data/lib/bitcoin/gui/tx_view.rb +67 -0
- data/lib/bitcoin/key.rb +125 -0
- data/lib/bitcoin/logger.rb +65 -0
- data/lib/bitcoin/network/command_client.rb +93 -0
- data/lib/bitcoin/network/command_handler.rb +179 -0
- data/lib/bitcoin/network/connection_handler.rb +274 -0
- data/lib/bitcoin/network/node.rb +399 -0
- data/lib/bitcoin/protocol.rb +140 -0
- data/lib/bitcoin/protocol/address.rb +48 -0
- data/lib/bitcoin/protocol/alert.rb +47 -0
- data/lib/bitcoin/protocol/block.rb +154 -0
- data/lib/bitcoin/protocol/handler.rb +38 -0
- data/lib/bitcoin/protocol/parser.rb +148 -0
- data/lib/bitcoin/protocol/tx.rb +205 -0
- data/lib/bitcoin/protocol/txin.rb +97 -0
- data/lib/bitcoin/protocol/txout.rb +73 -0
- data/lib/bitcoin/protocol/version.rb +70 -0
- data/lib/bitcoin/script.rb +634 -0
- data/lib/bitcoin/storage/dummy.rb +164 -0
- data/lib/bitcoin/storage/models.rb +133 -0
- data/lib/bitcoin/storage/sequel.rb +335 -0
- data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
- data/lib/bitcoin/storage/storage.rb +243 -0
- data/lib/bitcoin/version.rb +3 -0
- data/lib/bitcoin/wallet/coinselector.rb +30 -0
- data/lib/bitcoin/wallet/keygenerator.rb +75 -0
- data/lib/bitcoin/wallet/keystore.rb +203 -0
- data/lib/bitcoin/wallet/txdp.rb +116 -0
- data/lib/bitcoin/wallet/wallet.rb +243 -0
- data/spec/bitcoin/bitcoin_spec.rb +472 -0
- data/spec/bitcoin/builder_spec.rb +90 -0
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
- data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
- data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
- data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
- data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
- data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
- data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
- data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
- data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
- data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
- data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
- data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
- data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
- data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
- data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
- data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
- data/spec/bitcoin/key_spec.rb +123 -0
- data/spec/bitcoin/network_spec.rb +48 -0
- data/spec/bitcoin/protocol/addr_spec.rb +68 -0
- data/spec/bitcoin/protocol/alert_spec.rb +20 -0
- data/spec/bitcoin/protocol/block_spec.rb +101 -0
- data/spec/bitcoin/protocol/inv_spec.rb +124 -0
- data/spec/bitcoin/protocol/ping_spec.rb +49 -0
- data/spec/bitcoin/protocol/tx_spec.rb +226 -0
- data/spec/bitcoin/protocol/version_spec.rb +77 -0
- data/spec/bitcoin/reorg_spec.rb +129 -0
- data/spec/bitcoin/script/opcodes_spec.rb +417 -0
- data/spec/bitcoin/script/script_spec.rb +246 -0
- data/spec/bitcoin/spec_helper.rb +36 -0
- data/spec/bitcoin/storage_spec.rb +229 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
- data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
- metadata +295 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Fetch a transaction from blockexplorer.com and verify all signatures.
|
|
4
|
+
#
|
|
5
|
+
# examples/bbe_verify_tx.rb <tx hash> [testnet]
|
|
6
|
+
# examples/bbe_verify_tx.rb f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
|
|
7
|
+
# see Bitcoin::P::Tx and Bitcoin::Script.
|
|
8
|
+
|
|
9
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib")
|
|
10
|
+
require 'bitcoin'
|
|
11
|
+
require 'open-uri'
|
|
12
|
+
|
|
13
|
+
tx_hash = ARGV[0]
|
|
14
|
+
$testnet = ARGV[1]
|
|
15
|
+
|
|
16
|
+
# fetch transaction from bbe as json and deserialize into Bitcoin::Protocol::Tx object
|
|
17
|
+
def get_tx(hash)
|
|
18
|
+
url = "http://blockexplorer.com/%srawtx/%s" % [$testnet ? 'testnet/' : '', hash]
|
|
19
|
+
json = open(url).read
|
|
20
|
+
Bitcoin::Protocol::Tx.from_json(json)
|
|
21
|
+
rescue Exception
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
tx1 = get_tx(tx_hash)
|
|
26
|
+
|
|
27
|
+
unless tx1
|
|
28
|
+
puts "Tx #{tx_hash} not found."
|
|
29
|
+
exit
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if tx1.in.all?{|txin| txin.coinbase? }
|
|
33
|
+
puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
tx1.in.each_with_index do |txin, idx|
|
|
38
|
+
if txin.coinbase?
|
|
39
|
+
puts "skipping coinbase transaction input.."; next
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
prev_tx = get_tx(txin.previous_output)
|
|
43
|
+
unless prev_tx
|
|
44
|
+
puts "Missing prev_out tx for input #{idx} of tx #{tx_hash}!"
|
|
45
|
+
exit
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
result = tx1.verify_input_signature(idx, prev_tx)
|
|
49
|
+
unless result
|
|
50
|
+
puts "Input #{idx} of tx #{tx_hash} is invalid!"
|
|
51
|
+
exit
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
puts "Tx #{tx_hash} is valid."
|
data/examples/connect.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Connect to a random peer and download the first 500 blocks.
|
|
4
|
+
#
|
|
5
|
+
# examples/connect.rb [testnet]
|
|
6
|
+
#
|
|
7
|
+
# see Bitcoin::Connection and Bitcoin::Protocol.
|
|
8
|
+
|
|
9
|
+
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
|
10
|
+
require 'bitcoin/connection'
|
|
11
|
+
|
|
12
|
+
Bitcoin::network = ARGV[0] || :bitcoin
|
|
13
|
+
|
|
14
|
+
class RawJSON_Connection < Bitcoin::Connection
|
|
15
|
+
def on_tx(tx)
|
|
16
|
+
p ['tx', tx.hash, Time.now]
|
|
17
|
+
# puts tx.to_json
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def on_block(block)
|
|
21
|
+
p ['block', block.hash, Time.now]
|
|
22
|
+
# puts block.to_json
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
EM.run do
|
|
27
|
+
|
|
28
|
+
host = '127.0.0.1'
|
|
29
|
+
#host = '217.157.1.202'
|
|
30
|
+
|
|
31
|
+
connections = []
|
|
32
|
+
#RawJSON_Connection.connect(host, 8333, connections)
|
|
33
|
+
|
|
34
|
+
RawJSON_Connection.connect_random_from_dns(connections)
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Relay transaction to the network.
|
|
4
|
+
# TODO
|
|
5
|
+
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
# TODO: use CommandClient
|
|
10
|
+
|
|
11
|
+
host, port = "127.0.0.1", 9999
|
|
12
|
+
if ARGV[0] == "-s"
|
|
13
|
+
host, port = ARGV[1].split(":")
|
|
14
|
+
ARGV.shift; ARGV.shift
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
s = TCPSocket.new("127.0.0.1", 9999)
|
|
18
|
+
s.puts ("relay_tx " + ARGF.read.unpack("H*")[0])
|
|
19
|
+
|
|
20
|
+
res = s.readline
|
|
21
|
+
puts JSON::pretty_generate(JSON::parse(res))
|
|
22
|
+
s.close
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Fetch a transaction and all its previous outputs from local storage and verify signatures.
|
|
4
|
+
#
|
|
5
|
+
# examples/verify_tx.rb <tx_hash>
|
|
6
|
+
# examples/verify_tx.rb f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
|
|
7
|
+
#
|
|
8
|
+
# see Bitcoin::Protocol::Tx and Bitcoin::Script.
|
|
9
|
+
# Note: For this to work, you need to have the transactions in your storage. see NODE.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
|
13
|
+
require 'bitcoin'
|
|
14
|
+
|
|
15
|
+
Bitcoin.network = :bitcoin
|
|
16
|
+
store = Bitcoin::Storage.sequel(:db => "sqlite://bitcoin.db")
|
|
17
|
+
|
|
18
|
+
tx_hash = ARGV.shift
|
|
19
|
+
|
|
20
|
+
tx1 = store.get_tx(tx_hash)
|
|
21
|
+
|
|
22
|
+
unless tx1
|
|
23
|
+
puts "Tx #{tx_hash} not found."
|
|
24
|
+
exit
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if tx1.in.all?{|txin| txin.coinbase? }
|
|
28
|
+
puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
|
|
29
|
+
exit
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
tx1.in.each_with_index do |txin, idx|
|
|
33
|
+
if txin.coinbase?
|
|
34
|
+
puts "skipping coinbase transaction input.."; next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
prev_tx = txin.get_prev_out.get_tx
|
|
38
|
+
if prev_tx
|
|
39
|
+
puts "Found prev tx #{prev_tx.hash}"
|
|
40
|
+
txout = prev_tx.out[txin.prev_out_index]
|
|
41
|
+
script = Bitcoin::Script.new(txout.pk_script)
|
|
42
|
+
puts "Output Script: #{script.to_string}"
|
|
43
|
+
else
|
|
44
|
+
puts "Missing prev tx for input #{idx}!"
|
|
45
|
+
exit
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
result = tx1.verify_input_signature(idx, prev_tx)
|
|
49
|
+
if result
|
|
50
|
+
puts "Valid signature for input #{idx}."
|
|
51
|
+
else
|
|
52
|
+
puts "Signature for input #{idx} is invalid!"
|
|
53
|
+
exit
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
puts "Tx #{tx_hash} is valid."
|
data/lib/bitcoin.rb
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# Bitcoin Utils and Network Protocol in Ruby.
|
|
2
|
+
|
|
3
|
+
require 'digest/sha2'
|
|
4
|
+
require 'digest/rmd160'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module Bitcoin
|
|
9
|
+
|
|
10
|
+
autoload :Connection, 'bitcoin/connection'
|
|
11
|
+
autoload :Protocol, 'bitcoin/protocol'
|
|
12
|
+
autoload :P, 'bitcoin/protocol'
|
|
13
|
+
autoload :Script, 'bitcoin/script'
|
|
14
|
+
autoload :VERSION, 'bitcoin/version'
|
|
15
|
+
autoload :Storage, 'bitcoin/storage/storage'
|
|
16
|
+
autoload :Logger, 'bitcoin/logger'
|
|
17
|
+
autoload :Key, 'bitcoin/key'
|
|
18
|
+
autoload :Config, 'bitcoin/config'
|
|
19
|
+
autoload :Builder, 'bitcoin/builder'
|
|
20
|
+
|
|
21
|
+
module Network
|
|
22
|
+
autoload :ConnectionHandler, 'bitcoin/network/connection_handler'
|
|
23
|
+
autoload :CommandHandler, 'bitcoin/network/command_handler'
|
|
24
|
+
autoload :CommandClient, 'bitcoin/network/command_client'
|
|
25
|
+
autoload :Node, 'bitcoin/network/node'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module Wallet
|
|
29
|
+
autoload :KeyGenerator, 'bitcoin/wallet/keygenerator'
|
|
30
|
+
autoload :SimpleKeyStore, 'bitcoin/wallet/keystore'
|
|
31
|
+
autoload :DeterministicKeyStore, 'bitcoin/wallet/keystore'
|
|
32
|
+
autoload :SimpleCoinSelector, 'bitcoin/wallet/coinselector'
|
|
33
|
+
autoload :Wallet, 'bitcoin/wallet/wallet'
|
|
34
|
+
autoload :TxDP, 'bitcoin/wallet/txdp'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module Gui
|
|
38
|
+
autoload :Gui, 'bitcoin/gui/gui'
|
|
39
|
+
autoload :Connection, 'bitcoin/gui/connection'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.require_dependency name, opts = {}
|
|
43
|
+
begin
|
|
44
|
+
require name.to_s
|
|
45
|
+
rescue LoadError
|
|
46
|
+
print "Cannot load #{opts[:exit] == false ? 'optional' : 'required'} dependency '#{name}'"
|
|
47
|
+
(opts[:gem] == false) ? puts("") :
|
|
48
|
+
puts(" - install with `gem install #{opts[:gem] || name}`")
|
|
49
|
+
puts opts[:message] if opts[:message]
|
|
50
|
+
exit 1 unless opts[:exit] == false
|
|
51
|
+
return false
|
|
52
|
+
end
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
module Util
|
|
57
|
+
|
|
58
|
+
def hth(h); h.unpack("H*")[0]; end
|
|
59
|
+
def htb(h); [h].pack("H*"); end
|
|
60
|
+
|
|
61
|
+
def address_version; Bitcoin.network[:address_version]; end
|
|
62
|
+
def p2sh_version; Bitcoin.network[:p2sh_version]; end
|
|
63
|
+
|
|
64
|
+
# hash160 is a 20 bytes (160bits) rmd610-sha256 hexdigest.
|
|
65
|
+
def hash160(hex)
|
|
66
|
+
bytes = [hex].pack("H*")
|
|
67
|
+
Digest::RMD160.hexdigest Digest::SHA256.digest(bytes)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# checksum is a 4 bytes sha256-sha256 hexdigest.
|
|
71
|
+
def checksum(hex)
|
|
72
|
+
b = [hex].pack("H*") # unpack hex
|
|
73
|
+
Digest::SHA256.hexdigest( Digest::SHA256.digest(b) )[0...8]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# verify base58 checksum for given +base58+ data.
|
|
77
|
+
def base58_checksum?(base58)
|
|
78
|
+
hex = decode_base58(base58) rescue nil
|
|
79
|
+
return false unless hex
|
|
80
|
+
Bitcoin.checksum( hex[0...42] ) == hex[-8..-1]
|
|
81
|
+
end
|
|
82
|
+
alias :address_checksum? :base58_checksum?
|
|
83
|
+
|
|
84
|
+
# check if given +address+ is valid.
|
|
85
|
+
# this means having a correct version byte, length and checksum.
|
|
86
|
+
def valid_address?(address)
|
|
87
|
+
hex = decode_base58(address) rescue nil
|
|
88
|
+
return false unless hex && hex.bytesize == 50
|
|
89
|
+
return false unless [address_version, p2sh_version].include?(hex[0...2])
|
|
90
|
+
address_checksum?(address)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# get hash160 for given +address+. returns nil if address is invalid.
|
|
94
|
+
def hash160_from_address(address)
|
|
95
|
+
return nil unless valid_address?(address)
|
|
96
|
+
decode_base58(address)[2...42]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# get type of given +address+.
|
|
100
|
+
def address_type(address)
|
|
101
|
+
return nil unless valid_address?(address)
|
|
102
|
+
case decode_base58(address)[0...2]
|
|
103
|
+
when address_version; :hash160
|
|
104
|
+
when p2sh_version; :p2sh
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def sha256(hex)
|
|
109
|
+
Digest::SHA256.hexdigest([hex].pack("H*"))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def hash160_to_address(hex)
|
|
113
|
+
hex = address_version + hex
|
|
114
|
+
encode_base58(hex + checksum(hex))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def pubkey_to_address(pubkey)
|
|
118
|
+
hash160_to_address( hash160(pubkey) )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def int_to_base58(int_val, leading_zero_bytes=0)
|
|
122
|
+
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
123
|
+
base58_val, base = '', alpha.size
|
|
124
|
+
while int_val > 0
|
|
125
|
+
int_val, remainder = int_val.divmod(base)
|
|
126
|
+
base58_val = alpha[remainder] + base58_val
|
|
127
|
+
end
|
|
128
|
+
base58_val
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def base58_to_int(base58_val)
|
|
132
|
+
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
133
|
+
int_val, base = 0, alpha.size
|
|
134
|
+
base58_val.reverse.each_char.with_index do |char,index|
|
|
135
|
+
raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
|
|
136
|
+
int_val += char_index*(base**index)
|
|
137
|
+
end
|
|
138
|
+
int_val
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def encode_base58(hex)
|
|
142
|
+
leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
|
|
143
|
+
("1"*leading_zero_bytes) + int_to_base58( hex.to_i(16) )
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def decode_base58(base58_val)
|
|
148
|
+
s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
|
|
149
|
+
s = '' if s == '00'
|
|
150
|
+
leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
|
|
151
|
+
s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
|
|
152
|
+
s
|
|
153
|
+
end
|
|
154
|
+
alias_method :base58_to_hex, :decode_base58
|
|
155
|
+
|
|
156
|
+
# target compact bits (int) to bignum hex
|
|
157
|
+
def decode_compact_bits(bits)
|
|
158
|
+
bytes = Array.new(size=((bits >> 24) & 255), 0)
|
|
159
|
+
bytes[0] = (bits >> 16) & 255 if size >= 1
|
|
160
|
+
bytes[1] = (bits >> 8) & 255 if size >= 2
|
|
161
|
+
bytes[2] = (bits ) & 255 if size >= 3
|
|
162
|
+
bytes.pack("C*").unpack("H*")[0].rjust(64, '0')
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# target bignum hex to compact bits (int)
|
|
166
|
+
def encode_compact_bits(target)
|
|
167
|
+
bytes = OpenSSL::BN.new(target, 16).to_mpi
|
|
168
|
+
size = bytes.size - 4
|
|
169
|
+
nbits = size << 24
|
|
170
|
+
nbits |= (bytes[4] << 16) if size >= 1
|
|
171
|
+
nbits |= (bytes[5] << 8) if size >= 2
|
|
172
|
+
nbits |= (bytes[6] ) if size >= 3
|
|
173
|
+
nbits
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def decode_target(target_bits)
|
|
177
|
+
case target_bits
|
|
178
|
+
when Fixnum
|
|
179
|
+
[ decode_compact_bits(target_bits).to_i(16), target_bits ]
|
|
180
|
+
when String
|
|
181
|
+
[ target_bits.to_i(16), encode_compact_bits(target_bits) ]
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def bitcoin_elliptic_curve
|
|
186
|
+
::OpenSSL::PKey::EC.new("secp256k1")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def generate_key
|
|
190
|
+
key = bitcoin_elliptic_curve.generate_key
|
|
191
|
+
inspect_key( key )
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def inspect_key(key)
|
|
195
|
+
[ key.private_key_hex, key.public_key_hex ]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def generate_address
|
|
199
|
+
prvkey, pubkey = generate_key
|
|
200
|
+
[ pubkey_to_address(pubkey), prvkey, pubkey, hash160(pubkey) ]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def bitcoin_hash(hex)
|
|
204
|
+
Digest::SHA256.digest(
|
|
205
|
+
Digest::SHA256.digest( [hex].pack("H*").reverse )
|
|
206
|
+
).reverse.unpack("H*")[0]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def bitcoin_mrkl(a, b); bitcoin_hash(b + a); end
|
|
210
|
+
|
|
211
|
+
def block_hash(prev_block, mrkl_root, time, bits, nonce, ver)
|
|
212
|
+
h = "%08x%08x%08x%064s%064s%08x" %
|
|
213
|
+
[nonce, bits, time, mrkl_root, prev_block, ver]
|
|
214
|
+
bitcoin_hash(h)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def hash_mrkl_tree(tx)
|
|
218
|
+
chunks = [ tx.dup ]
|
|
219
|
+
while chunks.last.size >= 2
|
|
220
|
+
chunks << chunks.last.each_slice(2).map{|i|
|
|
221
|
+
Bitcoin.bitcoin_mrkl( i[0], i[1] || i[0] )
|
|
222
|
+
}
|
|
223
|
+
end
|
|
224
|
+
chunks.flatten
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def sign_data(key, data)
|
|
229
|
+
key.dsa_sign_asn1(data)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def verify_signature(hash, signature, public_key)
|
|
233
|
+
key = bitcoin_elliptic_curve
|
|
234
|
+
key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
|
|
235
|
+
key.dsa_verify_asn1(hash, signature)
|
|
236
|
+
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
|
|
237
|
+
false
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def open_key(private_key, public_key=nil)
|
|
241
|
+
key = bitcoin_elliptic_curve
|
|
242
|
+
key.private_key = ::OpenSSL::BN.from_hex(private_key)
|
|
243
|
+
public_key = regenerate_public_key(private_key) unless public_key
|
|
244
|
+
key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
|
|
245
|
+
key
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def regenerate_public_key(private_key)
|
|
249
|
+
Bitcoin::OpenSSL_EC.regenerate_key(private_key)[1]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
RETARGET_INTERVAL = 2016
|
|
254
|
+
|
|
255
|
+
# block count when the next retarget will take place.
|
|
256
|
+
def block_next_retarget(block_height)
|
|
257
|
+
(block_height + (RETARGET_INTERVAL-block_height.divmod(RETARGET_INTERVAL).last)) - 1
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# current difficulty as a multiple of the minimum difficulty (highest target).
|
|
261
|
+
def block_difficulty(target_nbits)
|
|
262
|
+
# max_target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
|
263
|
+
# current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
|
|
264
|
+
# "%.7f" % (max_target / current_target.to_f)
|
|
265
|
+
bits, max_body, scaland = target_nbits, Math.log(0x00ffff), Math.log(256)
|
|
266
|
+
"%.7f" % Math.exp(max_body - Math.log(bits&0x00ffffff) + scaland * (0x1d - ((bits&0xff000000)>>24)))
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# average number of hashes required to win a block with the current target. (nbits)
|
|
270
|
+
def block_hashes_to_win(target_nbits)
|
|
271
|
+
current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
|
|
272
|
+
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff / current_target
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# probability of a single hash solving a block with the current difficulty.
|
|
276
|
+
def block_probability(target_nbits)
|
|
277
|
+
current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
|
|
278
|
+
"%.55f" % (current_target.to_f / 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# average time to find a block in seconds with the current target. (nbits)
|
|
282
|
+
def block_average_hashing_time(target_nbits, hashes_per_second)
|
|
283
|
+
block_hashes_to_win(target_nbits) / hashes_per_second
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# shows the total number of Bitcoins in circulation, reward era and reward in that era.
|
|
287
|
+
def blockchain_total_btc(height)
|
|
288
|
+
reward, interval = 5000000000, 210000
|
|
289
|
+
total_btc = reward
|
|
290
|
+
reward_era, remainder = (height).divmod(interval)
|
|
291
|
+
reward_era.times{
|
|
292
|
+
total_btc += interval * reward
|
|
293
|
+
reward = reward / 2
|
|
294
|
+
}
|
|
295
|
+
total_btc += remainder * reward
|
|
296
|
+
[total_btc, reward_era+1, reward, height]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def block_creation_reward(block_height)
|
|
300
|
+
5000000000 / (2 ** (block_height / 210000.0).floor)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
module ::OpenSSL
|
|
305
|
+
class BN
|
|
306
|
+
def self.from_hex(hex); new(hex, 16); end
|
|
307
|
+
def to_hex; to_i.to_s(16); end
|
|
308
|
+
def to_mpi; to_s(0).unpack("C*"); end
|
|
309
|
+
end
|
|
310
|
+
class PKey::EC
|
|
311
|
+
def private_key_hex; private_key.to_hex.rjust(64, '0'); end
|
|
312
|
+
def public_key_hex; public_key.to_hex.rjust(130, '0'); end
|
|
313
|
+
end
|
|
314
|
+
class PKey::EC::Point
|
|
315
|
+
def self.from_hex(group, hex)
|
|
316
|
+
new(group, BN.from_hex(hex))
|
|
317
|
+
end
|
|
318
|
+
def to_hex; to_bn.to_hex; end
|
|
319
|
+
def self.bn2mpi(hex) BN.from_hex(hex).to_mpi; end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
autoload :OpenSSL_EC, "bitcoin/ffi/openssl"
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
extend Util
|
|
327
|
+
|
|
328
|
+
@network = :bitcoin
|
|
329
|
+
|
|
330
|
+
def self.network
|
|
331
|
+
NETWORKS[@network]
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def self.network= name
|
|
335
|
+
@network = name.to_sym
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
NETWORKS = {
|
|
339
|
+
:bitcoin => {
|
|
340
|
+
:magic_head => "\xF9\xBE\xB4\xD9",
|
|
341
|
+
:address_version => "00",
|
|
342
|
+
:p2sh_version => "05",
|
|
343
|
+
:privkey_version => "80",
|
|
344
|
+
:default_port => 8333,
|
|
345
|
+
:dns_seeds => ["bitseed.xf2.org", "dnsseed.bluematt.me",
|
|
346
|
+
"dnsseed.bitcoin.dashjr.org", "seed.bitcoin.sipa.be"],
|
|
347
|
+
:genesis_hash => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
|
348
|
+
:proof_of_work_limit => 0x1d00ffff,
|
|
349
|
+
:known_nodes => [
|
|
350
|
+
'relay.eligius.st',
|
|
351
|
+
'mining.bitcoin.cz',
|
|
352
|
+
'bitcoins.lc',
|
|
353
|
+
'blockchain.info',
|
|
354
|
+
'blockexplorer.com',
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
:testnet => {
|
|
358
|
+
:magic_head => "\xFA\xBF\xB5\xDA",
|
|
359
|
+
:address_version => "6f",
|
|
360
|
+
:p2sh_version => "c4",
|
|
361
|
+
:privkey_version => "ef",
|
|
362
|
+
:default_port => 18333,
|
|
363
|
+
:dns_seeds => ["testseed.bitcoin.interesthings.de"],
|
|
364
|
+
:genesis_hash => "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
|
|
365
|
+
:proof_of_work_limit => 0x1d07fff8,
|
|
366
|
+
:known_nodes => []
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
end
|