bitcoin-ruby 0.0.6 → 0.0.7
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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -7
- data/COPYING +1 -1
- data/Gemfile +2 -6
- data/Gemfile.lock +34 -0
- data/README.rdoc +16 -68
- data/Rakefile +3 -6
- data/bin/bitcoin_shell +0 -1
- data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
- data/lib/bitcoin.rb +350 -296
- data/lib/bitcoin/builder.rb +3 -1
- data/lib/bitcoin/connection.rb +2 -1
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
- data/lib/bitcoin/ffi/openssl.rb +98 -2
- data/lib/bitcoin/ffi/secp256k1.rb +144 -0
- data/lib/bitcoin/key.rb +12 -2
- data/lib/bitcoin/logger.rb +3 -12
- data/lib/bitcoin/protocol/block.rb +3 -9
- data/lib/bitcoin/protocol/parser.rb +6 -2
- data/lib/bitcoin/protocol/tx.rb +44 -13
- data/lib/bitcoin/protocol/txin.rb +4 -2
- data/lib/bitcoin/protocol/txout.rb +2 -2
- data/lib/bitcoin/script.rb +212 -37
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +32 -3
- data/spec/bitcoin/builder_spec.rb +18 -0
- data/spec/bitcoin/contracthash_spec.rb +45 -0
- data/spec/bitcoin/dogecoin_spec.rb +176 -0
- data/spec/bitcoin/ffi_openssl.rb +45 -0
- data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
- data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
- data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
- data/spec/bitcoin/fixtures/coinbase.json +24 -0
- data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/bitcoin/protocol/block_spec.rb +0 -22
- data/spec/bitcoin/protocol/tx_spec.rb +145 -2
- data/spec/bitcoin/script/script_spec.rb +282 -0
- data/spec/bitcoin/secp256k1_spec.rb +48 -0
- data/spec/bitcoin/spec_helper.rb +0 -51
- data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
- metadata +48 -98
- data/bin/bitcoin_dns_seed +0 -130
- data/bin/bitcoin_gui +0 -80
- data/bin/bitcoin_node +0 -153
- data/bin/bitcoin_node_cli +0 -81
- data/bin/bitcoin_wallet +0 -402
- data/doc/CONFIG.rdoc +0 -66
- data/doc/EXAMPLES.rdoc +0 -13
- data/doc/NAMECOIN.rdoc +0 -34
- data/doc/NODE.rdoc +0 -225
- data/doc/STORAGE.rdoc +0 -33
- data/doc/WALLET.rdoc +0 -102
- data/examples/balance.rb +0 -66
- data/examples/forwarder.rb +0 -73
- data/examples/index_nhash.rb +0 -24
- data/examples/reindex_p2sh_addrs.rb +0 -44
- data/examples/relay_tx.rb +0 -22
- data/examples/verify_tx.rb +0 -57
- data/lib/bitcoin/config.rb +0 -58
- data/lib/bitcoin/gui/addr_view.rb +0 -44
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
- data/lib/bitcoin/gui/conn_view.rb +0 -38
- data/lib/bitcoin/gui/connection.rb +0 -70
- data/lib/bitcoin/gui/em_gtk.rb +0 -30
- data/lib/bitcoin/gui/gui.builder +0 -1643
- data/lib/bitcoin/gui/gui.rb +0 -292
- data/lib/bitcoin/gui/helpers.rb +0 -115
- data/lib/bitcoin/gui/tree_view.rb +0 -84
- data/lib/bitcoin/gui/tx_view.rb +0 -69
- data/lib/bitcoin/namecoin.rb +0 -280
- data/lib/bitcoin/network/command_client.rb +0 -104
- data/lib/bitcoin/network/command_handler.rb +0 -570
- data/lib/bitcoin/network/connection_handler.rb +0 -387
- data/lib/bitcoin/network/node.rb +0 -565
- data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
- data/lib/bitcoin/storage/models.rb +0 -171
- data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
- data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
- data/lib/bitcoin/storage/storage.rb +0 -517
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
- data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
- data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
- data/lib/bitcoin/validation.rb +0 -400
- data/lib/bitcoin/wallet/coinselector.rb +0 -33
- data/lib/bitcoin/wallet/keygenerator.rb +0 -77
- data/lib/bitcoin/wallet/keystore.rb +0 -207
- data/lib/bitcoin/wallet/txdp.rb +0 -118
- data/lib/bitcoin/wallet/wallet.rb +0 -281
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
- data/spec/bitcoin/namecoin_spec.rb +0 -182
- data/spec/bitcoin/node/command_api_spec.rb +0 -663
- data/spec/bitcoin/storage/models_spec.rb +0 -104
- data/spec/bitcoin/storage/reorg_spec.rb +0 -236
- data/spec/bitcoin/storage/storage_spec.rb +0 -387
- data/spec/bitcoin/storage/validation_spec.rb +0 -300
- data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
- data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
- data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
- data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
- data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
data/bin/bitcoin_node
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
|
3
|
-
|
|
4
|
-
require 'bitcoin'
|
|
5
|
-
require 'optparse'
|
|
6
|
-
require 'fileutils'
|
|
7
|
-
Bitcoin.require_dependency :eventmachine
|
|
8
|
-
Bitcoin.require_dependency :json
|
|
9
|
-
|
|
10
|
-
defaults = Bitcoin::Network::Node::DEFAULT_CONFIG
|
|
11
|
-
|
|
12
|
-
options = Bitcoin::Config.load(defaults, :blockchain)
|
|
13
|
-
|
|
14
|
-
optparse = OptionParser.new do |opts|
|
|
15
|
-
opts.banner = "Usage: bitcoin_node [options]"
|
|
16
|
-
|
|
17
|
-
opts.separator("\nAvailable options:\n")
|
|
18
|
-
|
|
19
|
-
opts.on("-c", "--config FILE",
|
|
20
|
-
"Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
|
|
21
|
-
options = Bitcoin::Config.load_file(options, file, :blockchain)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
opts.on("-n", "--network [NETWORK]",
|
|
25
|
-
"User Network (default: #{options[:network]})") do |network|
|
|
26
|
-
options[:network] = network
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
opts.on("--command [HOST:PORT]",
|
|
30
|
-
"Command socket (default: #{options[:command].join(':')})") do |command|
|
|
31
|
-
options[:command] = command.split(":")
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
opts.on("-l", "--listen [HOST:PORT]",
|
|
35
|
-
"Listen address/port (default: #{options[:listen].join(':')})") do |listen|
|
|
36
|
-
options[:listen] = listen.split(":")
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
opts.on("--connect [HOST:PORT]",
|
|
40
|
-
"Hosts to connect to (default: #{options[:connect].map{|c|c.join(':')}.join(',')})") do |connect|
|
|
41
|
-
connect.split(",").each do |host|
|
|
42
|
-
options[:connect] << host.split(":")
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
opts.on("-a", "--announce",
|
|
47
|
-
"Announce our own address to the network, so we will get incoming connections.") do
|
|
48
|
-
options[:announce] = true
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
opts.on("--external-port PORT",
|
|
52
|
-
"Specify external port that can be used to reach this node (uses local port otherwise).") do |port|
|
|
53
|
-
options[:external_port] = port.to_i
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
opts.on("-s", "--storage [BACKEND::CONFIG]",
|
|
57
|
-
"Use storage backend (default: #{options[:storage]})") do |storage|
|
|
58
|
-
options[:storage] = storage
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
opts.on("--skip-validation", "Skip validation of blocks and transactions") do
|
|
62
|
-
options[:skip_validation] = true
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
opts.on("--check-blocks COUNT",
|
|
66
|
-
"Check consistency of COUNT latest blocks; -1 for all (default: 1000)") do |check|
|
|
67
|
-
options[:check_blocks] = check.to_i
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
opts.on("--ho", "--headers-only",
|
|
71
|
-
"Download only block headers") do
|
|
72
|
-
options[:headers_only] = true
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
opts.on("-d", "--dns", "Use DNS seeds (default)") do
|
|
76
|
-
options[:dns] = true
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
opts.on("--nd", "--no-dns", "Don't use DNS seeds") do
|
|
80
|
-
options[:dns] = false
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
opts.on("--epoll", "Enable epoll support") do
|
|
84
|
-
options[:epoll] = true
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
opts.on("--epoll-limit [NUM]",
|
|
88
|
-
"Increase socket descriptor limit to NUM") do |num|
|
|
89
|
-
options[:epoll_limit] = num.to_i
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
opts.on("--epoll-user [NAME]",
|
|
93
|
-
"Set effective user after increasing socket descriptor limit") do |user|
|
|
94
|
-
options[:epoll_user] = user
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
opts.on("--mco", "--max-connections-out COUNT", "Maximum number of outgoing connections (default: #{options[:max][:connections_out]})") do |count|
|
|
98
|
-
options[:max][:connections_out] = count.to_i
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
opts.on("--mci", "--max-connections-in COUNT", "Maximum number of incoming connections (default: #{options[:max][:connections_in]})") do |count|
|
|
102
|
-
options[:max][:connections_in] = count.to_i
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
[:addr, :queue, :inv, :unconfirmed].each do |name|
|
|
106
|
-
opts.on("--m#{name.to_s[0]}", "--max-#{name} [COUNT]",
|
|
107
|
-
"Max #{name} (default: #{options[:max][name]})") do |count|
|
|
108
|
-
options[:max][name] = count.to_i
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
[:queue, :inv_queue, :blocks, :addrs, :connect].each do |name|
|
|
113
|
-
opts.on("--i#{name.to_s[0]}", "--interval-#{name} [SECONDS]",
|
|
114
|
-
"Interval for #{name} worker (default: #{options[:intervals][name]})") do |sec|
|
|
115
|
-
options[:intervals][name] = sec.to_i
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
[:network, :storage].each do |name|
|
|
120
|
-
opts.on("--l#{name.to_s[0]}", "--log-#{name} [LEVEL]",
|
|
121
|
-
"Log level for #{name} (default: #{options[:log][name]})") do |level|
|
|
122
|
-
options[:log][name] = level.to_sym
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
opts.on("--import DIR", "Import blk*.dat files from DIR") do |dir|
|
|
127
|
-
options[:import] = dir
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
opts.on("-v", "--verbose", "Set all loggers to debug") do
|
|
131
|
-
options[:log].each_key {|k| options[:log][k] = :debug }
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
opts.on("-q", "--quiet", "Set all loggers to warn") do
|
|
135
|
-
options[:log].each_key {|k| options[:log][k] = :warn }
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
opts.on( '-h', '--help', 'Display this screen' ) do
|
|
139
|
-
puts opts; exit
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
optparse.parse!
|
|
144
|
-
|
|
145
|
-
Bitcoin.network = options[:network]
|
|
146
|
-
FileUtils.mkdir_p File.join(ENV['HOME'], ".bitcoin-ruby/#{Bitcoin.network_name}")
|
|
147
|
-
|
|
148
|
-
if ARGV.any?
|
|
149
|
-
system(File.join(File.dirname(__FILE__), "bitcoin_node_cli"), *ARGV)
|
|
150
|
-
else
|
|
151
|
-
node = Bitcoin::Network::Node.new(options)
|
|
152
|
-
node.run
|
|
153
|
-
end
|
data/bin/bitcoin_node_cli
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
|
3
|
-
|
|
4
|
-
require 'bitcoin'
|
|
5
|
-
require 'optparse'
|
|
6
|
-
require 'fileutils'
|
|
7
|
-
Bitcoin.require_dependency :eventmachine
|
|
8
|
-
Bitcoin.require_dependency :json
|
|
9
|
-
|
|
10
|
-
defaults = Bitcoin::Network::Node::DEFAULT_CONFIG
|
|
11
|
-
|
|
12
|
-
options = Bitcoin::Config.load(defaults, :blockchain)
|
|
13
|
-
|
|
14
|
-
optparse = OptionParser.new do |opts|
|
|
15
|
-
opts.banner = "Usage: bitcoin_node [options]"
|
|
16
|
-
|
|
17
|
-
opts.separator("\nAvailable options:\n")
|
|
18
|
-
|
|
19
|
-
opts.on("-c", "--config FILE",
|
|
20
|
-
"Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
|
|
21
|
-
options = Bitcoin::Config.load_file(options, file, :blockchain)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
opts.on("-n", "--network [NETWORK]",
|
|
25
|
-
"User Network (default: #{options[:network]})") do |network|
|
|
26
|
-
options[:network] = network
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
opts.on("--command [HOST:PORT]",
|
|
30
|
-
"Command socket (default: #{options[:command].join(':')})") do |command|
|
|
31
|
-
options[:command] = command.split(":")
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
opts.on( '-h', '--help', 'Display this screen' ) do
|
|
35
|
-
puts opts; exit
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
optparse.parse!
|
|
40
|
-
|
|
41
|
-
Bitcoin.network = options[:network]
|
|
42
|
-
FileUtils.mkdir_p File.join(ENV['HOME'], ".bitcoin-ruby/#{Bitcoin.network_name}")
|
|
43
|
-
|
|
44
|
-
EM.run do
|
|
45
|
-
Bitcoin::Network::CommandClient.connect(*options[:command]) do
|
|
46
|
-
on_response do |cmd, data|
|
|
47
|
-
unless cmd == "monitor"
|
|
48
|
-
puts JSON.pretty_generate data
|
|
49
|
-
EM.stop
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
on_block do |block, depth|
|
|
53
|
-
hash = Bitcoin::P::Block.new(block['hex'].htb).to_hash.merge("depth" => block['depth'])
|
|
54
|
-
puts JSON.pretty_generate(hash)
|
|
55
|
-
end
|
|
56
|
-
on_tx do |tx, confirmations|
|
|
57
|
-
hash = Bitcoin::P::Tx.new(tx['hex'].htb).to_hash.merge("nhash" => tx['nhash'], "conf" => tx['conf'])
|
|
58
|
-
puts JSON.pretty_generate(hash)
|
|
59
|
-
end
|
|
60
|
-
on_output do |data|
|
|
61
|
-
puts JSON.pretty_generate(data)
|
|
62
|
-
end
|
|
63
|
-
on_connection do |data|
|
|
64
|
-
p data
|
|
65
|
-
end
|
|
66
|
-
on_connected do
|
|
67
|
-
cmd, params = ARGV.shift, Hash[ARGV.map {|a| a.split("=") }]
|
|
68
|
-
case cmd
|
|
69
|
-
when "create_tx"
|
|
70
|
-
params["keys"] = JSON.parse(params["keys"])
|
|
71
|
-
params["recipients"] = JSON.parse(params["recipients"])
|
|
72
|
-
when "assemble_tx"
|
|
73
|
-
params["sig_pubs"] = JSON.parse(params["sig_pubs"])
|
|
74
|
-
when "monitor"
|
|
75
|
-
params["addresses"] = JSON.parse(params["addresses"]) if params["addresses"]
|
|
76
|
-
$stdout.sync = true
|
|
77
|
-
end
|
|
78
|
-
request(cmd, params)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
data/bin/bitcoin_wallet
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
$:.unshift( File.expand_path("../../lib", __FILE__) )
|
|
3
|
-
|
|
4
|
-
require 'bitcoin'
|
|
5
|
-
require 'eventmachine'
|
|
6
|
-
require 'optparse'
|
|
7
|
-
require 'yaml'
|
|
8
|
-
|
|
9
|
-
defaults = {
|
|
10
|
-
:network => "testnet",
|
|
11
|
-
:storage => nil,
|
|
12
|
-
:keystore => nil,
|
|
13
|
-
:command => "127.0.0.1:9999"
|
|
14
|
-
}
|
|
15
|
-
options = Bitcoin::Config.load(defaults, :wallet)
|
|
16
|
-
|
|
17
|
-
optparse = OptionParser.new do |opts|
|
|
18
|
-
opts.banner =
|
|
19
|
-
"Usage: bitcoin_wallet [options] <command> [<command options>]\n"
|
|
20
|
-
|
|
21
|
-
opts.separator("\nAvailable options:\n")
|
|
22
|
-
|
|
23
|
-
opts.on("-c", "--config FILE",
|
|
24
|
-
"Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
|
|
25
|
-
options = Bitcoin::Config.load_file(options, file, :wallet)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
opts.on("-n", "--network NETWORK",
|
|
29
|
-
"User Network (default: #{options[:network]})") do |network|
|
|
30
|
-
options[:network] = network
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
opts.on("-s", "--storage BACKEND::CONFIG",
|
|
34
|
-
"Use storage backend (default: #{options[:storage]})") do |storage|
|
|
35
|
-
options[:storage] = storage
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
opts.on("--command [HOST:PORT]",
|
|
39
|
-
"Node command socket (default: #{options[:command]})") do |command|
|
|
40
|
-
options[:command] = command
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
opts.on("-k", "--keystore [backend::<config>]",
|
|
44
|
-
"Key store (default: #{options[:store]})") do |store|
|
|
45
|
-
options[:keystore] = store.gsub("~", ENV['HOME'])
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
opts.on("-h", "--help", "Display this help") do
|
|
49
|
-
puts opts; exit
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
opts.separator "\nAvailable commands:\n" +
|
|
53
|
-
" balance [<addr>] - display balance for given addr or whole wallet\n" +
|
|
54
|
-
" list <addr> - list transaction history for address\n" +
|
|
55
|
-
" send <addr>:<amount>[,<addr>:<amount>...] [<fee>] - send transaction\n" +
|
|
56
|
-
" new - generate new key and add to keystore\n" +
|
|
57
|
-
" import <base58> - import key in base58 format\n" +
|
|
58
|
-
" export <addr> - export key to base58 format\n" +
|
|
59
|
-
" name_list - list names in the wallet\n" +
|
|
60
|
-
" name_show <name> - display name information\n" +
|
|
61
|
-
" name_history <name> - display name history\n" +
|
|
62
|
-
" name_new <name> - reserve a name\n" +
|
|
63
|
-
" name_firstupdate <name> <rand> <value> - register a name\n" +
|
|
64
|
-
" name_update <name> <value> [<toaddress>] - update/transfer a name\n"
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
optparse.parse!
|
|
69
|
-
|
|
70
|
-
cmd = ARGV.shift; cmdopts = ARGV
|
|
71
|
-
unless cmd
|
|
72
|
-
puts optparse; exit
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
Bitcoin.network = options[:network]
|
|
76
|
-
|
|
77
|
-
options[:keystore] ||= "simple::file=~/.bitcoin-ruby/#{Bitcoin.network_name}/keys.json"
|
|
78
|
-
backend, config = options[:keystore].split("::")
|
|
79
|
-
config = Hash[config.split(",").map{|c| c.split("=")}]
|
|
80
|
-
keystore = Bitcoin::Wallet.const_get("#{backend.capitalize}KeyStore").new(config)
|
|
81
|
-
if backend == "deterministic" && !config["nonce"]
|
|
82
|
-
puts "nonce: #{keystore.generator.nonce}"
|
|
83
|
-
end
|
|
84
|
-
#puts *keystore.get_keys.map(&:addr)
|
|
85
|
-
|
|
86
|
-
options[:storage] ||= "sequel::sqlite://~/.bitcoin-ruby/#{Bitcoin.network_name}/blocks.db"
|
|
87
|
-
backend, config = options[:storage].split("::")
|
|
88
|
-
storage = Bitcoin::Storage.send(backend, :db => config)
|
|
89
|
-
|
|
90
|
-
wallet = Bitcoin::Wallet::Wallet.new(storage, keystore, Bitcoin::Wallet::SimpleCoinSelector)
|
|
91
|
-
|
|
92
|
-
def str_val(val, pre='')
|
|
93
|
-
("#{pre}%.8f" % (val / 1e8)).rjust(15)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def val_str(str)
|
|
97
|
-
(str.to_f * 1e8).to_i
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def send_transaction(storage, options, tx, ask = true)
|
|
101
|
-
# puts tx.to_json
|
|
102
|
-
if ask
|
|
103
|
-
total = 0
|
|
104
|
-
puts "Hash: #{tx.hash}"
|
|
105
|
-
puts "inputs:"
|
|
106
|
-
tx.in.each do |txin|
|
|
107
|
-
prev_out = storage.get_txout_for_txin(txin)
|
|
108
|
-
total += prev_out.value
|
|
109
|
-
puts " #{prev_out.get_address} - #{str_val prev_out.value}"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
puts "outputs:"
|
|
113
|
-
tx.out.each do |txout|
|
|
114
|
-
total -= txout.value
|
|
115
|
-
script = Bitcoin::Script.new(txout.pk_script)
|
|
116
|
-
print "#{str_val txout.value} "
|
|
117
|
-
if script.is_pubkey?
|
|
118
|
-
puts "#{script.get_pubkey} (pubkey)"
|
|
119
|
-
elsif script.is_hash160?
|
|
120
|
-
puts "#{script.get_address} (address)"
|
|
121
|
-
elsif script.is_multisig?
|
|
122
|
-
puts "#{script.get_addresses.join(' ')} (multisig)"
|
|
123
|
-
elsif script.is_op_return?
|
|
124
|
-
puts "#{script.get_op_return_data} (op_return)"
|
|
125
|
-
elsif script.is_namecoin?
|
|
126
|
-
puts "#{script.get_address} (#{script.type})"
|
|
127
|
-
print " " * 16
|
|
128
|
-
if script.is_name_new?
|
|
129
|
-
puts "Name Hash: #{script.get_namecoin_hash}"
|
|
130
|
-
else
|
|
131
|
-
puts "#{script.get_namecoin_name}: #{script.get_namecoin_value}"
|
|
132
|
-
end
|
|
133
|
-
else
|
|
134
|
-
puts "#{str_val txout.value} (unknown type)"
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
puts "Fee: #{str_val total}"
|
|
138
|
-
|
|
139
|
-
$stdout.sync = true
|
|
140
|
-
print "Really send transaction? (y/N) " and $stdout.flush
|
|
141
|
-
unless $stdin.gets.chomp.downcase == 'y'
|
|
142
|
-
puts "Aborted."; exit
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
EM.run do
|
|
146
|
-
Bitcoin::Network::CommandClient.connect(*options[:command]) do
|
|
147
|
-
on_connected do
|
|
148
|
-
request(:relay_tx, tx.to_payload.hth)
|
|
149
|
-
end
|
|
150
|
-
on_relay_tx do |res|
|
|
151
|
-
if res["success"]
|
|
152
|
-
puts "Transaction #{tx.hash} relayed to approx. #{"%.2f" % res['propagation']['percent']}% of the network."
|
|
153
|
-
else
|
|
154
|
-
puts "Error relaying tx: #{res['error']}"
|
|
155
|
-
end
|
|
156
|
-
EM.stop
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
case cmd
|
|
163
|
-
when "balance"
|
|
164
|
-
if cmdopts && cmdopts.size == 1
|
|
165
|
-
addr = cmdopts[0]
|
|
166
|
-
balance = storage.get_balance(Bitcoin.hash160_from_address(addr))
|
|
167
|
-
puts "#{addr} balance: #{str_val balance}"
|
|
168
|
-
else
|
|
169
|
-
puts "Total balance: #{str_val wallet.get_balance}"
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
when "new"
|
|
173
|
-
puts "Generated new key with address: #{wallet.get_new_addr}"
|
|
174
|
-
|
|
175
|
-
when "add"
|
|
176
|
-
key = {:label => ARGV[2]}
|
|
177
|
-
case ARGV[0]
|
|
178
|
-
when "pub"
|
|
179
|
-
k = Bitcoin::Key.new(nil, ARGV[1])
|
|
180
|
-
key[:key] = k
|
|
181
|
-
key[:addr] = k.addr
|
|
182
|
-
when "priv"
|
|
183
|
-
k = Bitcoin::Key.new(ARGV[1], nil)
|
|
184
|
-
k.regenerate_pubkey
|
|
185
|
-
key[:key] = k
|
|
186
|
-
key[:addr] = k.addr
|
|
187
|
-
when "addr"
|
|
188
|
-
key[:addr] = ARGV[1]
|
|
189
|
-
else
|
|
190
|
-
raise "unknown type #{ARGV[0]}"
|
|
191
|
-
end
|
|
192
|
-
wallet.add_key key
|
|
193
|
-
|
|
194
|
-
when "label"
|
|
195
|
-
wallet.label(ARGV[0], ARGV[1])
|
|
196
|
-
|
|
197
|
-
when "flag"
|
|
198
|
-
wallet.flag(ARGV[0], *ARGV[1].split("="))
|
|
199
|
-
|
|
200
|
-
when "key"
|
|
201
|
-
key = wallet.keystore.key(ARGV[0])
|
|
202
|
-
puts "Label: #{key[:label]}"
|
|
203
|
-
puts "Address: #{key[:addr]}"
|
|
204
|
-
puts "Pubkey: #{key[:key].pub}"
|
|
205
|
-
puts "Privkey: #{key[:key].priv}" if ARGV[1] == '-p'
|
|
206
|
-
puts "Mine: #{key[:mine]}"
|
|
207
|
-
|
|
208
|
-
when "import"
|
|
209
|
-
if wallet.keystore.respond_to?(:import)
|
|
210
|
-
addr = wallet.import_key(cmdopts[0])
|
|
211
|
-
puts "Key for #{addr} imported."
|
|
212
|
-
else
|
|
213
|
-
puts "Keystore doesn't support importing."
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
when "rescan"
|
|
217
|
-
wallet.rescan
|
|
218
|
-
|
|
219
|
-
when "export"
|
|
220
|
-
base58 = wallet.keystore.export(cmdopts[0])
|
|
221
|
-
puts "Base58 encoded private key for #{cmdopts[0]}:"
|
|
222
|
-
puts base58
|
|
223
|
-
|
|
224
|
-
when "list"
|
|
225
|
-
if cmdopts && cmdopts.size == 1
|
|
226
|
-
depth = storage.get_depth
|
|
227
|
-
total = 0
|
|
228
|
-
key = wallet.keystore.key(cmdopts[0])
|
|
229
|
-
storage.get_txouts_for_address(key[:addr]).each do |txout|
|
|
230
|
-
total += txout.value
|
|
231
|
-
tx = txout.get_tx
|
|
232
|
-
blocks = depth - tx.get_block.depth rescue 0
|
|
233
|
-
puts "#{tx.hash} | #{str_val txout.value, '+ '} | " +
|
|
234
|
-
"#{str_val total} | #{blocks}"
|
|
235
|
-
tx.in.map(&:get_prev_out).each do |prev_out|
|
|
236
|
-
if prev_out
|
|
237
|
-
puts " <- #{prev_out.get_address}"
|
|
238
|
-
else
|
|
239
|
-
puts " <- generation"
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
puts
|
|
243
|
-
|
|
244
|
-
if txin = txout.get_next_in
|
|
245
|
-
tx = txin.get_tx
|
|
246
|
-
total -= txout.value
|
|
247
|
-
blocks = depth - tx.get_block.depth rescue 0
|
|
248
|
-
puts "#{tx.hash} | #{str_val txout.value, '- '} | " +
|
|
249
|
-
"#{str_val total} | #{blocks}"
|
|
250
|
-
txin.get_tx.out.each do |out|
|
|
251
|
-
if Bitcoin.namecoin? && out.type.to_s =~ /^name_/
|
|
252
|
-
script = out.script
|
|
253
|
-
puts " -> #{script.get_namecoin_name || script.get_namecoin_hash} (#{out.type})"
|
|
254
|
-
else
|
|
255
|
-
puts " -> #{out.get_addresses.join(', ') rescue 'unknown'}"
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
puts
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
puts "Total balance: #{str_val total}"
|
|
262
|
-
else
|
|
263
|
-
puts "Wallet addresses:"
|
|
264
|
-
total = 0
|
|
265
|
-
wallet.list.each do |key, balance|
|
|
266
|
-
total += balance
|
|
267
|
-
icon = key[:key] && key[:key].priv ? "P" : (key[:mine] ? "M" : " ")
|
|
268
|
-
puts " #{icon} #{key[:label].to_s.ljust(10)} (#{key[:addr].to_s.ljust(34)}) - #{("%.8f" % (balance / 1e8)).rjust(15)}"
|
|
269
|
-
end
|
|
270
|
-
puts "Total balance: #{str_val wallet.get_balance}"
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
when "name_list"
|
|
274
|
-
names = wallet.get_txouts.select {|o| [:name_firstupdate, :name_update].include?(o.type)}
|
|
275
|
-
.map(&:get_namecoin_name).group_by(&:name).map {|n, l| l.sort_by(&:expires_in).last }.map {|name|
|
|
276
|
-
{ name: name.name, value: name.value, address: name.get_address, expires_in: name.expires_in } }
|
|
277
|
-
puts JSON.pretty_generate(names)
|
|
278
|
-
|
|
279
|
-
when "name_show"
|
|
280
|
-
name = storage.name_show(cmdopts[0])
|
|
281
|
-
puts name.to_json
|
|
282
|
-
|
|
283
|
-
when "name_history"
|
|
284
|
-
names = storage.name_history(cmdopts[0])
|
|
285
|
-
puts JSON.pretty_generate(names)
|
|
286
|
-
|
|
287
|
-
when "name_new"
|
|
288
|
-
name = cmdopts[0]
|
|
289
|
-
address = wallet.keystore.keys.sample[:key].addr
|
|
290
|
-
@rand = nil
|
|
291
|
-
def self.set_rand rand
|
|
292
|
-
@rand = rand
|
|
293
|
-
end
|
|
294
|
-
tx = wallet.new_tx([[:name_new, self, name, address, 1000000]])
|
|
295
|
-
(puts "Error creating tx."; exit) unless tx
|
|
296
|
-
send_transaction(storage, options, tx, true)
|
|
297
|
-
puts JSON.pretty_generate([tx.hash, @rand])
|
|
298
|
-
|
|
299
|
-
when "name_firstupdate"
|
|
300
|
-
name, rand, value = *cmdopts
|
|
301
|
-
address = wallet.keystore.keys.sample[:key].addr
|
|
302
|
-
tx = wallet.new_tx([[:name_firstupdate, name, rand, value, address, 1000000]])
|
|
303
|
-
(puts "Error creating tx."; exit) unless tx
|
|
304
|
-
send_transaction(storage, options, tx, true)
|
|
305
|
-
puts tx.hash
|
|
306
|
-
|
|
307
|
-
when "name_update"
|
|
308
|
-
name, value, address = *cmdopts
|
|
309
|
-
address ||= wallet.keystore.keys.sample[:key].addr
|
|
310
|
-
tx = wallet.new_tx([[:name_update, name, value, address, 1000000]])
|
|
311
|
-
(puts "Error creating tx."; exit) unless tx
|
|
312
|
-
send_transaction(storage, options, tx, true)
|
|
313
|
-
puts tx.hash
|
|
314
|
-
|
|
315
|
-
when "send"
|
|
316
|
-
to = cmdopts[0].split(',').map do |opts|
|
|
317
|
-
o = opts.split(":")
|
|
318
|
-
type, *addrs, value = *(o.size == 2 ? [:address, *o] : o)
|
|
319
|
-
value = val_str(value)
|
|
320
|
-
[type.to_sym, *addrs, value]
|
|
321
|
-
end
|
|
322
|
-
fee = val_str(cmdopts[1]) || 0
|
|
323
|
-
value = val_str value
|
|
324
|
-
|
|
325
|
-
unless wallet.get_balance >= (to.map{|t|t[-1]}.inject{|a,b|a+=b;a} + fee)
|
|
326
|
-
puts "Insufficient funds."; exit
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
tx = wallet.new_tx(to, fee)
|
|
330
|
-
|
|
331
|
-
if tx.is_a?(Bitcoin::Wallet::TxDP)
|
|
332
|
-
puts "Transaction needs to be signed by additional keys."
|
|
333
|
-
print "Filename to save TxDP: [./#{tx.id}.txdp] "
|
|
334
|
-
$stdout.flush
|
|
335
|
-
filename = $stdin.gets.strip
|
|
336
|
-
filename = "./#{tx.id}.txdp" if filename == ""
|
|
337
|
-
File.open(filename, "w") {|f| f.write(tx.serialize) }
|
|
338
|
-
exit
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
(puts "Error creating tx."; exit) unless tx
|
|
342
|
-
|
|
343
|
-
send_transaction(storage, options, tx)
|
|
344
|
-
|
|
345
|
-
when "sign"
|
|
346
|
-
txt = File.read(ARGV[0])
|
|
347
|
-
txdp = Bitcoin::Wallet::TxDP.parse(txt)
|
|
348
|
-
puts txdp.tx[0].to_json
|
|
349
|
-
|
|
350
|
-
print "Really sign transaction? (y/N) " and $stdout.flush
|
|
351
|
-
unless $stdin.gets.chomp.downcase == 'y'
|
|
352
|
-
puts "Aborted."; exit
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
txdp.sign_inputs do |tx, prev_tx, i, addr|
|
|
356
|
-
key = keystore.key(addr)[:key] rescue nil
|
|
357
|
-
next nil unless key && !key.priv.nil?
|
|
358
|
-
sig_hash = tx.signature_hash_for_input(i, prev_tx)
|
|
359
|
-
sig = key.sign(sig_hash)
|
|
360
|
-
script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [key.pub].pack("H*"))
|
|
361
|
-
script_sig.unpack("H*")[0]
|
|
362
|
-
end
|
|
363
|
-
File.open(ARGV[0], "w") {|f| f.write txdp.serialize }
|
|
364
|
-
|
|
365
|
-
when "relay"
|
|
366
|
-
txt = File.read(ARGV[0])
|
|
367
|
-
txdp = Bitcoin::Wallet::TxDP.parse(txt)
|
|
368
|
-
tx = txdp.tx[0]
|
|
369
|
-
puts tx.to_json
|
|
370
|
-
txdp.inputs.each_with_index do |s, i|
|
|
371
|
-
value, sigs = *s
|
|
372
|
-
tx.in[i].script_sig = [sigs[0][1]].pack("H*")
|
|
373
|
-
end
|
|
374
|
-
tx.in.each_with_index do |txin, i|
|
|
375
|
-
p txdp.tx.map(&:hash)
|
|
376
|
-
prev_tx = storage.get_tx(txin.prev_out.reverse_hth)
|
|
377
|
-
raise "prev tx #{txin.prev_out.reverse_hth} not found" unless prev_tx
|
|
378
|
-
raise "signature error" unless tx.verify_input_signature(i, prev_tx)
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
$stdout.sync = true
|
|
382
|
-
print "Really send transaction? (y/N) " and $stdout.flush
|
|
383
|
-
unless $stdin.gets.chomp.downcase == 'y'
|
|
384
|
-
puts "Aborted."; exit
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
EM.run do
|
|
388
|
-
EM.connect(*options[:command]) do |conn|
|
|
389
|
-
conn.send_data(["relay_tx", tx.to_payload.unpack("H*")[0]].to_json)
|
|
390
|
-
def conn.receive_data(data)
|
|
391
|
-
(@buf ||= BufferedTokenizer.new("\x00")).extract(data).each do |packet|
|
|
392
|
-
res = JSON.load(packet)
|
|
393
|
-
puts "Transaction relayed: #{res[1]["hash"]}"
|
|
394
|
-
EM.stop
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
else
|
|
401
|
-
puts "Unknown command. See --help for available commands."
|
|
402
|
-
end
|