Commit 540865da277528c758d93771f70004390c8dda3d

Authored by Marius Hanne
1 parent b4547a3b78

gui: display spent tx, toggle un/confirmed tx display, add remote addresses to wallet

Showing 13 changed files with 242 additions and 71 deletions Side-by-side Diff

lib/bitcoin/gui/addr_view.rb
... ... @@ -5,25 +5,37 @@
5 5 super(gui, :addr_view, [
6 6 [GObject::TYPE_STRING, "Address", :format_address_col],
7 7 [GObject::TYPE_STRING],
8   - [GObject::TYPE_STRING, "Balance", :format_value_col]])
9   - embed(:addr_view)
  8 + [GObject::TYPE_STRING, "Balance", :format_value_col],
  9 + [GObject::TYPE_BOOLEAN, "Mine?"]])
10 10 end
11 11  
12 12 def update addrs
13   - @model.clear
14   - addrs.each do |addr, balance|
15   - row = @model.append(nil)
16   - @model.set_value(row, 0, addr[:addr])
17   - @model.set_value(row, 1, addr[:label] || "")
18   - @model.set_value(row, 2, balance.to_s)
19   -
20   - @gui.storage.get_txouts_for_address(addr[:addr]).each do |txout|
21   - tx_row = @model.append(row)
22   - @model.set_value(tx_row, 0, txout.get_tx.hash)
23   - @model.set_value(tx_row, 2, txout.value.to_s)
  13 + EM.defer do
  14 + @model.clear
  15 + addrs.each do |addr|
  16 + row = @model.append(nil)
  17 + @model.set_value(row, 0, addr[:addr])
  18 + @model.set_value(row, 1, addr[:label] || "")
  19 + @model.set_value(row, 3, !!addr[:mine])
  20 + balance = 0
  21 + unconfirmed = @gui.check_unconfirmed.active
  22 + @gui.storage.get_txouts_for_address(addr[:addr]).each do |txout|
  23 + next if !unconfirmed && !txout.get_tx.get_block
  24 + tx_row = @model.append(row)
  25 + @model.set_value(tx_row, 0, txout.get_tx.hash)
  26 + @model.set_value(tx_row, 2, txout.value.to_s)
  27 + balance += txout.value
  28 + if txin = txout.get_next_in
  29 + tx_row = @model.append(row)
  30 + @model.set_value(tx_row, 0, txin.get_tx.hash)
  31 + @model.set_value(tx_row, 2, (0 - txout.value).to_s)
  32 + balance -= txout.value
  33 + end
  34 + end
  35 + @model.set_value(row, 2, balance.to_s)
24 36 end
  37 + @view.set_model @model
25 38 end
26   - @view.set_model @model
27 39 end
28 40  
29 41 end
lib/bitcoin/gui/bitcoin-ruby.png

2.1 KB

