bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ .rvmrc
7
+ *.DS_Store
8
+ db/database.yml
9
+ tmp/
10
+ log/
11
+ rdoc/
12
+ coverage/
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2011 Julian Langschaedel <meta.rb@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in bitcoin-ruby.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,189 @@
1
+ = Bitcoin-ruby
2
+
3
+ This is a ruby library for interacting with the bitcoin protocol/network.
4
+
5
+ Some of the main features are:
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
16
+
17
+ == Installation
18
+
19
+ We assume you already have a ruby 1.9 compatible interpreter and rubygems environment.
20
+
21
+ git clone https://github.com/lian/bitcoin-ruby.git; cd bitcoin-ruby
22
+ ruby -Ilib bin/bitcoin_node
23
+
24
+ if you want to install it system-wide, just build the gem and install it
25
+
26
+ gem build bitcoin-ruby.gemspec && gem install bitcoin-ruby-0.0.1.gem
27
+
28
+ now you can just call +bitcoin_node+ from anywhere.
29
+
30
+
31
+ Note that some aspects of the library (such as networking, storage, etc.) need
32
+ additional dependencies which are not specified in the gemspec. The core requirements are
33
+ intentionally kept to a minimum, so nobody has to install unneeded dependencies.
34
+
35
+ * +eventmachine+ to run a node / connect to peers
36
+ * +sequel+, +sqlite3+/+pg+/+mysql+ to use a storage backend
37
+ * +em-dns+ or +nslookup+ to get peer addrs from DNS seeds
38
+ * +gir_ffi+ for the gui
39
+ * +bacon+ to run the specs
40
+
41
+ == Client
42
+
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.
49
+
50
+
51
+ There is a WALLET implementation to manage a set of keys, list balances and create
52
+ transactions. see Bitcoin::Wallet
53
+
54
+
55
+ == Library Usage
56
+
57
+ 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,
59
+ see EXAMPLES.
60
+
61
+
62
+ === Keys/Addresses
63
+
64
+ Generate a Bitcoin::Key
65
+
66
+ key = Bitcoin::generate_key
67
+ key #=> [<privkey>, <pubkey>]
68
+
69
+ Get the address from a public key
70
+
71
+ address = Bitcoin::pubkey_to_address(key[1])
72
+ address #=> <bitcoin address>
73
+
74
+ Check if an address is valid
75
+
76
+ Bitcoin::valid_address?(address) #=> true
77
+
78
+
79
+ === Blocks / Transactions parsing
80
+
81
+ Parse a Bitcoin::Protocol::Block
82
+
83
+ raw_block = File.open('spec/bitcoin/fixtures/rawblock-0.bin', 'rb') {|f| f.read}
84
+ blk = Bitcoin::Protocol::Block.new(raw_block)
85
+ blk.hash #=> 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048
86
+ blk.tx.count #=> 1
87
+ blk.to_hash #=> ...
88
+ Bitcoin::Protocol::Block.from_json( blk.to_json )
89
+
90
+ Parse a Bitcoin::Protocol::Tx
91
+
92
+ raw_tx = File.open('spec/bitcoin/fixtures/rawtx-01.bin', 'rb') {|f| f.read}
93
+ tx = Bitcoin::Protocol::Tx.new(raw_tx)
94
+ tx.hash #=> 6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4
95
+ tx.in.size #=> 1
96
+ tx.out.size #=> 2
97
+ tx.to_hash #=> ...
98
+ Bitcoin::Protocol::Tx.from_json( tx.to_json )
99
+
100
+ Bitcoin::Script.new(tx.out[0].pk_script).to_string
101
+ #=> "OP_DUP OP_HASH160 b2e21c1db922e3bdc529de7b38b4c401399e9afd OP_EQUALVERIFY OP_CHECKSIG"
102
+
103
+ === Transaction verification / Scripts
104
+
105
+ Get the matching transactions (in this example tx1 is the spending transaction)
106
+
107
+ rawtx1 = File.open("spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin", 'rb') {|f| f.read}
108
+ rawtx2 = File.open("spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin", 'rb') {|f| f.read}
109
+ tx1 = Bitcoin::Protocol::Tx.new(rawtx1)
110
+ tx2 = Bitcoin::Protocol::Tx.new(rawtx2)
111
+
112
+ Then simply ask the transaction to verify an input
113
+
114
+ tx1.verify_input_signature(0, tx2) #=> true
115
+
116
+ === Scripts
117
+
118
+ If you want to control the Bitcoin::Script yourself, you can do so
119
+
120
+ txin = tx1.in.first
121
+ txout = tx2.out[txin.prev_out_index]
122
+ script = Bitcoin::Script.new(txin.script_sig + txout.pk_script)
123
+
124
+ result = script.run do |pubkey, sig, hash_type|
125
+ hash = tx1.signature_hash_for_input(0, nil, txout.pk_script)
126
+ Bitcoin.verify_signature(hash, sig, pubkey.unpack("H*")[0])
127
+ end
128
+ result #=> true
129
+
130
+ === Create Transactions
131
+
132
+ TODO
133
+
134
+ === Node / Network connections
135
+
136
+ The Bitcoin::Network::Node can connect to peers and download the blockchain into a
137
+ Bitcoin::Storage backend. For now it works completely self-contained:
138
+
139
+ node = Bitcoin::Network::Node.new(options) # options = {:network => :bitcoin, ...}
140
+ node.run
141
+
142
+ In the future you will be able to register callbacks to the node and control many aspects
143
+ of its operation yourself. Also see NODE.
144
+
145
+ If you want to implement your own node, see lib/bitcoin/connection.rb or
146
+ lib/bitcoin/network/node.rb for examples.
147
+
148
+
149
+ === Storage / Database backends
150
+
151
+ There is support for multiple database backends, but currently the only stable one is
152
+ the Bitcoin::Storage::Backends::SequelStore backend. All backends implement the interface
153
+ defined in Bitcoin::Storage::Backends::StoreBase and return Bitcoin::Storage::Models.
154
+
155
+ store = Bitcoin::Storage.sequel(:db => "sqlite://bitcoin.db") # in-memory db
156
+ store.get_head #=> block
157
+ txouts = store.get_txouts_for_address("15yN7NPEpu82sHhB6TzCW5z5aXoamiKeGy")
158
+ txouts.first.value #=> 5000000000
159
+ txouts.first.type #=> :pubkey
160
+ txouts.first.get_address #=> "15yN7NPEpu82sHhB6TzCW5z5aXoamiKeGy"
161
+
162
+ See Bitcoin::Storage::Backends::StoreBase, Bitcoin::Storage::Backends::SequelStore
163
+ and Bitcoin::Storage::Models for details.
164
+
165
+ == Documentation
166
+
167
+ Always trying to improve, any help appreciated! If anything is unclear to you, let us know!
168
+
169
+ Documentation is generated using RDoc
170
+
171
+ rake rdoc
172
+
173
+ The specs are also a good place to see how something works.
174
+
175
+ == Specs
176
+
177
+ The specs can be run with
178
+
179
+ rake bacon
180
+
181
+ or, if you want to run a single spec
182
+
183
+ ruby spec/bitcoin/bitcoin_spec.rb
184
+
185
+ If you make changes to the code or add functionality, please also add specs.
186
+
187
+ == Development
188
+
189
+ If you are curious or like to participate in development, drop by \#bitcoin-ruby on irc.freenode.net!
data/Rakefile ADDED
@@ -0,0 +1,104 @@
1
+ begin
2
+ require 'bundler/gem_tasks'
3
+ rescue LoadError
4
+ end
5
+
6
+
7
+ PROJECT_SPECS = ( FileList['spec/bitcoin/bitcoin_spec.rb'] +
8
+ FileList['spec/bitcoin/protocol/*_spec.rb'] +
9
+ FileList['spec/bitcoin/script/*_spec.rb'] +
10
+ FileList['spec/bitcoin/wallet/*_spec.rb'] +
11
+ FileList['spec/bitcoin/*_spec.rb'] ).uniq
12
+
13
+ RUBY = 'ruby' unless defined?(RUBY)
14
+
15
+ task :default => :bacon
16
+ #
17
+ # test runner
18
+ #
19
+ desc 'Run all bacon specs with pretty output'
20
+ task :bacon do
21
+ require 'open3'
22
+ require 'scanf'
23
+ require 'matrix'
24
+
25
+ specs = PROJECT_SPECS
26
+ #specs.delete_if{|i| File.basename(i) == 'storage_spec.rb' } # skip for now
27
+
28
+ some_failed = false
29
+ specs_size = specs.size
30
+ len = specs.map{|s| s.size }.sort.last
31
+ total_tests = total_assertions = total_failures = total_errors = 0
32
+ totals = Vector[0, 0, 0, 0]
33
+
34
+ red, yellow, green = "\e[31m%s\e[0m", "\e[33m%s\e[0m", "\e[32m%s\e[0m"
35
+ left_format = "%4d/%d: %-#{len + 11}s"
36
+ spec_format = "%d specifications (%d requirements), %d failures, %d errors"
37
+
38
+ specs.each_with_index do |spec, idx|
39
+ print(left_format % [idx + 1, specs_size, spec])
40
+
41
+ Open3.popen3(RUBY, spec) do |sin, sout, serr|
42
+ out = sout.read.strip
43
+ err = serr.read.strip
44
+
45
+ # this is conventional, see spec/innate/state/fiber.rb for usage
46
+ if out =~ /^Bacon::Error: (needed .*)/
47
+ puts(yellow % ("%6s %s" % ['', $1]))
48
+ else
49
+ total = nil
50
+
51
+ out.each_line do |line|
52
+ scanned = line.scanf(spec_format)
53
+
54
+ next unless scanned.size == 4
55
+
56
+ total = Vector[*scanned]
57
+ break
58
+ end
59
+
60
+ if total
61
+ totals += total
62
+ tests, assertions, failures, errors = total_array = total.to_a
63
+
64
+ if tests > 0 && failures + errors == 0
65
+ puts((green % "%6d passed") % tests)
66
+ else
67
+ some_failed = true
68
+ puts(red % " failed")
69
+ puts out unless out.empty?
70
+ puts err unless err.empty?
71
+ end
72
+ else
73
+ some_failed = true
74
+ puts(red % " failed")
75
+ puts out unless out.empty?
76
+ puts err unless err.empty?
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ total_color = some_failed ? red : green
83
+ puts(total_color % (spec_format % totals.to_a))
84
+ exit 1 if some_failed
85
+ end
86
+
87
+
88
+ desc 'Generate RDoc documentation'
89
+ task :rdoc do
90
+ `rm -rf rdoc`
91
+ system("rdoc -o rdoc -m README.rdoc examples/ doc/ lib/ README.rdoc COPYING")
92
+ end
93
+
94
+
95
+ desc 'Generate test coverage report'
96
+ task :coverage do
97
+ begin
98
+ require 'simplecov'
99
+ rescue LoadError
100
+ puts "Simplecov not found. Run `gem install simplecov` to install it."
101
+ exit
102
+ end
103
+ system "bacon #{PROJECT_SPECS.join(' ')}"
104
+ end
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
3
+
4
+ require 'bitcoin'
5
+ require 'socket'
6
+ require 'json'
7
+ require 'rubydns'
8
+ require 'optparse'
9
+
10
+ Bitcoin::Network::Node # autoload to include Array#weighted_sample
11
+
12
+ DNS_OPTS = {
13
+ :name => nil,
14
+ :connect => "127.0.0.1:9999",
15
+ :port => 8333,
16
+ :cache => 1024,
17
+ :send => 16,
18
+ :interval => 10,
19
+ :ttl => nil,
20
+ :hosts => nil,
21
+ }
22
+ optparse = OptionParser.new do|opts|
23
+ opts.banner = "Usage: bitcoin_dns_seed [options] <domain>"
24
+
25
+ opts.on("-c", "--connect [HOST:PORT]",
26
+ "Connect to server (default: 127.0.0.1:9999)") do |connect|
27
+ DNS_OPTS[:connect] = connect
28
+ end
29
+
30
+ opts.on("-p", "--port [PORT]",
31
+ "Network default port each addr must match (default: 8333)") do |port|
32
+ DNS_OPTS[:port] = port.to_i
33
+ end
34
+
35
+ opts.on("-c", "--cache [SIZE]", "Cache addresses (default: 1024)") do |size|
36
+ DNS_OPTS[:cache] = size.to_i
37
+ end
38
+
39
+ opts.on("-s", "--send [SIZE]", "Send addresses (default: 16)") do |size|
40
+ DNS_OPTS[:send] = size.to_i
41
+ end
42
+
43
+ opts.on("-i", "--interval [SECONDS]", "Refresh addresses (default: 10)") do |s|
44
+ DNS_OPTS[:interval] = s.to_i
45
+ end
46
+
47
+ opts.on("-t", "--ttl [SECONDS]", "TTL to set on served records (default: relative to addr age)") do |t|
48
+ DNS_OPTS[:ttl] = t.to_i
49
+ end
50
+
51
+ opts.on("--hosts [HOSTS]", "Additional hosts file with name => addr mappings") do |h|
52
+ DNS_OPTS[:hosts] = h
53
+ end
54
+
55
+ opts.on( '-h', '--help', 'Display this screen' ) do
56
+ puts opts
57
+ exit
58
+ end
59
+ end
60
+ optparse.parse!
61
+
62
+ DNS_OPTS[:domain] = ARGV.shift
63
+ unless DNS_OPTS[:domain]
64
+ puts optparse
65
+ exit
66
+ end
67
+
68
+
69
+ class RubyDNS::Server
70
+ attr_accessor :addrs
71
+ end
72
+
73
+ class AddrFetcher < EM::Connection
74
+ def initialize(dns)
75
+ @dns = dns
76
+ @dns.addrs ||= []
77
+ @buf = BufferedTokenizer.new("\x00")
78
+ send_data(["addrs", DNS_OPTS[:cache].to_s].to_json)
79
+ end
80
+
81
+ def receive_data(data)
82
+ @buf.extract(data).each do |packet|
83
+ json = JSON.load(packet)[1]
84
+ @dns.logger.info { "received #{json.size} new addrs" }
85
+ @dns.addrs += json.select{|a| a[1] == DNS_OPTS[:port]}
86
+ @dns.addrs.uniq! {|a| a[0]}
87
+ pop = @dns.addrs.size - DNS_OPTS[:cache]
88
+ @dns.addrs.pop(pop) if pop > 0
89
+ @dns.logger.debug { "addr pool size now #{@dns.addrs.size}" }
90
+ close_connection
91
+ end
92
+ end
93
+
94
+ def self.run(host, port, dns, interval = 30)
95
+ EM.connect(host, port, self, dns)
96
+ EM.add_periodic_timer(interval) do
97
+ EM.connect(host, port, self, dns)
98
+ end
99
+ end
100
+ end
101
+
102
+ dns = RubyDNS::Server.new
103
+ dns.match(DNS_OPTS[:domain], :A) do |transaction|
104
+ addrs = dns.addrs.weighted_sample(DNS_OPTS[:send]) {|addr| 10800 - addr[2] }.uniq
105
+ addrs.each do |addr|
106
+ begin
107
+ ttl = DNS_OPTS[:ttl] || ((10800 - addr[2]) / 30)
108
+ transaction.respond!(addr[0], :ttl => ttl)
109
+ rescue
110
+ # ignore faulty addrs ("localhost" etc)
111
+ end
112
+ end
113
+ end
114
+
115
+ if DNS_OPTS[:hosts]
116
+ dns.logger.info { "serving additional addrs from file" }
117
+ File.read(DNS_OPTS[:hosts]).each_line do |line|
118
+ dns.logger.debug { line.strip }
119
+ addr, *names = line.split(/\s+/)
120
+ names.each {|n| dns.match(n, :A) {|t| t.respond!(addr) } }
121
+ end
122
+ end
123
+
124
+ EM.run do
125
+ EventMachine.open_datagram_socket("0.0.0.0", 53, RubyDNS::UDPHandler, dns)
126
+ EventMachine.start_server("0.0.0.0", 53, RubyDNS::TCPHandler, dns)
127
+ dns.logger.info { "DNS Server listening on 0.0.0.0:53" }
128
+
129
+ timer = AddrFetcher.run(*DNS_OPTS[:connect].split(":"), dns, DNS_OPTS[:interval])
130
+ end