bitcoin-ruby 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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