Commit 111e966ec0a52c742fa0dfcd7df1d745b89603dc

Authored by Marius Hanne
1 parent 67b6e2db0d

documentation

Showing 38 changed files with 999 additions and 795 deletions Side-by-side Diff

1   -= Bitcoin-ruby
2   -
3   -This is a ruby library for interacting with the bitcoin protocol/network.
4   -It can parse and generate protocol messages, run basic scripts,
5   -connect to other peers and download and store the blockchain (WITHOUT verification yet!).
6   -
7   -
8   -== Installation
9   -
10   -We assume you already have a ruby 1.9 compatible interpreter and rubygems environment.
11   -
12   - git clone https://github.com/lian/bitcoin-ruby.git; cd bitcoin-ruby
13   - ruby -Ilib bin/bitcoin_connect
14   -
15   -
16   -Note that some aspects of the library (such as networking, or storage) may need
17   -additional dependencies which are not specified in the gemspec.
18   -You'll find out what's missing once it fails when you try to use it ;)
19   -
20   -
21   -== Demos
22   -
23   -There are a few demo scripts in bin/ you can run to try things out:
24   -
25   -=== bitcoin_connect
26   -
27   -Connect to a network node and chat a little.
28   -
29   -=== bitcoin_blockchain
30   -
31   -Connect to a node and download the whole blockchain into storage.
32   -
33   - bitcoin_blockchain # connects to testnet using dummy store
34   - bitcoin_blockchain -n bitcoin -s sequel::postgres///bitcoin
35   - bitcoin_blockchain -h
36   -
37   -=== bitcoin_verify_tx
38   -
39   -Fetch a transaction and all its prev_outs from local storage
40   -and verify the signatures.
41   -
42   - bitcoin_verify_tx [options] <tx_hash>
43   - bitcoin_verify_tx -s sequel::postgres:/bitcoin 0f6741210a02e196ca5f5ad17f684968623546c1accdbcb701a668a51a7ba9fd
44   -
45   -Note: For this to work, you obviously need to have the transactions in your storage.
46   -
47   -=== bbe_verify_tx
48   -
49   -In case you don't have a local storage, this script fetches a transaction and
50   -all its prev_outs from blockexplorer.com and verifies the signatures.
51   -
52   - bbe_verify_tx <tx_hash> [testnet]
53   -
54   -=== bitcoin_balance
55   -
56   -Collect transactions and display balance for an address.
57   -
58   - bitcoin_balance -s sequel::postgres:/bitcoin -l moz14kFmgHPszRvS6rvhfEVYmx4RbcNMfH
59   -
60   -=== bitcoin_wallet
61   -
62   -To use the wallet you should create a configfile. It's not required but more convenient.
63   -See the +Config+ +Files+ section for details.
64   -
65   - all:
66   - network: bitcoin
67   - storage: "sequel::postgres:/bitcoin"
68   - command: "127.0.01:9999"
69   - wallet:
70   - keystore: "simple::file=keys.json"
71   - # keystore: "deterministic::seed=foo,nonce=2116,keys=7"
72   -
73   -The +simple+ keystore will create one key and put it into the given file.
74   -You can later ask it to generate more keys.
75   -
76   -The +deterministic+ keystore uses the seed (which should be a large, unique, random string!)
77   -and generates the given number of keys. The nonce is optional, it just speeds up finding the first key.
78   -See Bitcoin::Wallet::KeyGenerator for details.
79   -
80   -Then you can list all addresses in the wallet:
81   -
82   - bitcoin_wallet list
83   -
84   -Display transaction history for a specific address:
85   -
86   - bitcoin_wallet list <address>
87   -
88   -Create new keys/addresses (only with +simple+ keystore; with +deterministic+ you have to edit your config and increase the number of keys):
89   -
90   - bitcoin_wallet new
91   -
92   -Import keys from base58 format:
93   -
94   - bitcoin_wallet import <base58>
95   -
96   -Export keys to base58 format:
97   -
98   - bitcoin_wallet export <address>
99   -
100   -And finally send bitcoins to an address:
101   -
102   - bitcoin_wallet send <type>:<address>:<amount> [<fee>]
103   - bitcoin_wallet send address:1GQkkFvAFW2ts3YLnEvMnu76WyCB6yDb4d:0.1 0.005
104   -
105   -This will create a transaction and display it, asking for confirmation.
106   -If you accept, it will be sent to your node and relayed to the rest of the network.
107   -
108   -There's also experimental multisig support, but you need to have all keys in your wallet for now:
109   -
110   - bitcoin_wallet send <type>:<m>:<key>:<key>:<amount> [<fee>]
111   - bitcoin_wallet send multisig:1:1GQkkFvAFW2ts3YLnEvMnu76WyCB6yDb4d:1C5uWeXorS46ZubciCLN4zyR7sAqeNJfLD:1.0 0.005
112   -
113   -=== bitcoin_shell
114   -
115   -Launches an irb session with bitcoin loaded where you can try stuff out.
116   -
117   -=== Storage Backends
118   -
119   -For examples that require storage backends, you can specify them using
120   -a string containing the backend name and a configuration string.
121   -The default backend is always +dummy+ which needs no configuration
122   -
123   - dummy
124   - sequel::sqlite:///tmp/bitcoin.db
125   - sequel::postgres:/bitcoin
126   - sequel::postgres://user:pass@host/database
127   -
128   -
129   -=== Config Files
130   -
131   -Some of the commands look for a config file in default locations, and provide
132   -an option to specify a custom location. The default locations are:
133   -
134   -* /etc/bitcoin-ruby.yml
135   -* ~/.bitcoin-ruby.yml
136   -* ./bitcoin-ruby.yml
137   -
138   -Files are loaded in order (if they exist) and may override each others settings.
139   -
140   -Inside a config file, you can put options for the various commands, separated
141   -into categories.
142   -
143   - all:
144   - network: bitcoin
145   - storage: sequel::postgres:/bitcoin
146   - command: 127.0.0.1:1234
147   - blockchain:
148   - max:
149   - connect: 30
150   - wallet:
151   - keystore: "simple::file=keys.json"
152   -
153   -Options in the +all+ category are loaded by every command, and are loaded first
154   -(so command-specific options will override them).
155   -
156   -Other categories are loaded by the corresponding command and may override options
157   -from the +all+ category (ie. +bitcoin_wallet+ loads +all+ and +wallet+).
158   -
159   -
160   -== Library Usage
161   -
162   -There are different aspects to the library which can be used separately or in combination.
163   -Here are some examples of what you could do.
164   -
165   -
166   -=== Keys / Addresses
167   -
168   -Generate a key:
169   -
170   - key = Bitcoin::generate_key
171   - key #=> [<privkey>, <pubkey>]
172   -
173   -Get the address from a public key
174   -
175   - address = Bitcoin::pubkey_to_address(key[1])
176   - address #=> <bitcoin address>
177   -
178   -Check if an address is valid
179   -
180   - Bitcoin::valid_address?(address) #=> true
181   -
182   -
183   -=== Blocks / Transactions parsing
184   -
185   -Parse a block
186   -
187   - raw_block = File.open('spec/bitcoin/fixtures/rawblock-0.bin', 'rb') {|f| f.read}
188   - blk = Bitcoin::Protocol::Block.new(raw_block)
189   - blk.hash #=> 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048
190   - blk.tx.count #=> 1
191   - blk.to_hash #=> ...
192   - Bitcoin::Protocol::Block.from_json( blk.to_json )
193   -
194   -Parse a transaction
195   -
196   - raw_tx = File.open('spec/bitcoin/fixtures/rawtx-01.bin', 'rb') {|f| f.read}
197   - tx = Bitcoin::Protocol::Tx.new(raw_tx)
198   - tx.hash #=> 6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4
199   - tx.in.size #=> 1
200   - tx.out.size #=> 2
201   - tx.to_hash #=> ...
202   - Bitcoin::Protocol::Tx.from_json( tx.to_json )
203   -
204   - Bitcoin::Script.new(tx.out[0].pk_script).to_string
205   - #=> "OP_DUP OP_HASH160 b2e21c1db922e3bdc529de7b38b4c401399e9afd OP_EQUALVERIFY OP_CHECKSIG"
206   -
207   -=== Transaction verification / Scripts
208   -
209   -Get the matching transactions (in this example tx1 is the spending transaction)
210   -
211   - rawtx1 = File.open("spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin", 'rb') {|f| f.read}
212   - rawtx2 = File.open("spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin", 'rb') {|f| f.read}
213   - tx1 = Bitcoin::Protocol::Tx.new(rawtx1)
214   - tx2 = Bitcoin::Protocol::Tx.new(rawtx2)
215   -
216   -Then simply ask the transaction to verify an input
217   -
218   - tx1.verify_input_signature(0, tx2) #=> true
219   -
220   -Or, if you want to control the script yourself
221   -
222   - txin = tx1.in.first
223   - txout = tx2.out[txin.prev_out_index]
224   - script = Bitcoin::Script.new(txin.script_sig + txout.pk_script)
225   -
226   - result = script.run do |pubkey, sig, hash_type|
227   - hash = tx1.signature_hash_for_input(0, nil, txout.pk_script)
228   - Bitcoin.verify_signature(hash, sig, pubkey.unpack("H*")[0])
229   - end
230   - result #=> true
231   -
232   -=== Node / Network connections
233   -
234   -The networking part is still work in progress. For now, you'll probably want to roll
235   -your own; see connection.rb or network/node.rb for examples.
236   -
237   -
238   -=== Storage / Database backends
239   -
240   -There are multiple database backends available, currently an in-memory dummy and
241   -and activerecord/postgres backend.
242   -
243   - store = Bitcoin::Storage.dummy # or:
244   - store = Bitcoin::Storage.sequel(:db => "postgres:/bitcoin")
245   - store.store_block(Bitcoin.network[:genesis_block])
246   - store.get_block(Bitcoin.network[:genesis_hash])
247   - store.get_head #=> Bitcoin.network[:genesis_hash]
248   -
249   -== Documentation
250   -
251   -Still needs some love, but you can generate RDoc documentation
252   -
253   - rake rdoc
254   -
255   -The specs are also a good place to see how something works.
256   -
257   -== Specs
258   -
259   -The specs can be run with
260   -
261   - rake bacon
262   -
263   -or, if you want to run a single spec
264   -
265   - ruby spec/bitcoin/bitcoin_spec.rb
266   -
267   -If you make changes to the code or add functionality, please also add specs.
268   -
269   -== Development
270   -
271   -If you are curious or like to participate in development, drop by \#bitcoin-ruby on irc.freenode.net!
  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::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 +* +ruby-gnome2+, +glib2+ and +pango+ 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!
