bitcoin-ruby 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
data/.gitignore CHANGED
@@ -5,8 +5,11 @@ pkg/*
5
5
  *.swp
6
6
  .rvmrc
7
7
  *.DS_Store
8
- db/database.yml
9
8
  tmp/
10
9
  log/
11
10
  rdoc/
12
11
  coverage/
12
+ *.yml
13
+ *.conf
14
+ *.db
15
+ .rbx/
data/Gemfile CHANGED
@@ -2,3 +2,24 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in bitcoin-ruby.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'sqlite3', :platforms => :ruby
8
+
9
+ gem 'bacon', '>= 1.2.0'
10
+ gem 'simplecov', :require => false
11
+
12
+ gem 'rake', '>= 0.8.0'
13
+ end
14
+
15
+ group :development do
16
+ gem 'eventmachine'
17
+ gem 'ffi'
18
+ gem 'log4r'
19
+ gem 'sequel'
20
+
21
+ gem 'sqlite3', :platforms => :ruby, :require => false
22
+ gem 'pg', :platforms => :ruby, :require => false
23
+
24
+ gem "rake", ">= 0.8.0"
25
+ end
data/README.rdoc CHANGED
@@ -4,24 +4,31 @@ This is a ruby library for interacting with the bitcoin protocol/network.
4
4
 
5
5
  Some of the main features are:
6
6
 
7
- * bitcoin utility functions for base58, ECC, etc. (Bitcoin::Util)
8
- * parse/create (almost?) all protocol messages (Bitcoin::Protocol)
9
- * connect to peers and exchange messages (Bitcoin::Network::Node, NODE)
10
- * load the blockchain from the network and stay in sync (WITHOUT verification yet!)
11
- * store and the blockchain and query for txouts (Bitcoin::Storage)
12
- * script implementation, create/run scripts and verify signatures (Bitcoin::Script)
13
- * create transactions (and even blocks!) (Bitcoin::Protocol, Bitcoin::Builder)
14
- * manage keys (Bitcoin::Key) in a wallet (Bitcoin::Wallet, WALLET)
15
- * there is even a highly experimental(!) Bitcoin::Gui
7
+ * Bitcoin::Util provides the basic bitcoin utility functions for base58, ECC, etc.
8
+ * Bitcoin::Protocol can parse/create (almost?) all protocol messages
9
+ * Bitcoin::Network::Node connects to peers, fetches the blockchain and keeps it up to date
10
+ (see NODE for usage)
11
+ * Bitcoin::Validation validates block and transaction rules
12
+ * Bitcoin::Storage stores the blockchain and can be queried for transaction data
13
+ * Bitcoin::Script implementation, create/run scripts and verify signatures
14
+ * Bitcoin::Key provides a high-level API for creating and handling keys/addresses
15
+ * Bitcoin::Builder provides a high-level API for creating transactions (and blocks)
16
+ * Bitcoin::Wallet is a draft implementation of a simple wallet
17
+ * Bitcoin::Namecoin implements all the namecoin-specific differences (see NAMECOIN)
18
+
19
+ == Compatible with...
20
+
21
+ * ruby 1.9.3
22
+ * ruby 2.0.0
16
23
 
17
24
  == Installation
18
25
 
19
- We assume you already have a ruby 1.9 compatible interpreter and rubygems environment.
26
+ We assume you already have a ruby 1.9 or 2.0 compatible interpreter and rubygems environment.
20
27
 
21
28
  git clone https://github.com/lian/bitcoin-ruby.git; cd bitcoin-ruby
22
29
  ruby -Ilib bin/bitcoin_node
23
30
 
24
- if you want to install it system-wide, just build the gem and install it
31
+ if you want to have it available system-wide, just build the gem and install it:
25
32
 
26
33
  gem build bitcoin-ruby.gemspec && gem install bitcoin-ruby-0.0.1.gem
27
34
 
@@ -40,22 +47,21 @@ intentionally kept to a minimum, so nobody has to install unneeded dependencies.
40
47
 
41
48
  == Client
42
49
 
43
- There is a NODE which connects to the network and downloads
44
- the blockchain into a database. see Bitcoin::Network::Node.
45
-
46
- It also opens an extra socket for clients to connect where they can call certain
47
- methods, ask for information or register callbacks for events.
48
- see Bitcoin::Network::CommandClient.
50
+ There is a node which connects to the network and downloads
51
+ the blockchain into a database. see NODE, Bitcoin::Network::Node.
49
52
 
53
+ It also opens an extra socket where local clients can query statistics,
54
+ monitor blockchain data, and relay there transactions to the network.
55
+ see NODE, Bitcoin::Network::CommandHandler, Bitcoin::Network::CommandClient.
50
56
 
51
57
  There is a WALLET implementation to manage a set of keys, list balances and create
52
- transactions. see Bitcoin::Wallet
58
+ transactions. see WALLET, Bitcoin::Wallet.
53
59
 
54
60
 
55
61
  == Library Usage
56
62
 
57
63
  There are different aspects to the library which can be used separately or in combination.
58
- Here are some Ideas of what you could do. There are also some scripts which you can run,
64
+ Here are some ideas of what you could do. There are also some demo scripts in examples/,
59
65
  see EXAMPLES.
60
66
 
61
67
 
@@ -86,7 +92,7 @@ Parse a Bitcoin::Protocol::Block
86
92
  blk.tx.count #=> 1
87
93
  blk.to_hash #=> ...
88
94
  Bitcoin::Protocol::Block.from_json( blk.to_json )
89
-
95
+
90
96
  Parse a Bitcoin::Protocol::Tx
91
97
 
92
98
  raw_tx = File.open('spec/bitcoin/fixtures/rawtx-01.bin', 'rb') {|f| f.read}
@@ -96,7 +102,7 @@ Parse a Bitcoin::Protocol::Tx
96
102
  tx.out.size #=> 2
97
103
  tx.to_hash #=> ...
98
104
  Bitcoin::Protocol::Tx.from_json( tx.to_json )
99
-
105
+
100
106
  Bitcoin::Script.new(tx.out[0].pk_script).to_string
101
107
  #=> "OP_DUP OP_HASH160 b2e21c1db922e3bdc529de7b38b4c401399e9afd OP_EQUALVERIFY OP_CHECKSIG"
102
108
 
@@ -120,7 +126,7 @@ If you want to control the Bitcoin::Script yourself, you can do so
120
126
  txin = tx1.in.first
121
127
  txout = tx2.out[txin.prev_out_index]
122
128
  script = Bitcoin::Script.new(txin.script_sig + txout.pk_script)
123
-
129
+
124
130
  result = script.run do |pubkey, sig, hash_type|
125
131
  hash = tx1.signature_hash_for_input(0, nil, txout.pk_script)
126
132
  Bitcoin.verify_signature(hash, sig, pubkey.unpack("H*")[0])
@@ -129,7 +135,56 @@ If you want to control the Bitcoin::Script yourself, you can do so
129
135
 
130
136
  === Create Transactions
131
137
 
132
- TODO
138
+ You need to know the previous output you want to spend (tx hash and output index),
139
+ as well as the private key for the address required to sign for it.
140
+
141
+ # use testnet so you don't acidentially your whole money!
142
+ Bitcoin.network = :testnet3
143
+
144
+ # make the DSL methods available in your scope
145
+ include Bitcoin::Builder
146
+
147
+ # the previous transaction that has an output to your address
148
+ prev_hash = "6c44b284c20fa22bd69c57a9dbff91fb71deddc8c54fb2f5aa41fc78c96c1ad1"
149
+
150
+ # the number of the output you want to use
151
+ prev_out_index = 0
152
+
153
+ # fetch the tx from whereever you like and parse it
154
+ prev_tx = Bitcoin::P::Tx.from_json(open("http://test.webbtc.com/tx/#{prev_hash}.json"))
155
+
156
+ # the key needed to sign an input that spends the previous output
157
+ key = Bitcoin::Key.from_base58("92ZRu28m2GHSKaaF2W7RswJ2iJYpTzVhBaN6ZLs7TENCs4b7ML8")
158
+
159
+ # create a new transaction (and sign the inputs)
160
+ new_tx = build_tx do |t|
161
+
162
+ # add the input you picked out earlier
163
+ t.input do |i|
164
+ i.prev_out prev_tx
165
+ i.prev_out_index prev_out_index
166
+ i.signature_key key
167
+ end
168
+
169
+ # add an output that sends some bitcoins to another address
170
+ t.output do |o|
171
+ o.value 50000000 # 0.5 BTC in satoshis
172
+ o.script {|s| s.recipient "mugwYJ1sKyr8EDDgXtoh8sdDQuNWKYNf88" }
173
+ end
174
+
175
+ # add another output spending the remaining amount back to yourself
176
+ # if you want to pay a tx fee, reduce the value of this output accordingly
177
+ # if you want to keep your financial history private, use a different address
178
+ t.output do |o|
179
+ o.value 49000000 # 0.49 BTC, leave 0.01 BTC as fee
180
+ o.script {|s| s.recipient key.addr }
181
+ end
182
+
183
+ end
184
+
185
+ # examine your transaction. you can relay it through http://webbtc.com/relay_tx
186
+ # that will also give you a hint on the error if something goes wrong
187
+ puts new_tx.to_json
133
188
 
134
189
  === Node / Network connections
135
190
 
@@ -148,7 +203,7 @@ lib/bitcoin/network/node.rb for examples.
148
203
 
149
204
  === Storage / Database backends
150
205
 
151
- There is support for multiple database backends, but currently the only stable one is
206
+ There is support for multiple database backends, but currently the only stable one is
152
207
  the Bitcoin::Storage::Backends::SequelStore backend. All backends implement the interface
153
208
  defined in Bitcoin::Storage::Backends::StoreBase and return Bitcoin::Storage::Models.
154
209
 
@@ -159,7 +214,7 @@ defined in Bitcoin::Storage::Backends::StoreBase and return Bitcoin::Storage::Mo
159
214
  txouts.first.type #=> :pubkey
160
215
  txouts.first.get_address #=> "15yN7NPEpu82sHhB6TzCW5z5aXoamiKeGy"
161
216
 
162
- See Bitcoin::Storage::Backends::StoreBase, Bitcoin::Storage::Backends::SequelStore
217
+ See Bitcoin::Storage::Backends::StoreBase, Bitcoin::Storage::Backends::SequelStore
163
218
  and Bitcoin::Storage::Models for details.
164
219
 
165
220
  == Documentation
@@ -186,4 +241,9 @@ If you make changes to the code or add functionality, please also add specs.
186
241
 
187
242
  == Development
188
243
 
244
+ Any help or feedback is greatly appreciated! From getting knee-deep into elliptic-curve acrobatics,
245
+ to cleaning up high-level naming conventions, there is something for everyone to do.
246
+ Even if you are completely lost, just pointing out what is unclear helps a lot!
247
+
189
248
  If you are curious or like to participate in development, drop by \#bitcoin-ruby on irc.freenode.net!
249
+
data/Rakefile CHANGED
@@ -8,6 +8,10 @@ PROJECT_SPECS = ( FileList['spec/bitcoin/bitcoin_spec.rb'] +
8
8
  FileList['spec/bitcoin/protocol/*_spec.rb'] +
9
9
  FileList['spec/bitcoin/script/*_spec.rb'] +
10
10
  FileList['spec/bitcoin/wallet/*_spec.rb'] +
11
+ ['spec/bitcoin/storage/storage_spec.rb',
12
+ 'spec/bitcoin/storage/reorg_spec.rb',
13
+ 'spec/bitcoin/storage/validation_spec.rb'] +
14
+ FileList['spec/bitcoin/node/*_spec.rb'] +
11
15
  FileList['spec/bitcoin/*_spec.rb'] ).uniq
12
16
 
13
17
  RUBY = 'ruby' unless defined?(RUBY)
@@ -88,10 +92,9 @@ end
88
92
  desc 'Generate RDoc documentation'
89
93
  task :rdoc do
90
94
  `rm -rf rdoc`
91
- system("rdoc -o rdoc -m README.rdoc examples/ doc/ lib/ README.rdoc COPYING")
95
+ system("rdoc -a -A -H -t 'bitcoin-ruby RDoc' -W 'http://github.com/mhanne/bitcoin-ruby/tree/master/%s' -o rdoc -m README.rdoc examples/ doc/ lib/ README.rdoc COPYING")
92
96
  end
93
97
 
94
-
95
98
  desc 'Generate test coverage report'
96
99
  task :coverage do
97
100
  begin
@@ -100,5 +103,6 @@ task :coverage do
100
103
  puts "Simplecov not found. Run `gem install simplecov` to install it."
101
104
  exit
102
105
  end
103
- system "bacon #{PROJECT_SPECS.join(' ')}"
106
+ sh "bacon", *PROJECT_SPECS
107
+ system('open coverage/index.html') if RUBY_PLATFORM.include? 'darwin'
104
108
  end
data/bin/bitcoin_node CHANGED
@@ -3,38 +3,11 @@ $:.unshift( File.expand_path("../../lib", __FILE__) )
3
3
 
4
4
  require 'bitcoin'
5
5
  require 'optparse'
6
+ require 'fileutils'
6
7
  Bitcoin.require_dependency :eventmachine
7
8
  Bitcoin.require_dependency :json
8
9
 
9
- defaults = {
10
- :network => "bitcoin",
11
- :command => "127.0.0.1:9999",
12
- :listen => "0.0.0.0:8332",
13
- :connect => "",
14
- :storage => "sequel::sqlite://bitcoin.db",
15
- :headers_only => false,
16
- :dns => true,
17
- :epoll => false,
18
- :epoll_limit => 10000,
19
- :epoll_user => nil,
20
- :log => {
21
- :network => :info,
22
- :storage => :info,
23
- },
24
- :max => {
25
- :connections => 32,
26
- :addr => 1024,
27
- :queue => 500,
28
- :inv => 500,
29
- },
30
- :intervals => {
31
- :queue => 5,
32
- :inv_queue => 5,
33
- :addrs => 15,
34
- :connect => 30,
35
- },
36
- :daemon => false,
37
- }
10
+ defaults = Bitcoin::Network::Node::DEFAULT_CONFIG
38
11
 
39
12
  options = Bitcoin::Config.load(defaults, :blockchain)
40
13
 
@@ -54,18 +27,20 @@ optparse = OptionParser.new do |opts|
54
27
  end
55
28
 
56
29
  opts.on("--command [HOST:PORT]",
57
- "Command socket (default: #{options[:command]})") do |command|
58
- options[:command] = command
30
+ "Command socket (default: #{options[:command].join(':')})") do |command|
31
+ options[:command] = command.split(":")
59
32
  end
60
33
 
61
34
  opts.on("-l", "--listen [HOST:PORT]",
62
- "Listen address/port (default: #{options[:listen]})") do |listen|
63
- options[:listen] = listen
35
+ "Listen address/port (default: #{options[:listen].join(':')})") do |listen|
36
+ options[:listen] = listen.split(":")
64
37
  end
65
38
 
66
- opts.on("--connect [HOST:PORT[,HOST:PORT[...]]]",
67
- "Hosts to connect to (default: #{options[:connect]})") do |connect|
68
- options[:connect] = connect
39
+ opts.on("--connect [HOST:PORT]",
40
+ "Hosts to connect to (default: #{options[:connect].map{|c|c.join(':')}.join(',')})") do |connect|
41
+ connect.split(",").each do |host|
42
+ options[:connect] << host.split(":")
43
+ end
69
44
  end
70
45
 
71
46
  opts.on("-s", "--storage [BACKEND::CONFIG]",
@@ -73,6 +48,15 @@ optparse = OptionParser.new do |opts|
73
48
  options[:storage] = storage
74
49
  end
75
50
 
51
+ opts.on("--skip-validation", "Skip validation of blocks and transactions") do
52
+ options[:skip_validation] = true
53
+ end
54
+
55
+ opts.on("--check-blocks COUNT",
56
+ "Check consistency of COUNT latest blocks; -1 for all (default: 1000)") do |check|
57
+ options[:check_blocks] = check.to_i
58
+ end
59
+
76
60
  opts.on("--ho", "--headers-only",
77
61
  "Download only block headers") do
78
62
  options[:headers_only] = true
@@ -100,7 +84,15 @@ optparse = OptionParser.new do |opts|
100
84
  options[:epoll_user] = user
101
85
  end
102
86
 
103
- [:connections, :addr, :queue, :inv].each do |name|
87
+ opts.on("--mco", "--max-connections-out COUNT", "Maximum number of outgoing connections (default: #{options[:max][:connections_out]})") do |count|
88
+ options[:max][:connections_out] = count.to_i
89
+ end
90
+
91
+ opts.on("--mci", "--max-connections-in COUNT", "Maximum number of incoming connections (default: #{options[:max][:connections_in]})") do |count|
92
+ options[:max][:connections_in] = count.to_i
93
+ end
94
+
95
+ [:addr, :queue, :inv, :unconfirmed].each do |name|
104
96
  opts.on("--m#{name.to_s[0]}", "--max-#{name} [COUNT]",
105
97
  "Max #{name} (default: #{options[:max][name]})") do |count|
106
98
  options[:max][name] = count.to_i
@@ -121,6 +113,10 @@ optparse = OptionParser.new do |opts|
121
113
  end
122
114
  end
123
115
 
116
+ opts.on("--import DIR", "Import blk*.dat files from DIR") do |dir|
117
+ options[:import] = dir
118
+ end
119
+
124
120
  opts.on("-v", "--verbose", "Set all loggers to debug") do
125
121
  options[:log].each_key {|k| options[:log][k] = :debug }
126
122
  end
@@ -137,9 +133,7 @@ end
137
133
  optparse.parse!
138
134
 
139
135
  Bitcoin.network = options[:network]
140
- options[:command] = options[:command] == "" ? nil : options[:command].split(':')
141
- options[:listen] = options[:listen] == "" ? nil : options[:listen].split(':')
142
- options[:connect] = options[:connect] == "" ? [] : options[:connect].split(',').map{|h| h.split(':')}
136
+ FileUtils.mkdir_p File.join(ENV['HOME'], ".bitcoin-ruby/#{Bitcoin.network_name}")
143
137
 
144
138
  if ARGV.any?
145
139
  EM.run do
@@ -153,8 +147,11 @@ if ARGV.any?
153
147
  on_block do |block, depth|
154
148
  puts "block: #{block['hash']} (#{depth})"
155
149
  end
156
- on_tx do |tx|
157
- puts "tx: #{tx['hash']}"
150
+ on_tx do |tx, confirmations|
151
+ puts "tx(#{confirmations}): #{tx['hash']}"
152
+ end
153
+ on_output do |tx_hash, address, value, confirmations|
154
+ puts "output(#{confirmations}): tx #{tx_hash[0..8]}: #{address} received #{value.to_f / 1e8} BTC"
158
155
  end
159
156
  on_connection do |type, host|
160
157
  if type == "connected"
data/bin/bitcoin_shell CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  $:.unshift( File.expand_path("../../lib", __FILE__) )
2
3
  require 'bitcoin'
3
4
 
data/bin/bitcoin_wallet CHANGED
@@ -8,8 +8,8 @@ require 'yaml'
8
8
 
9
9
  defaults = {
10
10
  :network => "testnet",
11
- :storage => "dummy",
12
- :keystore => "simple::file=#{ENV['HOME']}/.bitcoin-ruby/keys.json",
11
+ :storage => nil,
12
+ :keystore => nil,
13
13
  :command => "127.0.0.1:9999"
14
14
  }
15
15
  options = Bitcoin::Config.load(defaults, :wallet)
@@ -55,7 +55,13 @@ optparse = OptionParser.new do |opts|
55
55
  " send <addr>:<amount>[,<addr>:<amount>...] [<fee>] - send transaction\n" +
56
56
  " new - generate new key and add to keystore\n" +
57
57
  " import <base58> - import key in base58 format\n" +
58
- " export <addr> - export key to base58 format\n"
58
+ " export <addr> - export key to base58 format\n" +
59
+ " name_list - list names in the wallet\n" +
60
+ " name_show <name> - display name information\n" +
61
+ " name_history <name> - display name history\n" +
62
+ " name_new <name> - reserve a name\n" +
63
+ " name_firstupdate <name> <rand> <value> - register a name\n" +
64
+ " name_update <name> <value> [<toaddress>] - update/transfer a name\n"
59
65
 
60
66
  end
61
67
 
@@ -67,6 +73,8 @@ unless cmd
67
73
  end
68
74
 
69
75
  Bitcoin.network = options[:network]
76
+
77
+ options[:keystore] ||= "simple::file=~/.bitcoin-ruby/#{Bitcoin.network_name}/keys.json"
70
78
  backend, config = options[:keystore].split("::")
71
79
  config = Hash[config.split(",").map{|c| c.split("=")}]
72
80
  keystore = Bitcoin::Wallet.const_get("#{backend.capitalize}KeyStore").new(config)
@@ -74,13 +82,13 @@ if backend == "deterministic" && !config["nonce"]
74
82
  puts "nonce: #{keystore.generator.nonce}"
75
83
  end
76
84
  #puts *keystore.get_keys.map(&:addr)
85
+
86
+ options[:storage] ||= "sequel::sqlite://~/.bitcoin-ruby/#{Bitcoin.network_name}/blocks.db"
77
87
  backend, config = options[:storage].split("::")
78
88
  storage = Bitcoin::Storage.send(backend, :db => config)
79
89
 
80
-
81
90
  wallet = Bitcoin::Wallet::Wallet.new(storage, keystore, Bitcoin::Wallet::SimpleCoinSelector)
82
91
 
83
-
84
92
  def str_val(val, pre='')
85
93
  ("#{pre}%.8f" % (val / 1e8)).rjust(15)
86
94
  end
@@ -89,6 +97,66 @@ def val_str(str)
89
97
  (str.to_f * 1e8).to_i
90
98
  end
91
99
 
100
+ def send_transaction(storage, options, tx, ask = true)
101
+ # puts tx.to_json
102
+ if ask
103
+ total = 0
104
+ puts "Hash: #{tx.hash}"
105
+ puts "inputs:"
106
+ tx.in.each do |txin|
107
+ prev_out = storage.get_txout_for_txin(txin)
108
+ total += prev_out.value
109
+ puts " #{prev_out.get_address} - #{str_val prev_out.value}"
110
+ end
111
+
112
+ puts "outputs:"
113
+ tx.out.each do |txout|
114
+ total -= txout.value
115
+ script = Bitcoin::Script.new(txout.pk_script)
116
+ print "#{str_val txout.value} "
117
+ if script.is_pubkey?
118
+ puts "#{script.get_pubkey} (pubkey)"
119
+ elsif script.is_hash160?
120
+ puts "#{script.get_address} (address)"
121
+ elsif script.is_multisig?
122
+ puts "#{script.get_addresses.join(' ')} (multisig)"
123
+ elsif script.is_namecoin?
124
+ puts "#{script.get_address} (#{script.type})"
125
+ print " " * 16
126
+ if script.is_name_new?
127
+ puts "Name Hash: #{script.get_namecoin_hash}"
128
+ else
129
+ puts "#{script.get_namecoin_name}: #{script.get_namecoin_value}"
130
+ end
131
+ else
132
+ puts "#{str_val txout.value} (unknown type)"
133
+ end
134
+ end
135
+ puts "Fee: #{str_val total}"
136
+
137
+ $stdout.sync = true
138
+ print "Really send transaction? (y/N) " and $stdout.flush
139
+ unless $stdin.gets.chomp.downcase == 'y'
140
+ puts "Aborted."; exit
141
+ end
142
+ end
143
+ EM.run do
144
+ Bitcoin::Network::CommandClient.connect(*options[:command].split(":")) do
145
+ on_connected do
146
+ request(:relay_tx, tx.to_payload.hth)
147
+ end
148
+ on_relay_tx do |res|
149
+ if res["success"]
150
+ puts "Transaction #{tx.hash} relayed to approx. #{"%.2f" % res['propagation']['percent']}% of the network."
151
+ else
152
+ puts "Error relaying tx: #{res['error']}"
153
+ end
154
+ EM.stop
155
+ end
156
+ end
157
+ end
158
+ end
159
+
92
160
  case cmd
93
161
  when "balance"
94
162
  if cmdopts && cmdopts.size == 1
@@ -135,15 +203,17 @@ when "key"
135
203
  puts "Privkey: #{key[:key].priv}" if ARGV[1] == '-p'
136
204
  puts "Mine: #{key[:mine]}"
137
205
 
138
-
139
206
  when "import"
140
207
  if wallet.keystore.respond_to?(:import)
141
- addr = wallet.keystore.import(cmdopts[0])
208
+ addr = wallet.import_key(cmdopts[0])
142
209
  puts "Key for #{addr} imported."
143
210
  else
144
211
  puts "Keystore doesn't support importing."
145
212
  end
146
213
 
214
+ when "rescan"
215
+ wallet.rescan
216
+
147
217
  when "export"
148
218
  base58 = wallet.keystore.export(cmdopts[0])
149
219
  puts "Base58 encoded private key for #{cmdopts[0]}:"
@@ -176,7 +246,12 @@ when "list"
176
246
  puts "#{tx.hash} | #{str_val txout.value, '- '} | " +
177
247
  "#{str_val total} | #{blocks}"
178
248
  txin.get_tx.out.each do |out|
179
- puts " -> #{out.get_addresses.join(', ')}"
249
+ if Bitcoin.namecoin? && out.type.to_s =~ /^name_/
250
+ script = out.script
251
+ puts " -> #{script.get_namecoin_name || script.get_namecoin_hash} (#{out.type})"
252
+ else
253
+ puts " -> #{out.get_addresses.join(', ') rescue 'unknown'}"
254
+ end
180
255
  end
181
256
  puts
182
257
  end
@@ -193,6 +268,48 @@ when "list"
193
268
  puts "Total balance: #{str_val wallet.get_balance}"
194
269
  end
195
270
 
271
+ when "name_list"
272
+ names = wallet.get_txouts.select {|o| [:name_firstupdate, :name_update].include?(o.type)}
273
+ .map(&:get_namecoin_name).group_by(&:name).map {|n, l| l.sort_by(&:expires_in).last }.map {|name|
274
+ { name: name.name, value: name.value, address: name.get_address, expires_in: name.expires_in } }
275
+ puts JSON.pretty_generate(names)
276
+
277
+ when "name_show"
278
+ name = storage.name_show(cmdopts[0])
279
+ puts name.to_json
280
+
281
+ when "name_history"
282
+ names = storage.name_history(cmdopts[0])
283
+ puts JSON.pretty_generate(names)
284
+
285
+ when "name_new"
286
+ name = cmdopts[0]
287
+ address = wallet.keystore.keys.sample[:key].addr
288
+ @rand = nil
289
+ def self.set_rand rand
290
+ @rand = rand
291
+ end
292
+ tx = wallet.new_tx([[:name_new, self, name, address, 1000000]])
293
+ (puts "Error creating tx."; exit) unless tx
294
+ send_transaction(storage, options, tx, true)
295
+ puts JSON.pretty_generate([tx.hash, @rand])
296
+
297
+ when "name_firstupdate"
298
+ name, rand, value = *cmdopts
299
+ address = wallet.keystore.keys.sample[:key].addr
300
+ tx = wallet.new_tx([[:name_firstupdate, name, rand, value, address, 1000000]])
301
+ (puts "Error creating tx."; exit) unless tx
302
+ send_transaction(storage, options, tx, true)
303
+ puts tx.hash
304
+
305
+ when "name_update"
306
+ name, value, address = *cmdopts
307
+ address ||= wallet.keystore.keys.sample[:key].addr
308
+ tx = wallet.new_tx([[:name_update, name, value, address, 1000000]])
309
+ (puts "Error creating tx."; exit) unless tx
310
+ send_transaction(storage, options, tx, true)
311
+ puts tx.hash
312
+
196
313
  when "send"
197
314
  to = cmdopts[0].split(',').map do |pair|
198
315
  type, *addrs, value = pair.split(":")
@@ -217,51 +334,10 @@ when "send"
217
334
  File.open(filename, "w") {|f| f.write(tx.serialize) }
218
335
  exit
219
336
  end
220
-
221
- unless tx
222
- puts "Error creating tx."; exit
223
- end
224
337
 
225
- total = 0
226
- puts "Hash: #{tx.hash}"
227
- puts "inputs:"
228
- tx.in.each do |txin|
229
- prev_out = storage.get_txout_for_txin(txin)
230
- total += prev_out.value
231
- puts " #{prev_out.get_address} - #{str_val prev_out.value}"
232
- end
233
-
234
- puts "outputs:"
235
- tx.out.each do |txout|
236
- total -= txout.value
237
- script = Bitcoin::Script.new(txout.pk_script)
238
- if script.is_pubkey?
239
- puts "#{str_val txout.value} #{script.get_pubkey} (pubkey)"
240
- elsif script.is_hash160?
241
- puts "#{str_val txout.value} #{script.get_address} (address)"
242
- elsif script.is_multisig?
243
- puts "#{str_val txout.value} #{script.get_addresses.join(' ')} (multisig)"
244
- end
245
- end
246
- puts "Fee: #{str_val total}"
247
-
248
- $stdout.sync = true
249
- print "Really send transaction? (y/N) " and $stdout.flush
250
- unless $stdin.gets.chomp.downcase == 'y'
251
- puts "Aborted."; exit
252
- end
338
+ (puts "Error creating tx."; exit) unless tx
253
339
 
254
- EM.run do
255
- Bitcoin::Network::CommandClient.connect(*options[:command].split(":")) do
256
- on_connected do
257
- request(:relay_tx, tx)
258
- end
259
- on_relay_tx do
260
- puts "Transaction #{tx.hash} relayed"
261
- EM.stop
262
- end
263
- end
264
- end
340
+ send_transaction(storage, options, tx)
265
341
 
266
342
  when "sign"
267
343
  txt = File.read(ARGV[0])
@@ -294,8 +370,8 @@ when "relay"
294
370
  end
295
371
  tx.in.each_with_index do |txin, i|
296
372
  p txdp.tx.map(&:hash)
297
- prev_tx = storage.get_tx(txin.prev_out.reverse.unpack("H*")[0])
298
- raise "prev tx #{txin.prev_out.reverse.unpack("H*")[0]} not found" unless prev_tx
373
+ prev_tx = storage.get_tx(txin.prev_out.reverse_hth)
374
+ raise "prev tx #{txin.prev_out.reverse_hth} not found" unless prev_tx
299
375
  raise "signature error" unless tx.verify_input_signature(i, prev_tx)
300
376
  end
301
377