bin/bitcoin_wallet in bitcoin-ruby-0.0.1 vs bin/bitcoin_wallet in bitcoin-ruby-0.0.2

- old
+ new

@@ -6,12 +6,12 @@ require 'optparse' require 'yaml' defaults = { :network => "testnet", - :storage => "dummy", - :keystore => "simple::file=#{ENV['HOME']}/.bitcoin-ruby/keys.json", + :storage => nil, + :keystore => nil, :command => "127.0.0.1:9999" } options = Bitcoin::Config.load(defaults, :wallet) optparse = OptionParser.new do |opts| @@ -53,11 +53,17 @@ " balance [<addr>] - display balance for given addr or whole wallet\n" + " list <addr> - list transaction history for address\n" + " send <addr>:<amount>[,<addr>:<amount>...] [<fee>] - send transaction\n" + " new - generate new key and add to keystore\n" + " import <base58> - import key in base58 format\n" + - " export <addr> - export key to base58 format\n" + " export <addr> - export key to base58 format\n" + + " name_list - list names in the wallet\n" + + " name_show <name> - display name information\n" + + " name_history <name> - display name history\n" + + " name_new <name> - reserve a name\n" + + " name_firstupdate <name> <rand> <value> - register a name\n" + + " name_update <name> <value> [<toaddress>] - update/transfer a name\n" end optparse.parse! @@ -65,32 +71,94 @@ unless cmd exit puts optparse end Bitcoin.network = options[:network] + +options[:keystore] ||= "simple::file=~/.bitcoin-ruby/#{Bitcoin.network_name}/keys.json" backend, config = options[:keystore].split("::") config = Hash[config.split(",").map{|c| c.split("=")}] keystore = Bitcoin::Wallet.const_get("#{backend.capitalize}KeyStore").new(config) if backend == "deterministic" && !config["nonce"] puts "nonce: #{keystore.generator.nonce}" end #puts *keystore.get_keys.map(&:addr) + +options[:storage] ||= "sequel::sqlite://~/.bitcoin-ruby/#{Bitcoin.network_name}/blocks.db" backend, config = options[:storage].split("::") storage = Bitcoin::Storage.send(backend, :db => config) - wallet = Bitcoin::Wallet::Wallet.new(storage, keystore, Bitcoin::Wallet::SimpleCoinSelector) - def str_val(val, pre='') ("#{pre}%.8f" % (val / 1e8)).rjust(15) end def val_str(str) (str.to_f * 1e8).to_i end +def send_transaction(storage, options, tx, ask = true) + # puts tx.to_json + if ask + total = 0 + puts "Hash: #{tx.hash}" + puts "inputs:" + tx.in.each do |txin| + prev_out = storage.get_txout_for_txin(txin) + total += prev_out.value + puts " #{prev_out.get_address} - #{str_val prev_out.value}" + end + + puts "outputs:" + tx.out.each do |txout| + total -= txout.value + script = Bitcoin::Script.new(txout.pk_script) + print "#{str_val txout.value} " + if script.is_pubkey? + puts "#{script.get_pubkey} (pubkey)" + elsif script.is_hash160? + puts "#{script.get_address} (address)" + elsif script.is_multisig? + puts "#{script.get_addresses.join(' ')} (multisig)" + elsif script.is_namecoin? + puts "#{script.get_address} (#{script.type})" + print " " * 16 + if script.is_name_new? + puts "Name Hash: #{script.get_namecoin_hash}" + else + puts "#{script.get_namecoin_name}: #{script.get_namecoin_value}" + end + else + puts "#{str_val txout.value} (unknown type)" + end + end + puts "Fee: #{str_val total}" + + $stdout.sync = true + print "Really send transaction? (y/N) " and $stdout.flush + unless $stdin.gets.chomp.downcase == 'y' + puts "Aborted."; exit + end + end + EM.run do + Bitcoin::Network::CommandClient.connect(*options[:command].split(":")) do + on_connected do + request(:relay_tx, tx.to_payload.hth) + end + on_relay_tx do |res| + if res["success"] + puts "Transaction #{tx.hash} relayed to approx. #{"%.2f" % res['propagation']['percent']}% of the network." + else + puts "Error relaying tx: #{res['error']}" + end + EM.stop + end + end + end +end + case cmd when "balance" if cmdopts && cmdopts.size == 1 addr = cmdopts[0] balance = storage.get_balance(Bitcoin.hash160_from_address(addr)) @@ -133,19 +201,21 @@ puts "Address: #{key[:addr]}" puts "Pubkey: #{key[:key].pub}" puts "Privkey: #{key[:key].priv}" if ARGV[1] == '-p' puts "Mine: #{key[:mine]}" - when "import" if wallet.keystore.respond_to?(:import) - addr = wallet.keystore.import(cmdopts[0]) + addr = wallet.import_key(cmdopts[0]) puts "Key for #{addr} imported." else puts "Keystore doesn't support importing." end +when "rescan" + wallet.rescan + when "export" base58 = wallet.keystore.export(cmdopts[0]) puts "Base58 encoded private key for #{cmdopts[0]}:" puts base58 @@ -174,11 +244,16 @@ total -= txout.value blocks = depth - tx.get_block.depth rescue 0 puts "#{tx.hash} | #{str_val txout.value, '- '} | " + "#{str_val total} | #{blocks}" txin.get_tx.out.each do |out| - puts " -> #{out.get_addresses.join(', ')}" + if Bitcoin.namecoin? && out.type.to_s =~ /^name_/ + script = out.script + puts " -> #{script.get_namecoin_name || script.get_namecoin_hash} (#{out.type})" + else + puts " -> #{out.get_addresses.join(', ') rescue 'unknown'}" + end end puts end end puts "Total balance: #{str_val total}" @@ -191,10 +266,52 @@ puts " #{icon} #{key[:label].to_s.ljust(10)} (#{key[:addr].to_s.ljust(34)}) - #{("%.8f" % (balance / 1e8)).rjust(15)}" end puts "Total balance: #{str_val wallet.get_balance}" end +when "name_list" + names = wallet.get_txouts.select {|o| [:name_firstupdate, :name_update].include?(o.type)} + .map(&:get_namecoin_name).group_by(&:name).map {|n, l| l.sort_by(&:expires_in).last }.map {|name| + { name: name.name, value: name.value, address: name.get_address, expires_in: name.expires_in } } + puts JSON.pretty_generate(names) + +when "name_show" + name = storage.name_show(cmdopts[0]) + puts name.to_json + +when "name_history" + names = storage.name_history(cmdopts[0]) + puts JSON.pretty_generate(names) + +when "name_new" + name = cmdopts[0] + address = wallet.keystore.keys.sample[:key].addr + @rand = nil + def self.set_rand rand + @rand = rand + end + tx = wallet.new_tx([[:name_new, self, name, address, 1000000]]) + (puts "Error creating tx."; exit) unless tx + send_transaction(storage, options, tx, true) + puts JSON.pretty_generate([tx.hash, @rand]) + +when "name_firstupdate" + name, rand, value = *cmdopts + address = wallet.keystore.keys.sample[:key].addr + tx = wallet.new_tx([[:name_firstupdate, name, rand, value, address, 1000000]]) + (puts "Error creating tx."; exit) unless tx + send_transaction(storage, options, tx, true) + puts tx.hash + +when "name_update" + name, value, address = *cmdopts + address ||= wallet.keystore.keys.sample[:key].addr + tx = wallet.new_tx([[:name_update, name, value, address, 1000000]]) + (puts "Error creating tx."; exit) unless tx + send_transaction(storage, options, tx, true) + puts tx.hash + when "send" to = cmdopts[0].split(',').map do |pair| type, *addrs, value = pair.split(":") value = val_str(value) [type.to_sym, *addrs, value] @@ -215,56 +332,15 @@ filename = $stdin.gets.strip filename = "./#{tx.id}.txdp" if filename == "" File.open(filename, "w") {|f| f.write(tx.serialize) } exit end - - unless tx - puts "Error creating tx."; exit - end - total = 0 - puts "Hash: #{tx.hash}" - puts "inputs:" - tx.in.each do |txin| - prev_out = storage.get_txout_for_txin(txin) - total += prev_out.value - puts " #{prev_out.get_address} - #{str_val prev_out.value}" - end + (puts "Error creating tx."; exit) unless tx - puts "outputs:" - tx.out.each do |txout| - total -= txout.value - script = Bitcoin::Script.new(txout.pk_script) - if script.is_pubkey? - puts "#{str_val txout.value} #{script.get_pubkey} (pubkey)" - elsif script.is_hash160? - puts "#{str_val txout.value} #{script.get_address} (address)" - elsif script.is_multisig? - puts "#{str_val txout.value} #{script.get_addresses.join(' ')} (multisig)" - end - end - puts "Fee: #{str_val total}" + send_transaction(storage, options, tx) - $stdout.sync = true - print "Really send transaction? (y/N) " and $stdout.flush - unless $stdin.gets.chomp.downcase == 'y' - puts "Aborted."; exit - end - - EM.run do - Bitcoin::Network::CommandClient.connect(*options[:command].split(":")) do - on_connected do - request(:relay_tx, tx) - end - on_relay_tx do - puts "Transaction #{tx.hash} relayed" - EM.stop - end - end - end - when "sign" txt = File.read(ARGV[0]) txdp = Bitcoin::Wallet::TxDP.parse(txt) puts txdp.tx[0].to_json @@ -292,11 +368,11 @@ value, sigs = *s tx.in[i].script_sig = [sigs[0][1]].pack("H*") end tx.in.each_with_index do |txin, i| p txdp.tx.map(&:hash) - prev_tx = storage.get_tx(txin.prev_out.reverse.unpack("H*")[0]) - raise "prev tx #{txin.prev_out.reverse.unpack("H*")[0]} not found" unless prev_tx + prev_tx = storage.get_tx(txin.prev_out.reverse_hth) + raise "prev tx #{txin.prev_out.reverse_hth} not found" unless prev_tx raise "signature error" unless tx.verify_input_signature(i, prev_tx) end $stdout.sync = true print "Really send transaction? (y/N) " and $stdout.flush