bitcoin-ruby 0.0.1 → 0.0.2

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 (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