lib/bitcoin/gui/bitcoin-ruby.svg
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +
  4 +<svg
  5 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  6 + xmlns:cc="http://creativecommons.org/ns#"
  7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8 + xmlns:svg="http://www.w3.org/2000/svg"
  9 + xmlns="http://www.w3.org/2000/svg"
  10 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  11 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  12 + width="744.09448819"
  13 + height="1052.3622047"
  14 + id="svg2"
  15 + version="1.1"
  16 + inkscape:version="0.48.3.1 r9886"
  17 + sodipodi:docname="bitcoin-ruby.svg">
  18 + <defs
  19 + id="defs4" />
  20 + <sodipodi:namedview
  21 + id="base"
  22 + pagecolor="#ffffff"
  23 + bordercolor="#666666"
  24 + borderopacity="1.0"
  25 + inkscape:pageopacity="0.0"
  26 + inkscape:pageshadow="2"
  27 + inkscape:zoom="0.35"
  28 + inkscape:cx="-129.28571"
  29 + inkscape:cy="520"
  30 + inkscape:document-units="px"
  31 + inkscape:current-layer="layer1"
  32 + showgrid="false"
  33 + inkscape:window-width="1596"
  34 + inkscape:window-height="1180"
  35 + inkscape:window-x="2"
  36 + inkscape:window-y="18"
  37 + inkscape:window-maximized="0" />
  38 + <metadata
  39 + id="metadata7">
  40 + <rdf:RDF>
  41 + <cc:Work
  42 + rdf:about="">
  43 + <dc:format>image/svg+xml</dc:format>
  44 + <dc:type
  45 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  46 + <dc:title></dc:title>
  47 + </cc:Work>
  48 + </rdf:RDF>
  49 + </metadata>
  50 + <g
  51 + inkscape:label="Layer 1"
  52 + inkscape:groupmode="layer"
  53 + id="layer1">
  54 + <rect
  55 + style="fill:#8c0000;fill-opacity:1;stroke:#ffffff;stroke-width:0.75155282;stroke-opacity:1"
  56 + id="rect2987"
  57 + width="120.24844"
  58 + height="120.24844"
  59 + x="268.4472"
  60 + y="200.80939"
  61 + inkscape:export-filename="/home/mhanne/work/bitcoin-ruby/lib/bitcoin/gui/bitcoin-ruby.png"
  62 + inkscape:export-xdpi="74.380165"
  63 + inkscape:export-ydpi="74.380165" />
  64 + <text
  65 + xml:space="preserve"
  66 + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
  67 + x="285.71429"
  68 + y="303.79077"
  69 + id="text2989"
  70 + sodipodi:linespacing="125%"
  71 + inkscape:export-filename="/home/mhanne/work/bitcoin-ruby/lib/bitcoin/gui/bitcoin-ruby.png"
  72 + inkscape:export-xdpi="74.380165"
  73 + inkscape:export-ydpi="74.380165"><tspan
  74 + sodipodi:role="line"
  75 + id="tspan2991"
  76 + x="285.71429"
  77 + y="303.79077"
  78 + style="font-size:144px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:FreeMono;-inkscape-font-specification:FreeMono Bold">B</tspan></text>
  79 + </g>
  80 +</svg>
lib/bitcoin/gui/conn_view.rb
... ... @@ -9,7 +9,7 @@
9 9 [GObject::TYPE_INT, "Block"],
10 10 [GObject::TYPE_INT, "Uptime", :format_uptime_col],
11 11 [GObject::TYPE_STRING, "User Agent"]])
12   - embed(:conn_view)
  12 + @view.set_model @model
13 13 end
14 14  
15 15 def connected data
lib/bitcoin/gui/gui.builder
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <interface>
3 3 <!-- interface-requires gtk+ 3.0 -->
4   - <object class="GtkAction" id="action_exit">
5   - <property name="label" translatable="yes">aaa</property>
6   - <signal name="activate" handler="on_exit" swapped="no"/>
7   - </object>
8 4 <object class="GtkAboutDialog" id="about_dialog">
9 5 <property name="can_focus">False</property>
10 6 <property name="border_width">5</property>
11 7  
... ... @@ -34,8 +30,9 @@
34 30 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</property>
35 31 <property name="authors">Julian Langschaedel
36 32 Marius Hanne</property>
  33 + <property name="logo">bitcoin-ruby.png</property>
37 34 <property name="wrap_license">True</property>
38   - <property name="license_type">mit-x11</property>
  35 + <property name="license_type">artistic</property>
39 36 <signal name="response" handler="on_about_close" swapped="no"/>
40 37 <child internal-child="vbox">
41 38 <object class="GtkBox" id="aboutdialog-vbox1">
... ... @@ -65,6 +62,20 @@
65 62 <object class="GtkAccelGroup" id="accelgroup1">
66 63 <signal name="accel-activate" handler="on_accel_activate" swapped="no"/>
67 64 </object>
  65 + <object class="GtkAction" id="action_exit">
  66 + <property name="label" translatable="yes">aaa</property>
  67 + <signal name="activate" handler="on_exit" swapped="no"/>
  68 + </object>
  69 + <object class="GtkTreeStore" id="addr_store">
  70 + <columns>
  71 + <!-- column-name Address -->
  72 + <column type="gchararray"/>
  73 + <!-- column-name column1 -->
  74 + <column type="gchararray"/>
  75 + <!-- column-name Balance -->
  76 + <column type="gchararray"/>
  77 + </columns>
  78 + </object>
68 79 <object class="GtkImage" id="image_new_addr">
69 80 <property name="visible">True</property>
70 81 <property name="can_focus">False</property>
... ... @@ -75,6 +86,7 @@
75 86 <property name="can_focus">False</property>
76 87 <property name="stock">gtk-go-forward</property>
77 88 </object>
  89 + <object class="GtkListStore" id="liststore1"/>
