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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +2 -7
  4. data/COPYING +1 -1
  5. data/Gemfile +2 -6
  6. data/Gemfile.lock +34 -0
  7. data/README.rdoc +16 -68
  8. data/Rakefile +3 -6
  9. data/bin/bitcoin_shell +0 -1
  10. data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
  11. data/lib/bitcoin.rb +350 -296
  12. data/lib/bitcoin/builder.rb +3 -1
  13. data/lib/bitcoin/connection.rb +2 -1
  14. data/lib/bitcoin/contracthash.rb +76 -0
  15. data/lib/bitcoin/dogecoin.rb +97 -0
  16. data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
  17. data/lib/bitcoin/ffi/openssl.rb +98 -2
  18. data/lib/bitcoin/ffi/secp256k1.rb +144 -0
  19. data/lib/bitcoin/key.rb +12 -2
  20. data/lib/bitcoin/logger.rb +3 -12
  21. data/lib/bitcoin/protocol/block.rb +3 -9
  22. data/lib/bitcoin/protocol/parser.rb +6 -2
  23. data/lib/bitcoin/protocol/tx.rb +44 -13
  24. data/lib/bitcoin/protocol/txin.rb +4 -2
  25. data/lib/bitcoin/protocol/txout.rb +2 -2
  26. data/lib/bitcoin/script.rb +212 -37
  27. data/lib/bitcoin/trezor/mnemonic.rb +130 -0
  28. data/lib/bitcoin/version.rb +1 -1
  29. data/spec/bitcoin/bitcoin_spec.rb +32 -3
  30. data/spec/bitcoin/builder_spec.rb +18 -0
  31. data/spec/bitcoin/contracthash_spec.rb +45 -0
  32. data/spec/bitcoin/dogecoin_spec.rb +176 -0
  33. data/spec/bitcoin/ffi_openssl.rb +45 -0
  34. data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
  35. data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
  36. data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
  37. data/spec/bitcoin/fixtures/coinbase.json +24 -0
  38. data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
  39. data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
  40. data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
  41. data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
  42. data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
  43. data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
  44. data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
  45. data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
  46. data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
  47. data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
  48. data/spec/bitcoin/protocol/block_spec.rb +0 -22
  49. data/spec/bitcoin/protocol/tx_spec.rb +145 -2
  50. data/spec/bitcoin/script/script_spec.rb +282 -0
  51. data/spec/bitcoin/secp256k1_spec.rb +48 -0
  52. data/spec/bitcoin/spec_helper.rb +0 -51
  53. data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
  54. metadata +48 -98
  55. data/bin/bitcoin_dns_seed +0 -130
  56. data/bin/bitcoin_gui +0 -80
  57. data/bin/bitcoin_node +0 -153
  58. data/bin/bitcoin_node_cli +0 -81
  59. data/bin/bitcoin_wallet +0 -402
  60. data/doc/CONFIG.rdoc +0 -66
  61. data/doc/EXAMPLES.rdoc +0 -13
  62. data/doc/NAMECOIN.rdoc +0 -34
  63. data/doc/NODE.rdoc +0 -225
  64. data/doc/STORAGE.rdoc +0 -33
  65. data/doc/WALLET.rdoc +0 -102
  66. data/examples/balance.rb +0 -66
  67. data/examples/forwarder.rb +0 -73
  68. data/examples/index_nhash.rb +0 -24
  69. data/examples/reindex_p2sh_addrs.rb +0 -44
  70. data/examples/relay_tx.rb +0 -22
  71. data/examples/verify_tx.rb +0 -57
  72. data/lib/bitcoin/config.rb +0 -58
  73. data/lib/bitcoin/gui/addr_view.rb +0 -44
  74. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  75. data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
  76. data/lib/bitcoin/gui/conn_view.rb +0 -38
  77. data/lib/bitcoin/gui/connection.rb +0 -70
  78. data/lib/bitcoin/gui/em_gtk.rb +0 -30
  79. data/lib/bitcoin/gui/gui.builder +0 -1643
  80. data/lib/bitcoin/gui/gui.rb +0 -292
  81. data/lib/bitcoin/gui/helpers.rb +0 -115
  82. data/lib/bitcoin/gui/tree_view.rb +0 -84
  83. data/lib/bitcoin/gui/tx_view.rb +0 -69
  84. data/lib/bitcoin/namecoin.rb +0 -280
  85. data/lib/bitcoin/network/command_client.rb +0 -104
  86. data/lib/bitcoin/network/command_handler.rb +0 -570
  87. data/lib/bitcoin/network/connection_handler.rb +0 -387
  88. data/lib/bitcoin/network/node.rb +0 -565
  89. data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
  90. data/lib/bitcoin/storage/models.rb +0 -171
  91. data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
  92. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
  93. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
  94. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
  95. data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
  96. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
  97. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
  98. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
  99. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
  100. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
  101. data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
  102. data/lib/bitcoin/storage/storage.rb +0 -517
  103. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
  104. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
  105. data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
  106. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
  107. data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
  108. data/lib/bitcoin/validation.rb +0 -400
  109. data/lib/bitcoin/wallet/coinselector.rb +0 -33
  110. data/lib/bitcoin/wallet/keygenerator.rb +0 -77
  111. data/lib/bitcoin/wallet/keystore.rb +0 -207
  112. data/lib/bitcoin/wallet/txdp.rb +0 -118
  113. data/lib/bitcoin/wallet/wallet.rb +0 -281
  114. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  115. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
  116. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  117. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
  118. data/spec/bitcoin/namecoin_spec.rb +0 -182
  119. data/spec/bitcoin/node/command_api_spec.rb +0 -663
  120. data/spec/bitcoin/storage/models_spec.rb +0 -104
  121. data/spec/bitcoin/storage/reorg_spec.rb +0 -236
  122. data/spec/bitcoin/storage/storage_spec.rb +0 -387
  123. data/spec/bitcoin/storage/validation_spec.rb +0 -300
  124. data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
  125. data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
  126. data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
  127. data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
  128. data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