... ... @@ -86,7 +86,7 @@
86 86 desc 'Generate RDoc documentation'
87 87 task :rdoc do
88 88 `rm -rf rdoc`
89   - system("rdoc -o rdoc -m README lib/ README COPYING")
  89 + system("rdoc -o rdoc -m README.rdoc examples/ doc/ lib/ README.rdoc COPYING")
90 90 end
91 91  
92 92  
bin/bbe_verify_tx
1   -#!/usr/bin/env ruby
2   -$:.unshift(File.dirname(__FILE__) + "/../lib")
3   -
4   -require 'bitcoin'
5   -require 'open-uri'
6   -
7   -tx_hash = ARGV[0]
8   -$testnet = ARGV[1]
9   -
10   -def get_tx(hash)
11   - url = "http://blockexplorer.com/%srawtx/%s" % [$testnet ? 'testnet/' : '', hash]
12   - #puts "fetching %s .." % [url]
13   - json = open(url).read
14   - Bitcoin::Protocol::Tx.from_json(json)
15   -rescue Exception
16   - nil
17   -end
18   -
19   -tx1 = get_tx(tx_hash)
20   -
21   -unless tx1
22   - puts "Tx #{tx_hash} not found."
23   - exit
24   -end
25   -
26   -if tx1.in.all?{|txin| txin.coinbase? }
27   - puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
28   - exit
29   -end
30   -
31   -tx1.in.each_with_index do |txin, idx|
32   - if txin.coinbase?
33   - puts "skipping coinbase transaction input.."; next
34   - end
35   -
36   - prev_tx = get_tx(txin.previous_output)
37   - unless prev_tx
38   - puts "Missing prev_out tx for input #{idx} of tx #{tx_hash}!"
39   - exit
40   - end
41   -
42   - result = tx1.verify_input_signature(idx, prev_tx)
43   - unless result
44   - puts "Input #{idx} of tx #{tx_hash} is invalid!"
45   - exit
46   - end
47   -end
48   -
49   -puts "Tx #{tx_hash} is valid."
bin/bitcoin_balance
1   -#!/usr/bin/env ruby
2   -$:.unshift( File.expand_path("../../lib", __FILE__) )
3   -
4   -require 'bitcoin'
5   -require 'optparse'
6   -
7   -defaults = {
8   - :network => "testnet",
9   - :storage => "dummy",
10   - :list => false,
11   -}
12   -
13   -options = Bitcoin::Config.load(defaults, :balance)
14   -
15   -optparse = OptionParser.new do|opts|
16   - opts.banner = "Usage: bitcoin_balance [options] <address>"
17   -
18   - opts.on("-c", "--config [FILE]",
19   - "Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
20   - options = Bitcoin::Config.load_file(options, file, :wallet)
21   - end
22   -
23   - opts.on("-n", "--network [NETWORK]", "User Network (default: testnet)") do |network|
24   - options[:network] = network
25   - end
26   -
27   - opts.on("-s", "--storage [BACKEND::CONFIG]", "Use storage backend (default: 'dummy')") do |storage|
28   - options[:storage] = storage
29   - end
30   -
31   - opts.on("-l", "--list", "List transactions") do |list|
32   - options[:list] = true
33   - end
34   -
35   - opts.on( '-h', '--help', 'Display this screen' ) do
36   - puts opts
37   - exit
38   - end
39   -end
40   -optparse.parse!
41   -
42   -Bitcoin.network = options[:network]
43   -puts "Using network #{options[:network]}"
44   -backend, config = options[:storage].split('::')
45   -store = Bitcoin::Storage.send(backend, :db => config)
46   -puts "Using #{backend} store #{config}"
47   -
48   -address = ARGV.shift
49   -
50   -unless Bitcoin.valid_address?(address)
51   - puts "Address #{address} is invalid."
52   - exit 1
53   -end
54   -
55   -def txout_address(txout)
56   - pk_script = txout.pk_script
57   - if pk_script[0...3] == ["76a914"].pack("H*") &&
58   - pk_script[-2..-1] == ["88ac"].pack("H*")
59   - hash = pk_script[3..-3].unpack("H*")[0]
60   - return Bitcoin.hash160_to_address(hash)
61   - end
62   -end
63   -
64   -script = Bitcoin::Script.to_address_script(address)
65   -txouts = store.get_txouts_for_pk_script(script)
66   -unless txouts.any?
67   - puts "Address not seen."
68   - exit
69   -end
70   -
71   -def str_val(val, pre = "")
72   - ("#{pre}#{"%.8f" % (val / 1e8)}").rjust(20)
73   -end
74   -
75   -if options[:list]
76   - total = 0
77   - txouts.each do |txout|
78   - tx = txout.get_tx
79   - total += txout.value
80   - puts "#{tx.hash} |#{str_val(txout.value, '+ ')} |=> #{str_val(total)}"
81   -
82   - txout.get_tx.in.map(&:get_prev_out).each do |prev_out|
83   - puts " <- #{txout_address(prev_out)}"
84   - end
85   - puts
86   -
87   - if txin = txout.get_next_in
88   - tx = txin.get_tx
89   - total -= txout.value
90   - puts "#{tx.hash} |#{str_val(txout.value, '- ')} |=> #{str_val(total)}"
91   - txin.get_tx.out.each do |out|
92   - puts " -> #{txout_address(out)}"
93   - end
94   - puts
95   - end
96   - end
97   -end
98   -
99   -balance = store.get_balance(address)
100   -puts "Balance: %.8f" % (balance / 1e8)
bin/bitcoin_blockchain
1   -#!/usr/bin/env ruby
2   -$:.unshift( File.expand_path("../../lib", __FILE__) )
3   -
4   -require 'bitcoin'
5   -require 'optparse'
6   -
7   -defaults = {
8   - :network => "testnet",
9   - :command => "127.0.0.1:9999",
10   - :listen => "0.0.0.0:8332",
11   - :connect => "",
12   - :storage => "dummy",
13   - :headers_only => false,
14   - :dns => true,
15   - :epoll => false,
16   - :epoll_limit => 10000,
17   - :epoll_user => nil,
18   - :log => {
19   - :network => :info,
20   - :storage => :info,
21   - },
22   - :max => {
23   - :connections => 32,
24   - :addr => 1024,
25   - :queue => 512,
26   - :inv => 512,
27   - },
28   - :intervals => {
29   - :queue => 5,
30   - :inv_queue => 5,
31   - :addrs => 5,
32   - :connect => 15,
33   - },
34   -}
35   -
36   -options = Bitcoin::Config.load(defaults, :blockchain)
37   -
38   -optparse = OptionParser.new do |opts|
39   - opts.banner = "Usage: bitcoin_node [options]"
40   -
41   - opts.separator("\nAvailable options:\n")
42   -
43   - opts.on("-c", "--config FILE",
44   - "Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
45   - options = Bitcoin::Config.load_file(options, file, :blockchain)
46   - end
47   -
48   - opts.on("-n", "--network [NETWORK]",
49   - "User Network (default: #{options[:network]})") do |network|
50   - options[:network] = network
51   - end
52   -
53   - opts.on("--command [HOST:PORT]",
54   - "Command socket (default: #{options[:command]})") do |command|
55   - options[:command] = command
56   - end
57   -
58   - opts.on("-l", "--listen [HOST:PORT]",
59   - "Listen address/port (default: #{options[:listen]})") do |listen|
60   - options[:listen] = listen
61   - end
62   -
63   - opts.on("--connect [HOST:PORT[,HOST:PORT[...]]]",
64   - "Hosts to connect to (default: #{options[:connect]})") do |connect|
65   - options[:connect] = connect
66   - end
67   -
68   - opts.on("-s", "--storage [BACKEND::CONFIG]",
69   - "Use storage backend (default: #{options[:storage]})") do |storage|
70   - options[:storage] = storage
71   - end
72   -
73   - opts.on("--ho", "--headers-only",
74   - "Download only block headers") do
75   - options[:headers_only] = true
76   - end
77   -
78   - opts.on("-d", "--dns", "Use DNS seeds (default)") do
79   - options[:dns] = true
80   - end
81   -
82   - opts.on("--nd", "--no-dns", "Don't use DNS seeds") do
83   - options[:dns] = false
84   - end
85   -
86   - opts.on("--epoll", "Enable epoll support") do
87   - options[:epoll] = true
88   - end
89   -
90   - opts.on("--epoll-limit [NUM]",
91   - "Increase socket descriptor limit to NUM") do |num|
92   - options[:epoll_limit] = num.to_i
93   - end
94   -
95   - opts.on("--epoll-user [NAME]",
96   - "Set effective user after increasing socket descriptor limit") do |user|
97   - options[:epoll_user] = user
98   - end
99   -
100   - [:connections, :addr, :queue, :inv].each do |name|
101   - opts.on("--m#{name[0]}", "--max-#{name} [COUNT]",
102   - "Max #{name} (default: #{options[:max][name]})") do |count|
103   - options[:max][name] = count.to_i
104   - end
105   - end
106   -
107   - [:queue, :inv_queue, :blocks, :addrs, :connect].each do |name|
108   - opts.on("--i#{name[0]}", "--interval-#{name} [SECONDS]",
109   - "Interval for #{name} worker (default: #{options[:intervals][name]})") do |sec|
110   - options[:intervals][name] = sec.to_i
111   - end
112   - end
113   -
114   - [:network, :storage].each do |name|
115   - opts.on("--l#{name[0]}", "--log-#{name} [LEVEL]",
116   - "Log level for #{name} (default: #{options[:log][name]})") do |level|
117   - options[:log][name] = level.to_sym
118   - end
119   - end
120   -
121   - opts.on("-v", "--verbose", "Set all loggers to debug") do
122   - options[:log].each_key {|k| options[:log][k] = :debug }
123   - end
124   -
125   - opts.on("-q", "--quiet", "Set all loggers to warn") do
126   - options[:log].each_key {|k| options[:log][k] = :warn }
127   - end
128   -
129   - opts.on( '-h', '--help', 'Display this screen' ) do
130   - puts opts; exit
131   - end
132   -end
133   -
134   -optparse.parse!
135   -
136   -Bitcoin.network = options[:network]
137   -options[:command] = options[:command] == "" ? nil : options[:command].split(':')
138   -options[:listen] = options[:listen] == "" ? nil : options[:listen].split(':')
139   -options[:connect] = options[:connect] == "" ? [] : options[:connect].split(',').map{|h| h.split(':')}
140   -
141   -node = Bitcoin::Network::Node.new(options)
142   -node.run
bin/bitcoin_command
1   -#!/usr/bin/env ruby
2   -$:.unshift( File.expand_path("../../lib", __FILE__) )
3   -
4   -require 'eventmachine'
5   -require 'json'
6   -require 'bitcoin'
7   -require 'optparse'
8   -
9   -defaults = {
10   - :command => "127.0.0.1:9999"
11   -}
12   -options = Bitcoin::Config.load(defaults, :command)
13   -
14   -optparse = OptionParser.new do |opts|
15   - opts.banner =
16   - "Usage: bitcoin_command [options] <command> [<command options>]\n"
17   -
18   - opts.separator("\nAvailable options:\n")
19   -
20   - opts.on("-c", "--config FILE",
21   - "Config file (default: #{Bitcoin::Config::CONFIG_PATHS})") do |file|
22   - options = Bitcoin::Config.load_file(options, file, :wallet)
23   - end
24   -
25   - opts.on("--command HOST:PORT",
26   - "Node command socket (default: #{options[:command]})") do |command|
27   - options[:command] = command
28   - end
29   -
30   - opts.on("-h", "--help", "Display this help") do
31   - puts opts; exit
32   - end
33   -
34   - opts.separator "\nAvailable commands:\n" +
35   - "run `bitcoin_command help` to see all available commands."
36   -end
37   -
38   -optparse.parse!
39   -host, port = options[:command].split(":")
40   -unless ARGV.any?
41   - puts optparse; exit
42   -end
43   -
44   -EM.run do
45   - Bitcoin::Network::CommandClient.connect(host, port) do
46   -
47   - on_response do |cmd, data|
48   - EM.stop unless cmd == "monitor"
49   - end
50   - on_info do |info|
51   - puts JSON.pretty_generate(info)
52   - end
53   - on_connections do |connections|
54   - puts *connections
55   - end
56   - [:connect, :disconnect, :getblocks, :getaddr, :relay_tx, :stop].each do |req|
57   - send("on_#{req}") {|res| p res}
58   - end
59   -
60   - on_block do |block, depth|
61   - puts "block: #{block['hash']} (#{depth})"
62   - end
63   - on_tx do |tx|
64   - puts "tx: #{tx['hash']}"
65   - end
66   - on_connection do |type, host|
67   - if type == "connected"
68   - puts "Connected: #{host['host']}:#{host['port']}"
69   - else
70   - puts "Disconnected: #{host.inspect}"
71   - end
72   - end
73   -
74   - on_connected do
75   - request(ARGV[0], *(ARGV[1] || "").split(" "))
76   - end
77   -
78   - end
79   -end
bin/bitcoin_connect
1   -#!/usr/bin/env ruby
2   -$:.unshift( File.expand_path("../../lib", __FILE__) )
3   -
4   -require 'bitcoin/connection'
5   -
6   -Bitcoin::network = :bitcoin
7   -
8   -class RawJSON_Connection < Bitcoin::Connection
9   - def on_tx(tx)
10   - p ['tx', tx.hash, Time.now]
11   - puts tx.to_json
12   - end
13   -
14   - def on_block(block)
15   - p ['block', block.hash, Time.now]
16   - puts block.to_json
17   - end
18   -end
19   -
20   -EM.run do
21   -
22   - host = '127.0.0.1'
23   - #host = '217.157.1.202'
24   -
25   - connections = []
26   - #RawJSON_Connection.connect(host, 8333, connections)
27   -
28   - RawJSON_Connection.connect_random_from_dns(connections)
29   -
30   -end
bin/bitcoin_relay_tx
1   -#!/usr/bin/env ruby
2   -require 'socket'
3   -require 'json'
4   -
5   -host, port = "127.0.0.1", 9999
6   -if ARGV[0] == "-s"
7   - host, port = ARGV[1].split(":")
8   - ARGV.shift; ARGV.shift
9   -end
10   -
11   -s = TCPSocket.new("127.0.0.1", 9999)
12   -s.puts ("relay_tx " + ARGF.read.unpack("H*")[0])
13   -
14   -res = s.readline
15   -puts JSON::pretty_generate(JSON::parse(res))
16   -s.close
1 1 $:.unshift( File.expand_path("../../lib", __FILE__) )
2 2 require 'bitcoin'
3 3  
  4 +include Bitcoin
4 5 include Bitcoin::Util
5 6 include Bitcoin::Storage
6 7  
7   -Bitcoin.network = ARGV.shift || :testnet
  8 +Bitcoin.network = ARGV.shift || :bitcoin
8 9  
9 10 require 'irb'
10 11 require 'irb/completion'
bin/bitcoin_verify_tx
1   -#!/usr/bin/env ruby
2   -$:.unshift( File.expand_path("../../lib", __FILE__) )
3   -
4   -require 'bitcoin'
5   -require 'optparse'
6   -
7   -options = {
8   - :network => "testnet",
9   - :storage => "dummy",
10   -}
11   -optparse = OptionParser.new do|opts|
12   - opts.banner = "Usage: bitcoin_verify_tx [options] <tx hash>"
13   -
14   - opts.on("-n", "--network [NETWORK]", "User Network (default: testnet)") do |network|
15   - options[:network] = network
16   - end
17   -
18   - opts.on("-s", "--storage [BACKEND::CONFIG]", "Use storage backend (default: 'dummy')") do |storage|
19   - options[:storage] = storage
20   - end
21   -
22   - opts.on( '-h', '--help', 'Display this screen' ) do
23   - puts opts
24   - exit
25   - end
26   -end
27   -optparse.parse!
28   -
29   -
30   -Bitcoin.network = options[:network]
31   -puts "Using network #{options[:network]}"
32   -backend, config = options[:storage].split('::')
33   -store = Bitcoin::Storage.send(backend, :db => config)
34   -puts "Using #{backend} store #{config}"
35   -
36   -tx_hash = ARGV.shift
37   -
38   -tx1 = store.get_tx(tx_hash)
39   -
40   -unless tx1
41   - puts "Tx #{tx_hash} not found."
42   - exit
43   -end
44   -
45   -if tx1.in.all?{|txin| txin.coinbase? }
46   - puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
47   - exit
48   -end
49   -
50   -tx1.in.each_with_index do |txin, idx|
51   - if txin.coinbase?
52   - puts "skipping coinbase transaction input.."; next
53   - end
54   -
55   - prev_tx = txin.get_prev_out.get_tx
56   - unless prev_tx
57   - puts "Missing prev_out tx for input #{idx} of tx #{tx_hash}!"
58   - exit
59   - end
60   -
61   - result = tx1.verify_input_signature(idx, prev_tx)
62   - unless result
63   - puts "Input #{idx} of tx #{tx_hash} is invalid!"
64   - exit
65   - end
66   -end
67   -
68   -puts "Tx #{tx_hash} is valid."
... ... @@ -9,7 +9,7 @@
9 9 defaults = {
10 10 :network => "testnet",
11 11 :storage => "dummy",
12   - :keystore => "dummy",
  12 + :keystore => "simple::file=#{ENV['HOME']}/.bitcoin-ruby/keys.json",
13 13 :command => "127.0.0.1:9999"
14 14 }
15 15 options = Bitcoin::Config.load(defaults, :wallet)
... ... @@ -42,7 +42,7 @@
42 42  
43 43 opts.on("-k", "--keystore [backend::<config>]",
44 44 "Key store (default: #{options[:store]})") do |store|
45   - options[:keystore] = store
  45 + options[:keystore] = store.gsub("~", ENV['HOME'])
46 46 end
47 47  
48 48 opts.on("-h", "--help", "Display this help") do
  1 += Config
  2 +
  3 +All commands accept configuration, either via config file, or at the command line.
  4 +
  5 +== Locations
  6 +
  7 +There are 3 default locations where configfiles are loaded from:
  8 +
  9 +* +/etc/bitcoin-ruby.yml+
  10 +* +~/.bitcoin-ruby.yml+
  11 +* +./bitcoin-ruby.yml+
  12 +
  13 +Files are loaded in order (if they exist) and override each others settings.
  14 +
  15 +To specify a different config file use the +--config+ option.
  16 +
  17 +== Files
  18 +
  19 +Inside a config file, you can put options for the different commands, separated
  20 +into categories.
  21 +
  22 + all:
  23 + network: bitcoin
  24 + storage: sequel::postgres:/bitcoin
  25 + command: 127.0.0.1:1234
  26 + blockchain:
  27 + max:
  28 + connect: 30
  29 + wallet:
  30 + keystore: "simple::file=keys.json"
  31 +
  32 +Options in the +all+ category are loaded by every command, and are loaded first
  33 +(so command-specific options will override them).
  34 +
  35 +Other categories are loaded by the corresponding command and may override options
  36 +from the +all+ category (ie. +bitcoin_wallet+ loads +all+ and +wallet+).
  37 +
  38 +== Default Values
  39 +
  40 + all:
  41 + network: bitcoin # network identifier ("bitcoin" or "testnet")
  42 + command: "127.0.0.1:9999" # IP:Port to listen for incomming command connections
  43 + listen: "0.0.0.0:8332" # IP:Port to listen for incoming peer connections
  44 + connect: "" # List of IP:Port,IP:Port of nodes to connect to
  45 + storage: "sequel::sqlite://bitcoin.db" # storage backend to use (see STORAGE)
  46 + node:
  47 + headers_only: false # download/store only block headers (experimental)
  48 + dns: true # query peers from dns server
  49 + epoll: false
  50 + epoll_limit: 10000
  51 + epoll_user: nil
  52 + log: # log levels (debug, info, warn, error, fatal)
  53 + network: info
  54 + storage: info
  55 + max:
  56 + connections: 32 # number of peer connections
  57 + addr: 1024 # peer-address store size
  58 + queue: 500 # storage-queue size
  59 + inv: 500 # inventory-queue size
  60 + intervals:
  61 + queue: 5 # work storage queue
  62 + inv_queue: 5 # work inventory queue
  63 + addrs: 15 # collect new peer addrs
  64 + connect: 30 # connect to new peers
  65 + wallet:
  66 + keystore: "simple::file=~/.bitcoin-ruby/keys.json" # keystore to use
  1 += Examples
  2 +
  3 +There are a few demo scripts in `examples/` that might give you an idea where to start.
  4 +
  5 +examples/connect.rb:: Connect to a network node and chat a little.
  6 +examples/verify_tx.rb:: Fetch a transaction from local storage and verify signatures.
  7 +examples/bbe_verify_tx.rb:: Fetch transaction from blockexplorer.com and verify signatures.
  8 +examples/balance.rb:: Display balance/transactions for given address.
  9 +examples/relay_tx.rb:: Relay transaction to the network.
  1 += NODE
  2 +
  3 +Bitcoin Node. Connects to the network and downloads the blockchain into local storage.
  4 +
  5 +== Usage
  6 +
  7 +To run the node with the default options (download the blockchain into ./bitcoin.db