bitcoin-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|