bitcoin-ruby 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -1
- data/README.rdoc +5 -0
- data/lib/bitcoin/script.rb +19 -11
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +1 -1
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +1 -6
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +1 -1
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +18 -0
- data/lib/bitcoin/storage/storage.rb +1 -0
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/script/opcodes_spec.rb +6 -0
- data/spec/bitcoin/spec_helper.rb +21 -0
- data/spec/bitcoin/storage/reorg_spec.rb +14 -8
- data/spec/bitcoin/storage/storage_spec.rb +15 -8
- data/spec/bitcoin/storage/validation_spec.rb +17 -8
- metadata +3 -2
data/.travis.yml
CHANGED
@@ -1,4 +1,10 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 1.9.3
|
4
|
-
- 2.0.0
|
4
|
+
- 2.0.0
|
5
|
+
before_script:
|
6
|
+
- psql -c 'create database bitcoin_test;' -U postgres
|
7
|
+
- mysql -e 'create database bitcoin_test;'
|
8
|
+
env:
|
9
|
+
- TEST_DB_POSTGRES="postgres://postgres@localhost/bitcoin_test"
|
10
|
+
- TEST_DB_MYSQL="mysql:/bitcoin_test"
|
data/README.rdoc
CHANGED
@@ -240,6 +240,11 @@ or, if you want to run a single spec
|
|
240
240
|
|
241
241
|
ruby spec/bitcoin/bitcoin_spec.rb
|
242
242
|
|
243
|
+
To test the postgres/mysql storage backend, create a database and set it as an environment variable:
|
244
|
+
|
245
|
+
echo "create database bitcoin_test" | psql
|
246
|
+
TEST_DB_POSTGRES="postgres:/bitcoin_test" rake bacon
|
247
|
+
|
243
248
|
If you make changes to the code or add functionality, please also add specs.
|
244
249
|
|
245
250
|
== Development
|
data/lib/bitcoin/script.rb
CHANGED
@@ -1035,15 +1035,17 @@ class Bitcoin::Script
|
|
1035
1035
|
# TODO: validate signature order
|
1036
1036
|
# TODO: take global opcode count
|
1037
1037
|
def op_checkmultisig(check_callback)
|
1038
|
+
return invalid if @stack.size < 1
|
1038
1039
|
n_pubkeys = pop_int
|
1039
1040
|
return invalid unless (0..20).include?(n_pubkeys)
|
1040
|
-
return invalid
|
1041
|
-
|
1041
|
+
#return invalid if (nOpCount += n_pubkeys) > 201
|
1042
|
+
return invalid if @stack.size < n_pubkeys
|
1042
1043
|
pubkeys = pop_string(n_pubkeys)
|
1043
1044
|
|
1045
|
+
return invalid if @stack.size < 1
|
1044
1046
|
n_sigs = pop_int
|
1045
|
-
return invalid
|
1046
|
-
return invalid
|
1047
|
+
return invalid if n_sigs < 0 || n_sigs > n_pubkeys
|
1048
|
+
return invalid if @stack.size < n_sigs
|
1047
1049
|
sigs = drop_sigs = pop_string(n_sigs)
|
1048
1050
|
|
1049
1051
|
@stack.pop if @stack[-1] && cast_to_bignum(@stack[-1]) == 0 # remove OP_0 from stack
|
@@ -1055,14 +1057,20 @@ class Bitcoin::Script
|
|
1055
1057
|
script_code, drop_sigs = nil, nil
|
1056
1058
|
end
|
1057
1059
|
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1060
|
+
success = true
|
1061
|
+
while success && n_sigs > 0
|
1062
|
+
sig, pub = sigs.pop, pubkeys.pop
|
1063
|
+
signature, hash_type = parse_sig(sig)
|
1064
|
+
if check_callback.call(pub, signature, hash_type, drop_sigs, script_code)
|
1065
|
+
n_sigs -= 1
|
1066
|
+
else
|
1067
|
+
sigs << sig
|
1068
|
+
end
|
1069
|
+
n_pubkeys -= 1
|
1070
|
+
success = false if n_sigs > n_pubkeys
|
1071
|
+
end
|
1064
1072
|
|
1065
|
-
@stack << (
|
1073
|
+
@stack << (success ? 1 : (invalid; 0))
|
1066
1074
|
end
|
1067
1075
|
|
1068
1076
|
# op_eval: https://en.bitcoin.it/wiki/BIP_0012
|
@@ -2,7 +2,7 @@ Sequel.migration do
|
|
2
2
|
|
3
3
|
up do
|
4
4
|
|
5
|
-
|
5
|
+
@log.info { "Running migration #{__FILE__}" }
|
6
6
|
|
7
7
|
next if tables.include?(:tx)
|
8
8
|
|
@@ -40,11 +40,6 @@ Sequel.migration do
|
|
40
40
|
column :type, :int, :null => false, :index => true
|
41
41
|
end
|
42
42
|
|
43
|
-
create_view(:unconfirmed,
|
44
|
-
"SELECT * FROM tx WHERE NOT EXISTS " +
|
45
|
-
"(SELECT 1 FROM blk_tx WHERE blk_tx.tx_id = tx.id)" +
|
46
|
-
"ORDER BY tx.id DESC")
|
47
|
-
|
48
43
|
end
|
49
44
|
|
50
45
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
|
3
|
+
up do
|
4
|
+
|
5
|
+
@log.info { "Running migration #{__FILE__}" }
|
6
|
+
|
7
|
+
if adapter_scheme == :postgres
|
8
|
+
add_column :txin, :tmp_prev_out, :bytea
|
9
|
+
self[:txin].where.update("tmp_prev_out = prev_out::bytea")
|
10
|
+
drop_column :txin, :prev_out
|
11
|
+
add_column :txin, :prev_out, :bytea, index: true
|
12
|
+
self[:txin].where.update("prev_out = tmp_prev_out")
|
13
|
+
drop_column :txin, :tmp_prev_out
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -101,6 +101,7 @@ module Bitcoin::Storage
|
|
101
101
|
migrations_path = File.join(File.dirname(__FILE__), "#{backend_name}/migrations")
|
102
102
|
Sequel.extension :migration
|
103
103
|
unless Sequel::Migrator.is_current?(@db, migrations_path)
|
104
|
+
log = @log; @db.instance_eval { @log = log }
|
104
105
|
Sequel::Migrator.run(@db, migrations_path)
|
105
106
|
unless (v = @db[:schema_info].first) && v[:magic] && v[:backend]
|
106
107
|
@db[:schema_info].update(
|
data/lib/bitcoin/version.rb
CHANGED
@@ -542,6 +542,12 @@ describe "Bitcoin::Script OPCODES" do
|
|
542
542
|
script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
543
543
|
run_script(script, "foobar").should == true
|
544
544
|
|
545
|
+
script = "0 #{sig2} #{sig1} 2 #{k1.pub} #{k2.pub} 2 OP_CHECKMULTISIG"
|
546
|
+
run_script(script, "foobar").should == false
|
547
|
+
|
548
|
+
script = "0 #{sig1} #{sig2} 2 #{k2.pub} #{k1.pub} 2 OP_CHECKMULTISIG"
|
549
|
+
run_script(script, "foobar").should == false
|
550
|
+
|
545
551
|
script = "0 #{sig1} #{sig2} 2 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
|
546
552
|
run_script(script, "foobar").should == true
|
547
553
|
|
data/spec/bitcoin/spec_helper.rb
CHANGED
@@ -102,3 +102,24 @@ rescue LoadError
|
|
102
102
|
end
|
103
103
|
Bacon.summary_on_exit
|
104
104
|
require 'minitest/mock'
|
105
|
+
|
106
|
+
require 'sequel'
|
107
|
+
def setup_db backend, db = nil, conf = {}
|
108
|
+
uri = case db
|
109
|
+
when :sqlite
|
110
|
+
"sqlite:/"
|
111
|
+
when :postgres
|
112
|
+
ENV["TEST_DB_POSTGRES"].dup rescue nil
|
113
|
+
when :mysql
|
114
|
+
ENV["TEST_DB_MYSQL"].dup rescue nil
|
115
|
+
end
|
116
|
+
if [:postgres, :mysql].include?(db)
|
117
|
+
unless uri
|
118
|
+
puts "Skipping #{db} tests"
|
119
|
+
return nil
|
120
|
+
end
|
121
|
+
db = Sequel.connect(uri)
|
122
|
+
db.drop_table(*db.tables, cascade: true)
|
123
|
+
end
|
124
|
+
Bitcoin::Storage.send(backend, conf.merge(db: uri, log_level: :warn))
|
125
|
+
end
|
@@ -4,23 +4,30 @@ require_relative '../spec_helper'
|
|
4
4
|
|
5
5
|
include Bitcoin::Builder
|
6
6
|
|
7
|
+
Bitcoin.network = :testnet
|
7
8
|
|
9
|
+
Bitcoin::Validation::Block::RETARGET = 10
|
8
10
|
|
9
|
-
[
|
10
|
-
|
11
|
+
[
|
12
|
+
[:utxo, :sqlite, index_all_addrs: true],
|
13
|
+
[:sequel, :sqlite], # [:sequel, :postgres],
|
14
|
+
[:utxo, :postgres, index_all_addrs: true],
|
15
|
+
[:sequel, :mysql],
|
16
|
+
[:utxo, :mysql, index_all_addrs: true],
|
17
|
+
].compact.each do |options|
|
11
18
|
|
12
|
-
|
19
|
+
next unless storage = setup_db(*options)
|
20
|
+
|
21
|
+
describe "reorg (#{options[0]} - #{options[1]})" do
|
13
22
|
|
14
23
|
def balance addr
|
15
24
|
@store.get_balance(Bitcoin.hash160_from_address(addr))
|
16
25
|
end
|
17
26
|
|
18
27
|
before do
|
19
|
-
|
20
|
-
@store = Bitcoin::Storage.send(configuration[:name], configuration)
|
28
|
+
@store = storage
|
21
29
|
@store.reset
|
22
30
|
def @store.in_sync?; true; end
|
23
|
-
@store.log.level = :warn
|
24
31
|
Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
|
25
32
|
@key = Bitcoin::Key.generate
|
26
33
|
@block0 = create_block "00"*32, false, [], @key
|
@@ -33,8 +40,6 @@ include Bitcoin::Builder
|
|
33
40
|
@store.reset
|
34
41
|
time = Time.now.to_i - 3000*600
|
35
42
|
|
36
|
-
Bitcoin::Validation::Block::RETARGET = 10
|
37
|
-
|
38
43
|
# create genesis block
|
39
44
|
block = create_block "00"*32, false, [], @key, 50e8, {time: time}
|
40
45
|
Bitcoin.network[:genesis_hash] = block.hash
|
@@ -187,6 +192,7 @@ include Bitcoin::Builder
|
|
187
192
|
balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000
|
188
193
|
balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0
|
189
194
|
balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000
|
195
|
+
Bitcoin.network = :testnet
|
190
196
|
class Bitcoin::Validation::Block
|
191
197
|
def difficulty
|
192
198
|
return true if Bitcoin.network_name == :testnet3
|
@@ -8,21 +8,28 @@ include Bitcoin::Storage::Backends
|
|
8
8
|
include Bitcoin::Builder
|
9
9
|
include Bitcoin::Validation
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
Bitcoin::network = :testnet
|
12
|
+
[
|
13
|
+
[:dummy],
|
14
|
+
[:sequel, :sqlite],
|
15
|
+
[:utxo, :sqlite, index_all_addrs: true],
|
16
|
+
[:sequel, :postgres],
|
17
|
+
[:utxo, :postgres, index_all_addrs: true],
|
18
|
+
[:sequel, :mysql],
|
19
|
+
[:utxo, :mysql, index_all_addrs: true],
|
20
|
+
].compact.each do |options|
|
14
21
|
|
15
|
-
|
22
|
+
next unless storage = setup_db(*options)
|
23
|
+
|
24
|
+
describe "Storage::Backends::#{options[0].to_s.capitalize}Store (#{options[1]})" do
|
16
25
|
|
17
26
|
before do
|
18
27
|
class Bitcoin::Validation::Block; def difficulty; true; end; end
|
19
28
|
Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
|
20
29
|
|
21
|
-
|
22
|
-
@store = Bitcoin::Storage.send(configuration[:name], configuration)
|
30
|
+
@store = storage
|
23
31
|
def @store.in_sync?; true; end
|
24
32
|
@store.reset
|
25
|
-
@store.log.level = 4
|
26
33
|
|
27
34
|
@store.store_block(P::Block.new(fixtures_file('testnet/block_0.bin')))
|
28
35
|
@store.store_block(P::Block.new(fixtures_file('testnet/block_1.bin')))
|
@@ -48,7 +55,7 @@ include Bitcoin::Validation
|
|
48
55
|
end
|
49
56
|
|
50
57
|
it "should get backend name" do
|
51
|
-
@store.backend_name.should ==
|
58
|
+
@store.backend_name.should == options[0].to_s
|
52
59
|
end
|
53
60
|
|
54
61
|
it "should get depth" do
|
@@ -6,18 +6,27 @@ include Bitcoin::Builder
|
|
6
6
|
include Bitcoin::Storage
|
7
7
|
include Bitcoin::Validation
|
8
8
|
|
9
|
-
|
10
|
-
{ :name => :sequel, :db => 'sqlite:/' } ].each do |configuration|
|
9
|
+
Bitcoin.network = :spec
|
11
10
|
|
12
|
-
|
11
|
+
[
|
12
|
+
[:sequel, :sqlite],
|
13
|
+
[:utxo, :sqlite, index_all_addrs: true],
|
14
|
+
[:sequel, :postgres],
|
15
|
+
[:utxo, :postgres, index_all_addrs: true],
|
16
|
+
[:sequel, :mysql],
|
17
|
+
[:utxo, :mysql, index_all_addrs: true],
|
18
|
+
].compact.each do |options|
|
19
|
+
|
20
|
+
next unless storage = setup_db(*options)
|
21
|
+
|
22
|
+
describe "block rules (#{options[0]} - #{options[1]})" do
|
13
23
|
|
14
24
|
def balance addr
|
15
25
|
@store.get_balance(Bitcoin.hash160_from_address(addr))
|
16
26
|
end
|
17
27
|
|
18
28
|
before do
|
19
|
-
|
20
|
-
@store = Bitcoin::Storage.send(configuration[:name], configuration)
|
29
|
+
@store = storage
|
21
30
|
@store.reset
|
22
31
|
@store.log.level = :warn
|
23
32
|
Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("f"*64)
|
@@ -147,11 +156,11 @@ include Bitcoin::Validation
|
|
147
156
|
|
148
157
|
end
|
149
158
|
|
150
|
-
describe "transaction rules (#{
|
159
|
+
describe "transaction rules (#{options[0]} - #{options[1]})" do
|
151
160
|
|
152
161
|
before do
|
153
162
|
Bitcoin.network = :spec
|
154
|
-
@store =
|
163
|
+
@store = storage
|
155
164
|
@store.reset
|
156
165
|
@store.log.level = :warn
|
157
166
|
|
@@ -247,7 +256,7 @@ describe "transaction rules (#{configuration[:name].capitalize}Store)" do
|
|
247
256
|
end
|
248
257
|
|
249
258
|
it "15. Using the referenced output transactions to get input values, check that each input value, as well as the sum, are in legal money range" do
|
250
|
-
@store.db[@store.class.name =~ /Utxo/ ? :utxo : :txout].
|
259
|
+
@store.db[@store.class.name =~ /Utxo/ ? :utxo : :txout].order(:id).reverse.limit(1).update(value: 22e14)
|
251
260
|
check_tx(@tx, [:input_values, [22e14, 21e14]])
|
252
261
|
end
|
253
262
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoin-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-12-
|
12
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: This is a ruby library for interacting with the bitcoin protocol/network
|
15
15
|
email:
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- lib/bitcoin/storage/sequel/migrations/001_base_schema.rb
|
94
94
|
- lib/bitcoin/storage/sequel/migrations/002_tx.rb
|
95
95
|
- lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb
|
96
|
+
- lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb
|
96
97
|
- lib/bitcoin/storage/sequel/sequel_store.rb
|
97
98
|
- lib/bitcoin/storage/storage.rb
|
98
99
|
- lib/bitcoin/storage/utxo/migrations/001_base_schema.rb
|