bitcoin-ruby 0.0.1

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