@@ -1,207 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- require 'json'
4
- require 'stringio'
5
-
6
- module Bitcoin::Wallet
7
-
8
- # JSON-file-based keystore used by the Wallet.
9
- class SimpleKeyStore
10
-
11
- attr_reader :config
12
-
13
- # Initialize keystore.
14
- # [config] Hash of settings ({:file => "/foo/bar.json"})
15
- def initialize config
16
- @config = Hash[config.map{|k,v|[k.to_sym,v]}]
17
- @config[:file].sub!("~", ENV["HOME"]) if @config[:file].is_a?(String)
18
- @keys = []
19
- load_keys
20
- end
21
-
22
- # List all stored keys.
23
- def keys(need = nil)
24
- @keys.select do |key|
25
- next !(key[:hidden] && key[:hidden] == "true") unless need
26
- case need
27
- when :label
28
- !!key[:label]
29
- when :pub
30
- !!key[:key].pub
31
- when :priv
32
- !!key[:key].priv
33
- when :hidden
34
- !!key[:hidden]
35
- when :mine
36
- !!key[:mine]
37
- end
38
- end
39
- end
40
-
41
- # Get key for given +label+, +addr+ or +pubkey+.
42
- def key(name)
43
- find_key(name)
44
- end
45
-
46
- # Generate and store a new key.
47
- def new_key(label = nil)
48
- raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
49
- key = Bitcoin::Key.generate
50
- @keys << {:label => label, :addr => key.addr, :key => key}
51
- save_keys
52
- key
53
- end
54
-
55
- # Add a key which can consist only of +addr+ and +label+.
56
- def add_key key
57
- label = key[:label]
58
- raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
59
- addr = key[:addr]
60
- raise ArgumentError, "Address #{addr} is invalid" if addr && !Bitcoin.valid_address?(addr)
61
- @keys << key
62
- save_keys
63
- key
64
- end
65
-
66
- def label_key(name, label)
67
- find_key(name) do |key|
68
- key[:label] = label
69
- end
70
- save_keys
71
- end
72
-
73
- def flag_key(name, flag, value)
74
- find_key(name, true) do |key|
75
- key[flag.to_sym] = value
76
- end
77
- save_keys
78
- end
79
-
80
- # Delete key for given +label+, +addr+ or +pubkey+.
81
- def delete(name)
82
- key = find_key(name)
83
- @keys.delete(key)
84
- save_keys
85
- end
86
-
87
- # Export key for given +name+ to base58 format.
88
- # (See Bitcoin::Key#to_base58)
89
- def export(name)
90
- find_key(name)[:key].to_base58 rescue nil
91
- end
92
-
93
- # Import key from given +base58+ string.
94
- # (See Bitcoin::Key.from_base58)
95
- def import(base58, label = nil)
96
- raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
97
- key = Bitcoin::Key.from_base58(base58)
98
- raise ArgumentError, "Address #{key.addr} already in use" if label && find_key(key.addr)
99
- @keys << {:label => label, :addr => key.addr, :key => key}
100
- save_keys
101
- key
102
- end
103
-
104
- # Load keys from file.
105
- # If file is empty this will generate a new key
106
- # and store it, creating the file.
107
- def load_keys
108
- loader = proc{|keys|
109
- keys.map!{|k| Hash[k.map{|k,v| [k.to_sym, v] }]}
110
- keys.map do |key|
111
- key[:key] = Bitcoin::Key.new(key[:priv], key[:pub])
112
- key[:priv], key[:pub] = nil
113
- @keys << key
114
- end
115
- }
116
- if @config[:file].is_a?(StringIO)
117
- json = JSON.load(@config[:file].read)
118
- loader.call(json)
119
- elsif File.exist?(@config[:file])
120
- json = JSON.load(File.read(@config[:file]))
121
- loader.call(json)
122
- else
123
- new_key; save_keys
124
- end
125
- end
126
-
127
- # Save keys to file.
128
- def save_keys
129
- dumper = proc{|file|
130
- keys = @keys.map do |key|
131
- key = key.dup
132
- if key[:key]
133
- key[:priv] = key[:key].priv
134
- key[:pub] = key[:key].pub
135
- key.delete(:key)
136
- end
137
- key
138
- end
139
- file.write(JSON.pretty_generate(keys))
140
- }
141
-
142
- if @config[:file].is_a?(StringIO)
143
- @config[:file].reopen
144
- dumper.call(@config[:file])
145
- @config[:file].rewind
146
- else
147
- File.open(@config[:file], 'w'){|file| dumper.call(file) }
148
- end
149
- end
150
-
151
- private
152
-
153
- def find_key(name, hidden = false)
154
- key = if Bitcoin.valid_address?(name)
155
- @keys.find{|k| k[:addr] == name }
156
- elsif name.size == 130
157
- @keys.find{|k| k[:key].pub == name }
158
- else
159
- @keys.find{|k| k[:label] == name }
160
- end
161
- return nil if !key || (!hidden && key[:hidden] == "true")
162
- block_given? ? yield(key) : key
163
- end
164
-
165
- end
166
-
167
- # Deterministic keystore.
168
- class DeterministicKeyStore
169
-
170
- attr_reader :generator
171
-
172
- # Initialize keystore.
173
- # [config] Hash of settings ({:keys => 1, :seed => ..., :nonce => ...})
174
- def initialize config
175
- @config = Hash[config.map{|k,v|[k.to_sym,v]}]
176
- @config[:keys] = (@config[:keys] || 1).to_i
177
- @generator = Bitcoin::Wallet::KeyGenerator.new(@config[:seed], @config[:nonce])
178
- end
179
-
180
- # List all keys upto configured limit.
181
- def keys
182
- 1.upto(@config[:keys].to_i).map {|i| @generator.get_key(i) }
183
- end
184
-
185
- # Get key for given +addr+.
186
- def key(addr)
187
- 1.upto(@config[:keys].to_i).map do |i|
188
- key = @generator.get_key(i)
189
- return key if key.addr == addr
190
- end
191
- end
192
-
193
- # Get new key (actually just increase the key limit).
194
- def new_key
195
- @config[:keys] += 1
196
- @generator.get_key(@config[:keys])
197
- end
198
-
199
- # Export key for given +addr+ to base58.
200
- # (See Bitcoin::Key.to_base58)
201
- def export(addr)
202
- key(addr).to_base58 rescue nil
203
- end
204
-
205
- end
206
-
207
- end
@@ -1,118 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- class Bitcoin::Wallet::TxDP
4
-
5
- attr_accessor :id, :tx, :inputs
6
- def initialize tx = []
7
- @id = Bitcoin.int_to_base58(rand(1e14))
8
- @tx = tx
9
- @inputs = []
10
- return unless tx.any?
11
- @tx[0].in.each_with_index do |input, i|
12
- prev_out_hash = input.prev_out.reverse_hth
13
- prev_tx = @tx[1..-1].find {|tx| tx.hash == prev_out_hash}
14
- raise "prev tx #{prev_out_hash} not found" unless prev_tx
15
- prev_out = prev_tx.out[input.prev_out_index]
16
- raise "prev out ##{input.prev_out_index} not found in tx #{@tx.hash}" unless prev_out
17
- out_script = Bitcoin::Script.new(prev_out.pk_script)
18
- out_script.get_addresses.each do |addr|
19
- add_sig(i, prev_out.value, addr, input.script_sig)
20
- end
21
- end
22
- end
23
-
24
- def add_sig(in_idx, value, addr, sig)
25
- sig = sig ? [[addr, sig.unpack("H*")[0]]] : []
26
- @inputs[in_idx] = [value, sig]
27
- end
28
-
29
- def sign_inputs
30
- @inputs.each_with_index do |txin, i|
31
- input = @tx[0].in[i]
32
- prev_out_hash = input.prev_out.reverse_hth
33
- prev_tx = @tx[1..-1].find {|tx| tx.hash == prev_out_hash}
34
- raise "prev tx #{prev_out_hash} not found" unless prev_tx
35
- prev_out = prev_tx.out[input.prev_out_index]
36
- raise "prev out ##{input.prev_out_index} not found in tx #{@tx.hash}" unless prev_out
37
- out_script = Bitcoin::Script.new(prev_out.pk_script)
38
- out_script.get_addresses.each do |addr|
39
- sig = yield(@tx[0], prev_tx, i, addr)
40
- if sig
41
- @inputs[i][1] ||= []
42
- @inputs[i][1] << [addr, sig]
43
- break
44
- end
45
- end
46
- end
47
- end
48
-
49
- def serialize
50
- lines = []
51
- lines << "-----BEGIN-TRANSACTION-#{@id}".ljust(80, '-')
52
- size = [@tx.first.to_payload.bytesize].pack("C").ljust(2, "\x00").reverse_hth
53
- lines << "_TXDIST_#{Bitcoin.network[:magic_head].unpack("H*")[0]}_#{@id}_#{size}"
54
- tx = @tx.map(&:to_payload).join.unpack("H*")[0]
55
- tx_str = ""; tx.split('').each_with_index{|c,i| tx_str << (i % 80 == 0 ? "\n#{c}" : c)}
56
- lines << tx_str.strip
57
- @inputs.each_with_index do |input, idx|
58
- lines << "_TXINPUT_#{idx.to_s.rjust(2, '0')}_#{"%.8f" % (input[0].to_f / 1e8)}"
59
- next unless input[1]
60
- input[1].each do |sig|
61
- size = [sig[1]].pack("H*").bytesize
62
- size = [size].pack("C").ljust(2, "\x00").reverse_hth
63
- lines << "_SIG_#{sig[0]}_#{idx.to_s.rjust(2, '0')}_#{size}"
64
- sig_str = ""; sig[1].split('').each_with_index{|c,i| sig_str << (i % 80 == 0 ? "\n#{c}" : c)}
65
- lines << sig_str.strip
66
- end
67
- end
68
- lines << "-------END-TRANSACTION-#{@id}".ljust(80, '-')
69
- lines.join("\n")
70
- end
71
-
72
- def parse str
73
- str.match(/-+BEGIN-TRANSACTION-(.*?)-+$(.*?)END-TRANSACTION-#{$1}/m) do |m|
74
- _, id, content = *m
75
- txdist, *inputs = content.split(/_TXINPUT_/)
76
- @id = id
77
- @txdist = parse_txdist(txdist)
78
- inputs.each {|input| parse_input(input) }
79
- end
80
- self
81
- end
82
-
83
- def parse_txdist txdist
84
- _, magic, txdp_id, size, serialized_tx = *txdist.match(/_TXDIST_(.*?)_(.*?)_(.*?)$(.*)/m)
85
- raise "Wrong network magic" unless [magic].pack("H*") == Bitcoin.network[:magic_head]
86
- tx = Bitcoin::P::Tx.new(nil)
87
- rest = [serialized_tx.gsub!("\n", '')].pack("H*")
88
- while rest = tx.parse_data(rest)
89
- @tx << tx
90
- break if rest == true
91
- tx = Bitcoin::P::Tx.new(nil)
92
- end
93
- end
94
-
95
- def parse_input input
96
- m = input.match(/(\d+)_(\d+\.\d+)\n(.*)/m)
97
- _, idx, value, sigs = *m
98
- value = (value.sub('.','').to_i)
99
- sigs = parse_sigs(sigs)
100
- @inputs[idx.to_i] = [value, sigs]
101
- end
102
-
103
- def parse_sigs sigs
104
- return nil unless sigs["_SIG_"]
105
- sigs = sigs.split("_SIG_").map do |s|
106
- if s == ""
107
- nil
108
- else
109
- m = s.match(/(.*?)_(\d+)_(.*?)\n(.*)/m)
110
- [$1, $4.gsub("\n", '').gsub('-', '')]
111
- end
112
- end.compact
113
- end
114
-
115
- def self.parse str
116
- new.parse str
117
- end
118
- end
@@ -1,281 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- Bitcoin.require_dependency :eventmachine, exit: false
4
-
5
- # The wallet implementation consists of several concepts:
6
- # Wallet:: the high-level API used to manage a wallet
7
- # SimpleKeyStore:: key store to manage keys/addresses/labels
8
- # SimpleCoinSelector:: coin selector to find unspent outputs to use when creating tx
9
- module Bitcoin::Wallet
10
-
11
- # A wallet manages a set of keys (through a +keystore+), can
12
- # list transactions/balances for those keys (using a Storage backend for
13
- # blockchain data).
14
- # It can also create transactions with various kinds of outputs and
15
- # connect with a CommandClient to relay those transactions through a node.
16
- #
17
- # TODO: new tx notification, keygenerators, keystore cleanup
18
- class Wallet
19
-
20
- include Bitcoin
21
- include Builder
22
-
23
- # the keystore (SimpleKeyStore) managing keys/addresses/labels
24
- attr_reader :keystore
25
-
26
- # the Storage which holds the blockchain
27
- attr_reader :storage
28
-
29
- # open wallet with given +storage+ Storage backend, +keystore+ SimpleKeyStore
30
- # and +selector+ SimpleCoinSelector
31
- def initialize storage, keystore, selector = SimpleCoinSelector
32
- @storage = storage
33
- @keystore = keystore
34
- @selector = selector
35
- @callbacks = {}
36
-
37
- @keystore.keys.each {|key| @storage.add_watched_address(key[:addr]) }
38
- # connect_node if defined?(EM)
39
-
40
- end
41
-
42
- def connect_node
43
- return unless EM.reactor_running?
44
- host, port = "127.0.0.1", 9999
45
- @node = Network::CommandClient.connect(host, port, self, @storage) do
46
- on_connected { request :monitor, "block", "tx" }
47
- on_block do |block, depth|
48
- EM.defer do
49
- block['tx'].each do |tx|
50
- relevant, tx = @args[0].check_tx(tx['hash'])
51
- @args[0].callback(:tx, :confirmed, tx) if relevant
52
- end
53
- end
54
- end
55
-
56
- on_tx do |response|
57
- EM.defer do
58
- relevant, tx = @args[0].check_tx(response['hash'])
59
- @args[0].callback(:tx, relevant, tx) if relevant
60
- end
61
- end
62
- end
63
- end
64
-
65
- def check_tx tx_hash
66
- relevant = false
67
- addrs = addrs
68
- tx = @storage.get_tx(tx_hash)
69
- unless tx
70
- log.warn { "Received tx #{response['hash']} but not found in storage" }
71
- binding.pry
72
- return false
73
- end
74
- addrs = @keystore.keys.map {|k| k[:addr] }
75
- tx.out.each do |txout|
76
- return :incoming, tx if (txout.get_addresses & addrs).any?
77
- end
78
- tx.in.each do |txin|
79
- next unless prev_out = txin.get_prev_out
80
- return :outgoing, tx if (prev_out.get_addresses & addrs).any?
81
- end
82
- return false
83
- end
84
-
85
- def log
86
- return @log if @log
87
- @log = Logger.create("wallet")
88
- @log.level = :debug
89
- @log
90
- end
91
-
92
- # call the callback specified by +name+ passing in +args+
93
- def callback name, *args
94
- cb = @callbacks[name.to_sym]
95
- return unless cb
96
- log.debug { "callback: #{name}" }
97
- cb.call(*args)
98
- end
99
-
100
- # register callback methods
101
- def method_missing(name, *args, &block)
102
- if name =~ /^on_/
103
- @callbacks[name.to_s.split("on_")[1].to_sym] = block
104
- log.debug { "callback #{name} registered" }
105
- else
106
- super(name, *args)
107
- end
108
- end
109
-
110
- # get all Storage::Models::TxOut concerning any address from this wallet
111
- def get_txouts(unconfirmed = false)
112
- txouts = @keystore.keys.map {|k|
113
- @storage.get_txouts_for_address(k[:addr])}.flatten.uniq
114
- (unconfirmed || @storage.class.name =~ /Utxo/) ? txouts : txouts.select {|o| !!o.get_tx.get_block}
115
- end
116
-
117
- # get total balance for all addresses in this wallet
118
- def get_balance(unconfirmed = false)
119
- values = get_txouts(unconfirmed).select{|o| !o.get_next_in}.map(&:value)
120
- ([0] + values).inject(:+)
121
- end
122
-
123
- # list all addresses in this wallet
124
- def addrs
125
- @keystore.keys.map{|k| k[:addr]}
126
- end
127
-
128
- # add +key+ to wallet
129
- def add_key key
130
- @keystore.add_key(key)
131
- @storage.add_watched_address(key[:addr])
132
- end
133
-
134
- # set label for key +old+ to +new+
135
- def label old, new
136
- @keystore.label_key(old, new)
137
- end
138
-
139
- # set +flag+ for key +name+ to +value+
140
- def flag name, flag, value
141
- @keystore.flag_key(name, flag, value)
142
- end
143
-
144
- # list all keys along with their balances
145
- def list
146
- @keystore.keys.map do |key|
147
- [key, @storage.get_balance(Bitcoin.hash160_from_address(key[:addr]))]
148
- end
149
- end
150
-
151
- # create new key and return its address
152
- def get_new_addr
153
- key = @keystore.new_key
154
- @storage.add_watched_address(key.addr)
155
- key.addr
156
- end
157
-
158
- def import_key base58, label = nil
159
- key = @keystore.import(base58, label)
160
- @storage.add_watched_address(key.addr)
161
- key.addr
162
- end
163
-
164
- def rescan
165
- @storage.rescan
166
- end
167
-
168
- # get SimpleCoinSelector with txouts for this wallet
169
- def get_selector
170
- @selector.new(get_txouts)
171
- end
172
-
173
- # create a transaction with given +outputs+, +fee+ and +change_policy+.
174
- #
175
- # outputs are of the form
176
- # [<type>, <recipients>, <value>]
177
- # examples:
178
- # [:address, <addr>, <value>]
179
- # [:multisig, 2, 3, <addr>, <addr>, <addr>, <value>]
180
- #
181
- # inputs are selected automatically by the SimpleCoinSelector.
182
- #
183
- # change_policy controls where the change_output is spent to.
184
- # see #get_change_addr
185
- def new_tx outputs, fee = 0, change_policy = :back
186
- output_value = outputs.map{|o| o[-1] }.inject(:+)
187
-
188
- prev_outs = get_selector.select(output_value)
189
- raise "Insufficient funds." if !prev_outs
190
- if Bitcoin.namecoin?
191
- prev_out = nil
192
- outputs.each do |out|
193
- if out[0] == :name_firstupdate
194
- name_hash = Bitcoin.hash160(out[2] + out[1].hth)
195
- break if prev_out = get_txouts.find {|o|
196
- o.type == :name_new && o.script.get_namecoin_hash == name_hash }
197
- elsif out[0] == :name_update
198
- break if prev_out = storage.name_show(out[1]).get_txout rescue nil
199
- end
200
- end
201
- if outputs.find{|o| [:name_firstupdate, :name_update].include?(o[0]) }
202
- raise "previous name tx not found in wallet." unless prev_out
203
- prev_outs += [prev_out]
204
- end
205
- end
206
-
207
- input_value = prev_outs.map(&:value).inject(:+)
208
- raise "Insufficient funds." unless input_value >= (output_value + fee)
209
-
210
- tx = build_tx do |t|
211
- t.version 0x7100 if Bitcoin.namecoin? && outputs.find {|o| o[0].to_s =~ /^name_/ }
212
- outputs.each do |type, *addrs, value|
213
- t.output do |o|
214
- o.value value
215
- o.script do |s|
216
- s.type type
217
- s.recipient *addrs
218
- end
219
- end
220
- end
221
-
222
- change_value = input_value - output_value - fee
223
- if change_value > 0
224
- change_addr = get_change_addr(change_policy, prev_outs.sample.get_address)
225
- t.output do |o|
226
- o.value change_value
227
- o.script do |s|
228
- s.type :address
229
- s.recipient change_addr
230
- end
231
- end
232
- end
233
-
234
- prev_outs.each_with_index do |prev_out, idx|
235
- t.input do |i|
236
- prev_tx = prev_out.get_tx
237
- i.prev_out prev_tx
238
- i.prev_out_index prev_tx.out.index(prev_out)
239
- pk_script = Script.new(prev_out.pk_script)
240
- if pk_script.is_pubkey? || pk_script.is_hash160? || pk_script.is_namecoin?
241
- i.signature_key @keystore.key(prev_out.get_address)[:key]
242
- elsif pk_script.is_multisig?
243
- raise "multisig not implemented"
244
- end
245
- end
246
- end
247
- end
248
-
249
- # TODO: spend multisig outputs again
250
- # TODO: verify signatures
251
- raise "Payload Error" unless P::Tx.new(tx.to_payload).to_payload == tx.to_payload
252
-
253
- tx
254
- end
255
-
256
- protected
257
-
258
- # get address to send change output to.
259
- # +policy+ controls which address is chosen:
260
- # first:: send to the first key in the wallets keystore
261
- # random:: send to a random key from the wallets keystore
262
- # new:: send to a new key generated in the wallets keystore
263
- # back:: send to the address given as +in_addr+
264
- def get_change_addr(policy, in_addr)
265
- case policy
266
- when :first
267
- @keystore.keys[0].addr
268
- when :random
269
- @keystore.keys.sample.addr
270
- when :new
271
- @keystore.new_key.addr
272
- when :back
273
- in_addr
274
- else
275
- policy
276
- end
277
- end
278
-
279
- end
280
-
281
- end