bitcoin-ruby 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|