bitcoin-ruby 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
data/.gitignore
CHANGED
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.
|
8
|
-
* parse/create (almost?) all protocol messages
|
9
|
-
*
|
10
|
-
|
11
|
-
*
|
12
|
-
*
|
13
|
-
* create
|
14
|
-
*
|
15
|
-
*
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
67
|
-
"Hosts to connect to (default: #{options[:connect]})") do |connect|
|
68
|
-
|
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
|
-
|
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
|
-
|
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
data/bin/bitcoin_wallet
CHANGED
@@ -8,8 +8,8 @@ require 'yaml'
|
|
8
8
|
|
9
9
|
defaults = {
|
10
10
|
:network => "testnet",
|
11
|
-
:storage =>
|
12
|
-
:keystore =>
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
298
|
-
raise "prev tx #{txin.prev_out.
|
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
|
|