78 90 <object class="GtkWindow" id="main_window">
79 91 <property name="can_focus">False</property>
80 92 <property name="title" translatable="yes">Bitcoin-Ruby GUI</property>
... ... @@ -177,6 +189,7 @@
177 189 <property name="can_focus">False</property>
178 190 <property name="use_underline">True</property>
179 191 <property name="use_stock">True</property>
  192 + <signal name="activate" handler="on_paste_addr" swapped="no"/>
180 193 </object>
181 194 </child>
182 195 <child>
... ... @@ -208,6 +221,23 @@
208 221 <property name="can_focus">False</property>
209 222 <property name="label" translatable="yes">_View</property>
210 223 <property name="use_underline">True</property>
  224 + <child type="submenu">
  225 + <object class="GtkMenu" id="menu4">
  226 + <property name="visible">True</property>
  227 + <property name="can_focus">False</property>
  228 + <child>
  229 + <object class="GtkCheckMenuItem" id="check_unconfirmed">
  230 + <property name="use_action_appearance">False</property>
  231 + <property name="visible">True</property>
  232 + <property name="can_focus">False</property>
  233 + <property name="tooltip_text" translatable="yes">Display transactions that are not yet confirmed and use unconfirmed values to calculate balances</property>
  234 + <property name="label" translatable="yes">Display Unconfirmed</property>
  235 + <property name="use_underline">True</property>
  236 + <signal name="toggled" handler="on_check_unconfirmed_toggled" swapped="no"/>
  237 + </object>
  238 + </child>
  239 + </object>
  240 + </child>
211 241 </object>
212 242 </child>
213 243 <child>
... ... @@ -227,6 +257,7 @@
227 257 <property name="use_action_appearance">False</property>
228 258 <property name="visible">True</property>
229 259 <property name="can_focus">False</property>
  260 + <property name="use_underline">True</property>
230 261 <property name="use_stock">True</property>
231 262 <property name="accel_group">accelgroup1</property>
232 263 <signal name="activate" handler="on_about" swapped="no"/>
... ... @@ -256,6 +287,7 @@
256 287 <object class="GtkTreeView" id="addr_view">
257 288 <property name="visible">True</property>
258 289 <property name="can_focus">True</property>
  290 + <property name="model">addr_store</property>
259 291 <property name="enable_grid_lines">both</property>
260 292 <child internal-child="selection">
261 293 <object class="GtkTreeSelection" id="treeview-selection1"/>
... ... @@ -1255,7 +1287,7 @@
1255 1287 </action-widgets>
1256 1288 </object>
1257 1289 <object class="GtkStatusIcon" id="statusicon">
1258   - <property name="stock">gtk-yes</property>
  1290 + <property name="pixbuf">bitcoin-ruby.png</property>
1259 1291 </object>
1260 1292 <object class="GtkDialog" id="tx_dialog">
1261 1293 <property name="can_focus">False</property>
lib/bitcoin/gui/gui.rb
... ... @@ -62,14 +62,21 @@
62 62  
63 63 def update_wallet_views
64 64 return unless @wallet
65   - @addr_view.update(@wallet.list)
66   - @tx_view.update(@wallet.get_txouts(true))
67   - wallet_text = "wallet: #{@wallet.keystore.config[:file]} | " +
68   - "addresses: #{@wallet.addrs.size} | " +
69   - "balance: #{"%.8f" % (@wallet.get_balance / 1e8)}"
70   - status_wallet.push 0, wallet_text
  65 + EM.defer do
  66 + @addr_view.update(@wallet.keystore.keys)#.select{|k| k[:mine] == true})
  67 + @tx_view.update(@wallet.get_txouts(true))
  68 + unconfirmed = check_unconfirmed.active
  69 + wallet_text = "wallet: #{@wallet.keystore.config[:file]} | " +
  70 + "addresses: #{@wallet.addrs.size} | " +
  71 + "balance: #{"%.8f" % (@wallet.get_balance(unconfirmed) / 1e8)}"
  72 + status_wallet.push 0, wallet_text
  73 + end
71 74 end
72 75  
  76 + def on_check_unconfirmed_toggled
  77 + update_wallet_views
  78 + end
  79 +
