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.
@@ -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"
@@ -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
@@ -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 unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
1041
- #return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
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 unless (0..n_pubkeys).include?(n_sigs)
1046
- return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
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
- valid_sigs = 0
1059
- sigs.each{|sig| pubkeys.each{|pubkey|
1060
- next if sig == ""
1061
- signature, hash_type = parse_sig(sig)
1062
- valid_sigs += 1 if check_callback.call(pubkey, signature, hash_type, drop_sigs, script_code)
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 << ((valid_sigs >= n_sigs) ? 1 : (invalid; 0))
1073
+ @stack << (success ? 1 : (invalid; 0))
1066
1074
  end
1067
1075
 
1068
1076
  # op_eval: https://en.bitcoin.it/wiki/BIP_0012
@@ -3,7 +3,7 @@ Sequel.migration do
3
3
 
4
4
  up do
5
5
 
6
- $stdout.puts "Running migration #{__FILE__}"
6
+ @log.info { "Running migration #{__FILE__}" }
7
7
 
8
8
  binary = adapter_scheme == :postgres ? :bytea : :varchar
9
9
 
@@ -2,7 +2,7 @@ Sequel.migration do
2
2
 
3
3
  up do
4
4
 
5
- $stdout.puts "Running migration #{__FILE__}"
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
@@ -2,7 +2,7 @@ Sequel.migration do
2
2
 
3
3
  up do
4
4
 
5
- $stdout.puts "Running migration #{__FILE__}"
5
+ @log.info { "Running migration #{__FILE__}" }
6
6
 
7
7
  if adapter_scheme == :postgres
8
8
  add_column :txin, :tmp_script_sig, :bytea
@@ -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(
@@ -3,7 +3,7 @@ Sequel.migration do
3
3
 
4
4
  up do
5
5
 
6
- $stdout.puts "Running migration #{__FILE__}"
6
+ @log.info { "Running migration #{__FILE__}" }
7
7
 
8
8
  binary = adapter_scheme == :postgres ? :bytea : :varchar
9
9
 
@@ -2,7 +2,7 @@ Sequel.migration do
2
2
 
3
3
  up do
4
4
 
5
- $stdout.puts "Running migration #{__FILE__}"
5
+ @log.info { "Running migration #{__FILE__}" }
6
6
 
7
7
  create_table :utxo do
8
8
  primary_key :id
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -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
 
@@ -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
- [ { :name => :utxo, :db => 'sqlite:/', :index_all_addrs => true },
10
- { :name => :sequel, :db => 'sqlite:/' } ] .each do |configuration|
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
- describe "reorg (#{configuration[:name].capitalize}Store)" do
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
- Bitcoin.network = :testnet
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
- [ { :name => :dummy },
12
- { :name => :utxo, :db => 'sqlite:/', :index_all_addrs => true },
13
- { :name => :sequel, :db => 'sqlite:/' } ].each do |configuration|
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
- describe "Bitcoin::Storage::Backends::#{configuration[:name].capitalize}Store" do
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
- Bitcoin::network = :testnet
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 == configuration[:name].to_s
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
- [ { :name => :utxo, :db => 'sqlite:/', :index_all_addrs => true },
10
- { :name => :sequel, :db => 'sqlite:/' } ].each do |configuration|
9
+ Bitcoin.network = :spec
11
10
 
12
- describe "block rules (#{configuration[:name].capitalize}Store)" do
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
- Bitcoin.network = :spec
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 (#{configuration[:name].capitalize}Store)" do
159
+ describe "transaction rules (#{options[0]} - #{options[1]})" do
151
160
 
152
161
  before do
153
162
  Bitcoin.network = :spec
154
- @store = Bitcoin::Storage.send(configuration[:name], configuration)
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].where(id: 2).update(value: 22e14)
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.3
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-04 00:00:00.000000000 Z
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