lib/bitcoin/wallet/wallet.rb in bitcoin-ruby-0.0.1 vs lib/bitcoin/wallet/wallet.rb in bitcoin-ruby-0.0.2
- old
+ new
@@ -1,5 +1,7 @@
+# encoding: ascii-8bit
+
Bitcoin.require_dependency :eventmachine, exit: false
# The wallet implementation consists of several concepts:
# Wallet:: the high-level API used to manage a wallet
# SimpleKeyStore:: key store to manage keys/addresses/labels
@@ -13,32 +15,36 @@
# connect with a CommandClient to relay those transactions through a node.
#
# TODO: new tx notification, keygenerators, keystore cleanup
class Wallet
- include Bitcoin::Builder
+ include Bitcoin
+ include Builder
# the keystore (SimpleKeyStore) managing keys/addresses/labels
attr_reader :keystore
# the Storage which holds the blockchain
attr_reader :storage
# open wallet with given +storage+ Storage backend, +keystore+ SimpleKeyStore
# and +selector+ SimpleCoinSelector
- def initialize storage, keystore, selector
+ def initialize storage, keystore, selector = SimpleCoinSelector
@storage = storage
@keystore = keystore
@selector = selector
@callbacks = {}
- connect_node if defined?(EM)
+
+ @keystore.keys.each {|key| @storage.add_watched_address(key[:addr]) }
+ # connect_node if defined?(EM)
+
end
def connect_node
return unless EM.reactor_running?
host, port = "127.0.0.1", 9999
- @node = Bitcoin::Network::CommandClient.connect(host, port, self, @storage) do
+ @node = Network::CommandClient.connect(host, port, self, @storage) do
on_connected { request :monitor, "block", "tx" }
on_block do |block, depth|
EM.defer do
block['tx'].each do |tx|
relevant, tx = @args[0].check_tx(tx['hash'])
@@ -76,11 +82,11 @@
return false
end
def log
return @log if @log
- @log = Bitcoin::Logger.create("wallet")
+ @log = Logger.create("wallet")
@log.level = :debug
@log
end
# call the callback specified by +name+ passing in +args+
@@ -103,17 +109,16 @@
# get all Storage::Models::TxOut concerning any address from this wallet
def get_txouts(unconfirmed = false)
txouts = @keystore.keys.map {|k|
@storage.get_txouts_for_address(k[:addr])}.flatten.uniq
- unconfirmed ? txouts : txouts.select {|o| !!o.get_tx.get_block}
+ (unconfirmed || @storage.class.name =~ /Utxo/) ? txouts : txouts.select {|o| !!o.get_tx.get_block}
end
# get total balance for all addresses in this wallet
def get_balance(unconfirmed = false)
values = get_txouts(unconfirmed).select{|o| !o.get_next_in}.map(&:value)
-
([0] + values).inject(:+)
end
# list all addresses in this wallet
def addrs
@@ -121,10 +126,11 @@
end
# add +key+ to wallet
def add_key key
@keystore.add_key(key)
+ @storage.add_watched_address(key[:addr])
end
# set label for key +old+ to +new+
def label old, new
@keystore.label_key(old, new)
@@ -142,13 +148,25 @@
end
end
# create new key and return its address
def get_new_addr
- @keystore.new_key.addr
+ key = @keystore.new_key
+ @storage.add_watched_address(key.addr)
+ key.addr
end
+ def import_key base58, label = nil
+ key = @keystore.import(base58, label)
+ @storage.add_watched_address(key.addr)
+ key.addr
+ end
+
+ def rescan
+ @storage.rescan
+ end
+
# get SimpleCoinSelector with txouts for this wallet
def get_selector
@selector.new(get_txouts)
end
@@ -159,23 +177,40 @@
# examples:
# [:address, <addr>, <value>]
# [:multisig, 2, 3, <addr>, <addr>, <addr>, <value>]
#
# inputs are selected automatically by the SimpleCoinSelector.
- #
+ #
# change_policy controls where the change_output is spent to.
# see #get_change_addr
def new_tx outputs, fee = 0, change_policy = :back
- output_value = outputs.map{|o|o[-1]}.inject(:+)
+ output_value = outputs.map{|o| o[-1] }.inject(:+)
prev_outs = get_selector.select(output_value)
- return nil if !prev_outs
+ raise "Insufficient funds." if !prev_outs
+ if Bitcoin.namecoin?
+ prev_out = nil
+ outputs.each do |out|
+ if out[0] == :name_firstupdate
+ name_hash = Bitcoin.hash160(out[2] + out[1].hth)
+ break if prev_out = get_txouts.find {|o|
+ o.type == :name_new && o.script.get_namecoin_hash == name_hash }
+ elsif out[0] == :name_update
+ break if prev_out = storage.name_show(out[1]).get_txout rescue nil
+ end
+ end
+ if outputs.find{|o| [:name_firstupdate, :name_update].include?(o[0]) }
+ raise "previous name tx not found in wallet." unless prev_out
+ prev_outs += [prev_out]
+ end
+ end
input_value = prev_outs.map(&:value).inject(:+)
- return nil unless input_value >= (output_value + fee)
+ raise "Insufficient funds." unless input_value >= (output_value + fee)
- tx = tx do |t|
+ tx = build_tx do |t|
+ t.version 0x7100 if Bitcoin.namecoin? && outputs.find {|o| o[0].to_s =~ /^name_/ }
outputs.each do |type, *addrs, value|
t.output do |o|
o.value value
o.script do |s|
s.type type
@@ -199,21 +234,24 @@
prev_outs.each_with_index do |prev_out, idx|
t.input do |i|
prev_tx = prev_out.get_tx
i.prev_out prev_tx
i.prev_out_index prev_tx.out.index(prev_out)
- pk_script = Bitcoin::Script.new(prev_out.pk_script)
- if pk_script.is_pubkey? || pk_script.is_hash160?
+ pk_script = Script.new(prev_out.pk_script)
+ if pk_script.is_pubkey? || pk_script.is_hash160? || pk_script.is_namecoin?
i.signature_key @keystore.key(prev_out.get_address)[:key]
elsif pk_script.is_multisig?
raise "multisig not implemented"
end
end
end
end
+
# TODO: spend multisig outputs again
# TODO: verify signatures
- Bitcoin::Protocol::Tx.new(tx.to_payload)
+ raise "Payload Error" unless P::Tx.new(tx.to_payload).to_payload == tx.to_payload
+
+ tx
end
protected
# get address to send change output to.