73 80 def on_preferences
74 81 dialog(:preferences, :setup => ->(d) {
75 82 config = Bitcoin::Config.load({}, :wallet)
76 83  
77 84  
... ... @@ -102,13 +109,27 @@
102 109 c.set_text text, text.size
103 110 end
104 111  
105   - def on_new_addr
  112 + def on_paste_addr
  113 + c = Gtk::Clipboard.get(Gdk::Atom.intern("PRIMARY", false))
  114 + c.request_text ->(clip, text, data) {
  115 + on_new_addr(text) if Bitcoin.valid_address?(text)
  116 + }, self
  117 + c = Gtk::Clipboard.get(Gdk::Atom.intern("CLIPBOARD", false))
  118 + c.request_text ->(clip, text, data) {
  119 + on_new_addr(text) if Bitcoin.valid_address?(text)
  120 + }, self
  121 + end
  122 +
  123 + def on_new_addr addr = nil
106 124 dialog(:new_addr, setup: ->(d) {
107   - new_addr_check_addr.active = false
  125 + new_addr_check_addr.active = addr ? true : false
108 126 new_addr_check_pubkey.active = false
109   - new_addr_check_mine.active = true
110   - [:label, :addr, :pubkey].each {|n| send("new_addr_entry_#{n}").text = "" }
111   - [:addr, :pubkey].each {|n| send("new_addr_entry_#{n}").hide }
  127 + new_addr_check_mine.active = addr ? false : true
  128 + new_addr_entry_label.text = ""
  129 + new_addr_entry_addr.text = addr ? addr : ""
  130 + new_addr_entry_pubkey.text = ""
  131 + new_addr_entry_addr.hide unless addr
  132 + new_addr_entry_pubkey.hide
112 133 GObject.signal_connect(new_addr_check_addr, "toggled") do
113 134 new_addr_check_addr.active ? new_addr_entry_addr.show :
114 135 new_addr_entry_addr.hide
... ... @@ -134,7 +155,7 @@
134 155 key[:key] = k
135 156 key[:addr] = k.addr
136 157 end
137   - if !set_addr && !set_pubkey
  158 + if !set_address && !set_pubkey
138 159 key[:key] = Bitcoin::Key.generate
139 160 key[:addr] = key[:key].addr
140 161 end
141 162  
142 163  
143 164  
144 165  
... ... @@ -157,25 +178,32 @@
157 178 model = Gtk::ListStore.new([GObject::TYPE_STRING, GObject::TYPE_STRING])
158 179 @wallet.keystore.keys.each do |key|
159 180 row = model.append
160   - model.set_value(row, 0, "#{key[:addr]}\n#{key[:label]}")
  181 + model.set_value(row, 0, key[:label] || "")
161 182 model.set_value(row, 1, key[:addr])
162 183 end
163 184 renderer = Gtk::CellRendererText.new
164 185 comp = Gtk::EntryCompletion.new_with_area(area)
165   - comp.text_column = 0
  186 + comp.text_column = 1
166 187 comp.minimum_key_length = 1
167 188 comp.set_match_func(->(comp, text, iter, _) {
168 189 label = comp.get_model.value(iter, 0).get_string
169 190 addr = comp.get_model.value(iter, 1).get_string
170 191 !!(label =~ /#{text}/ || addr =~ /#{text}/)
171 192 }, nil, nil)
  193 + comp.area.pack_start renderer, false, false, false
  194 + comp.area.orientation = :vertical
172 195 comp.set_model model
  196 + renderer.set_padding 10, 0
  197 + renderer.foreground = "blue"
  198 + comp.set_cell_data_func(renderer, ->(layout, renderer, model, iter, data) {
  199 + renderer.text = model.get_value(iter, 0).get_string
  200 + }, nil, nil)
173 201 new_tx_entry_address.set_completion comp
174   - GObject.signal_connect(comp, "match-selected") do |comp, _, iter, _|
175   - addr = comp.get_model.get_value(iter, 1).get_string
176   - new_tx_entry_address.text = addr
177   - true
178   - end
  202 +# GObject.signal_connect(comp, "match-selected") do |comp, _, iter, _|
  203 +# addr = comp.get_model.get_value(iter, 1).get_string
  204 +# new_tx_entry_address.text = addr
  205 +# true
  206 +# end
179 207 }) do |dialog|
180 208  
181 209 address = new_tx_entry_address.text
182 210  
... ... @@ -205,9 +233,9 @@
205 233 message(:question, "Really send transaction?",
206 234 "Do you really want to send #{format_value value} to #{address}?", [:no, :yes]) do
207 235 tx = @wallet.tx([[:address, *[address], value]], 0.00)
208   - puts tx.to_json
  236 + # puts tx.to_json
209 237 if @node.request(:relay_tx, tx)
210   - p 'hoho'
  238 + puts "Transaction #{tx} relayed."
211 239 end
212 240 end
213 241 end
lib/bitcoin/gui/helpers.rb
... ... @@ -58,7 +58,7 @@
58 58 (opts[:callbacks] || {}).each do |name, block|
59 59 ids << GObject.signal_connect(dialog, name) {|*a| block.call(*a) }
60 60 end
61   - dialog.show_all
  61 + dialog.show
62 62 @dialog_cb_ids[name] = ids
63 63 rescue
64 64 p $!
... ... @@ -76,6 +76,8 @@
76 76 end
77 77 }) do |dialog|
78 78 yield(dialog) if block_given?
  79 + dialog.show_all
  80 + dialog.hide
79 81 end
80 82 rescue
81 83 p $!
lib/bitcoin/gui/tree_view.rb
... ... @@ -9,13 +9,22 @@
9 9 @gui = gui
10 10 @view = @gui.builder.get_object(view_name.to_s)
11 11 @model = Gtk::TreeStore.new(columns.map{|c| c[0] })
  12 + @view.set_model @model
12 13  
13 14 columns.each_with_index do |cdef, i|
14 15 type, label, cb = *cdef
15 16 next unless label
16   - renderer = Gtk::CellRendererText.new
17   - col = tree_view_col(renderer, label, "text", i) do |*args|
18   - method(cb).call(@model, i, args[1], args[3]) if cb
  17 + case type
  18 + when GObject::TYPE_BOOLEAN
  19 + renderer = Gtk::CellRendererToggle.new
  20 + col = tree_view_col(renderer, label, "active", i) do |*args|
  21 + method(cb).call(@model, i, args[1], args[3]) if cb
  22 + end
  23 + when GObject::TYPE_STRING, GObject::TYPE_INT
  24 + renderer = Gtk::CellRendererText.new
  25 + col = tree_view_col(renderer, label, "text", i) do |*args|
  26 + method(cb).call(@model, i, args[1], args[3]) if cb
  27 + end
19 28 end
20 29 @view.append_column(col)
21 30 end
... ... @@ -62,6 +71,11 @@
62 71 def format_uptime_col model, i, renderer, iter
63 72 started = model.get_value(iter, i).get_int
64 73 renderer.text = format_uptime(started)
  74 + end
  75 +
  76 + def format_bool_col model, i, renderer, iter
  77 + active = model.get_value(iter, i).get_boolean
  78 + renderer.set_active active
65 79 end
66 80  
67 81 end
lib/bitcoin/gui/tx_view.rb
... ... @@ -18,24 +18,26 @@
18 18 end
19 19  
20 20 def update txouts
21   - @model.clear
22   - txouts.each do |txout|
23   - row = @model.append(nil)
24   - @model.set_value(row, 0, txout.type.to_s)
25   - @model.set_value(row, 1, txout.get_tx.hash)
26   - @model.set_value(row, 2, txout.value.to_s)
27   - @model.set_value(row, 3, txout.get_tx.confirmations)
28   - @model.set_value(row, 4, "incoming")
29   - if txin = txout.get_next_in
  21 + EM.defer do
  22 + @model.clear
  23 + txouts.each do |txout|
30 24 row = @model.append(nil)
31 25 @model.set_value(row, 0, txout.type.to_s)
32   - @model.set_value(row, 1, txin.get_tx.hash)
  26 + @model.set_value(row, 1, txout.get_tx.hash)
33 27 @model.set_value(row, 2, txout.value.to_s)
34   - @model.set_value(row, 3, txin.get_tx.confirmations)
35   - @model.set_value(row, 4, "outgoing")
  28 + @model.set_value(row, 3, txout.get_tx.confirmations)
  29 + @model.set_value(row, 4, "incoming")
  30 + if txin = txout.get_next_in
  31 + row = @model.append(nil)
  32 + @model.set_value(row, 0, txout.type.to_s)
  33 + @model.set_value(row, 1, txin.get_tx.hash)
  34 + @model.set_value(row, 2, (0 - txout.value).to_s)
  35 + @model.set_value(row, 3, txin.get_tx.confirmations)
  36 + @model.set_value(row, 4, "outgoing")
  37 + end
36 38 end
  39 + @view.set_model @model
37 40 end
38   - @view.set_model @model
39 41 end
40 42 end
41 43  
lib/bitcoin/network/command_handler.rb
... ... @@ -53,7 +53,7 @@
53 53 case channel.to_sym
54 54 when :block
55 55 head = Bitcoin::P::Block.new(@node.store.get_head.to_payload) rescue nil
56   - respond("monitor", ["block", [head, @node.store.get_depth.to_s]])
  56 + respond("monitor", ["block", [head, @node.store.get_depth.to_s]]) if head
57 57 when :connection
58 58 @node.connections.select {|c| c.connected?}.each do |conn|
59 59 respond("monitor", [:connection, [:connected, conn.info]])
lib/bitcoin/network/connection_handler.rb
... ... @@ -115,14 +115,14 @@
115 115 # received +tx+ message for given +tx+.
116 116 # push tx to storage queue
117 117 def on_tx(tx)
118   - log.info { ">> tx: #{tx.hash} (#{tx.payload.size} bytes)" }
  118 + log.debug { ">> tx: #{tx.hash} (#{tx.payload.size} bytes)" }
119 119 @node.queue.push([:tx, tx])
120 120 end
121 121  
122 122 # received +block+ message for given +blk+.
123 123 # push block to storage queue
124 124 def on_block(blk)
125   - log.info { ">> block: #{blk.hash} (#{blk.payload.size} bytes)" }
  125 + log.debug { ">> block: #{blk.hash} (#{blk.payload.size} bytes)" }
126 126 @node.queue.push([:block, blk])
127 127 end
128 128  
129 129  
... ... @@ -159,14 +159,14 @@
159 159 # send +getdata tx+ message for given tx +hash+
160 160 def send_getdata_tx(hash)
161 161 pkt = Protocol.getdata_pkt(:tx, [hash])
162   - log.info { "<< getdata tx: #{hth(hash)}" }
  162 + log.debug { "<< getdata tx: #{hth(hash)}" }
163 163 send_data(pkt)
164 164 end
165 165  
166 166 # send +getdata block+ message for given block +hash+
167 167 def send_getdata_block(hash)
168 168 pkt = Protocol.getdata_pkt(:block, [hash])
169   - log.info { "<< getdata block: #{hth(hash)}" }
  169 + log.debug { "<< getdata block: #{hth(hash)}" }
170 170 send_data(pkt)
171 171 end
172 172  
lib/bitcoin/storage/storage.rb
... ... @@ -147,10 +147,12 @@
147 147 end
148 148  
149 149 # get balance for given +hash160+
150   - def get_balance(hash160)
  150 + def get_balance(hash160, unconfirmed = false)
151 151 txouts = get_txouts_for_hash160(hash160)
152   - confirmed = txouts.select {|o| !!o.get_tx.get_block}
153   - unspent = confirmed.select {|o| o.get_next_in.nil?}
  152 + unless unconfirmed
  153 + txouts = txouts.select {|o| !!o.get_tx.get_block}
  154 + end
  155 + unspent = txouts.select {|o| o.get_next_in.nil?}
154 156 unspent.map(&:value).inject {|a,b| a+=b; a} || 0
155 157 rescue
156 158 nil
lib/bitcoin/wallet/wallet.rb
... ... @@ -47,7 +47,6 @@
47 47  
48 48 on_tx do |response|
49 49 EM.defer do
50   - p "tx: #{response['hash']}"
51 50 relevant, tx = @args[0].check_tx(response['hash'])
52 51 @args[0].callback(:tx, relevant, tx) if relevant
53 52 end
54 53  
... ... @@ -108,15 +107,15 @@
108 107 end
109 108  
110 109 # get total balance for all addresses in this wallet
111   - def get_balance
112   - values = get_txouts.select{|o| !o.get_next_in}.map(&:value)
  110 + def get_balance(unconfirmed = false)
  111 + values = get_txouts(unconfirmed).select{|o| !o.get_next_in}.map(&:value)
113 112  
114 113 ([0] + values).inject(:+)
115 114 end
116 115  
117 116 # list all addresses in this wallet
118 117 def addrs
119   - @keystore.keys.map{|k| k[:key].addr}
  118 + @keystore.keys.map{|k| k[:addr]}
120 119 end
121 120  
122 121 # add +key+ to wallet