diff --git a/.gitignore b/.gitignore index 4004bee9..6cd4effe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ tmp/ log/ rdoc/ coverage/ +spec/bitcoin/fixtures/fake_chain *.conf *.db .rbx/ diff --git a/lib/bitcoin.rb b/lib/bitcoin.rb index a163f8d9..4f2e73e2 100644 --- a/lib/bitcoin.rb +++ b/lib/bitcoin.rb @@ -552,6 +552,7 @@ def self.network=(name) :target_spacing => 600, # block interval :max_money => 21_000_000 * COIN, :min_tx_fee => 10_000, + :no_difficulty => true, :min_relay_tx_fee => 10_000, :dns_seeds => [ "testnet-seed.bitcoin.petertodd.org", @@ -701,6 +702,39 @@ def self.network=(name) } }, + + :peercoin => { + :project => :peercoin, + :magic_head => "\xe6\xe8\xe9\xe5", + :address_version => "37", + :p2sh_version => "75", + :privkey_version => "b7", + :default_port => 9901, + :dns_seeds => [ "seed.ppcoin.net" ], + :genesis_hash => "0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3", + :proof_of_work_limit => 0, + :alert_pubkeys => [], + :known_nodes => [ "theseven.bounceme.net", "cryptocoinexplorer.com" ], + :checkpoints => [ + [19080, "000000000000bca54d9ac17881f94193fd6a270c1bb21c3bf0b37f588a40dbd7"], + [30583, "d39d1481a7eecba48932ea5913be58ad3894c7ee6d5a8ba8abeb772c66a6696e"], + ] + }, + + :peercoin_testnet => { + :project => :peercoin, + :magic_head => "\xcb\xf2\xc0\xef", + :address_version => "6f", + :p2sh_version => "c4", + :privkey_version => "ef", + :default_port => 9903, + :dns_seeds => [ "tnseed.ppcoin.net" ], + :genesis_hash => "00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06", + :proof_of_work_limit => 0, + :alert_pubkeys => [], + :known_nodes => [], + :checkpoints => [] + }, } end diff --git a/lib/bitcoin/builder.rb b/lib/bitcoin/builder.rb index 880b208e..099c4449 100644 --- a/lib/bitcoin/builder.rb +++ b/lib/bitcoin/builder.rb @@ -71,6 +71,7 @@ def time time def tx tx = nil tx ||= ( c = TxBuilder.new; yield c; c.tx ) @block.tx << tx + tx end # create the block according to values specified via DSL. diff --git a/lib/bitcoin/network/node.rb b/lib/bitcoin/network/node.rb index 7bbc304e..316dcdd3 100644 --- a/lib/bitcoin/network/node.rb +++ b/lib/bitcoin/network/node.rb @@ -254,8 +254,8 @@ def run end subscribe(:block) do |blk, depth| - @log.debug { "Relaying block #{blk.hash}" } next unless @store.in_sync? + @log.debug { "Relaying block #{blk.hash}" } @connections.each do |conn| next unless conn.connected? conn.send_inv(:block, blk.hash) diff --git a/lib/bitcoin/protocol/block.rb b/lib/bitcoin/protocol/block.rb index 119f8dfb..68c66a13 100644 --- a/lib/bitcoin/protocol/block.rb +++ b/lib/bitcoin/protocol/block.rb @@ -90,6 +90,11 @@ def parse_data_from_io(buf, header_only=false) @tx << t } + if Bitcoin.network_project == :peercoin + @block_signature = Protocol.unpack_var_string_from_io(buf) + @block_signature ||= "" + end + @payload = to_payload buf end @@ -121,6 +126,7 @@ def to_payload return head if @tx.size == 0 head << Protocol.pack_var_int(@tx.size) @tx.each{|tx| head << tx.to_payload } + head << Protocol.pack_var_string(@block_signature) if Bitcoin.network_project == :peercoin head end @@ -135,6 +141,7 @@ def to_hash 'mrkl_tree' => Bitcoin.hash_mrkl_tree( @tx.map{|i| i.hash } ) } h['aux_pow'] = @aux_pow.to_hash if @aux_pow + h['signature'] = @block_signature.reverse_hth if Bitcoin.network_project == :peercoin h end @@ -196,6 +203,7 @@ def self.from_hash(h, do_raise=true) if h['tx'].any? && !Bitcoin.freicoin? (raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise end + @block_signature = h['signature'].htb_reverse if Bitcoin.network_project == :peercoin } blk end diff --git a/lib/bitcoin/protocol/tx.rb b/lib/bitcoin/protocol/tx.rb index 9361d4ab..0f8376ce 100644 --- a/lib/bitcoin/protocol/tx.rb +++ b/lib/bitcoin/protocol/tx.rb @@ -41,6 +41,7 @@ def binary_hash # create tx from raw binary +data+ def initialize(data=nil) @ver, @lock_time, @in, @out = 1, 0, [], [] + @time = Time.now.to_i if Bitcoin.network_project == :peercoin parse_data_from_io(data) if data end @@ -63,6 +64,8 @@ def parse_data_from_io(data) @ver = buf.read(4).unpack("V")[0] + @time = buf.read(4).unpack("V")[0] if Bitcoin.network_project == :peercoin + in_size = Protocol.unpack_var_int_from_io(buf) @in = [] in_size.times{ @in << TxIn.from_io(buf) } @@ -94,7 +97,9 @@ def to_payload pout = "" @out.each{|output| pout << output.to_payload } - [@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V") + result = [@ver].pack("V") + result << [@time].pack("V") if Bitcoin.network_project == :peercoin + result << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V") end @@ -150,7 +155,11 @@ def signature_hash_for_input(input_idx, subscript, hash_type=nil) in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ] end - buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join + if Bitcoin.network_project == :peercoin + buf = [ [@ver, @time].pack("VV"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join + else + buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join + end Digest::SHA256.digest( Digest::SHA256.digest( buf ) ) end @@ -185,6 +194,7 @@ def to_hash(options = {}) 'in' => @in.map{|i| i.to_hash(options) }, 'out' => @out.map{|o| o.to_hash(options) } } + h['time'] = @time if Bitcoin.network_project == :peercoin h end @@ -205,6 +215,7 @@ def self.from_hash(h) tx.ver, tx.lock_time = *h.values_at('ver', 'lock_time') h['in'] .each{|input| tx.add_in TxIn.from_hash(input) } h['out'].each{|output| tx.add_out TxOut.from_hash(output) } + tx.instance_eval{ @time = h['time'] } if Bitcoin.network_project == :peercoin tx.instance_eval{ @hash = hash_from_payload(@payload = to_payload) } tx end diff --git a/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb b/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb index fb390dfe..d3601119 100644 --- a/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +++ b/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb @@ -11,23 +11,20 @@ def process_block blk end end - if $stdin.tty? - print "Do you want to build an index for normalized tx hashes? (~1GB) (y/N) " - if $0 =~ /spec/ || $stdin.gets.chomp == "y" - puts "Building normalized hash index..." + if @store.config[:index_nhash] + puts "Building normalized hash index..." - add_column :tx, :nhash, :bytea + add_column :tx, :nhash, :bytea - if blk = @store.get_block_by_depth(0) + if blk = @store.get_block_by_depth(0) + process_block(blk) + while blk = blk.get_next_block process_block(blk) - while blk = blk.get_next_block - process_block(blk) - end end + end - add_index :tx, :nhash + add_index :tx, :nhash - end end end diff --git a/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb b/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb new file mode 100644 index 00000000..cdf6b49d --- /dev/null +++ b/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb @@ -0,0 +1,16 @@ +Sequel.migration do + + up do + + @log.info { "Running migration #{__FILE__}" } + + # Naming seems to be different on different adapters and sequel's + # "drop_index(:txin, :prev_out)" doesn't seem to be handling it correctly + execute "DROP INDEX IF EXISTS txin_prev_out_idx;" + execute "DROP INDEX IF EXISTS txin_prev_out_index;" + + add_index :txin, [:prev_out, :prev_out_index] + + end + +end diff --git a/lib/bitcoin/validation.rb b/lib/bitcoin/validation.rb index 28df5c09..d5e1f8d6 100644 --- a/lib/bitcoin/validation.rb +++ b/lib/bitcoin/validation.rb @@ -109,7 +109,7 @@ def prev_hash # check that bits satisfy required difficulty def difficulty - return true if Bitcoin.network_name == :testnet3 + return true if Bitcoin.network[:no_difficulty] == true block.bits == next_bits_required || [block.bits, next_bits_required] end @@ -155,19 +155,28 @@ def transactions_context end def tx_validators - @tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block, tx_cache: prev_txs_hash)} + @tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block, tx_cache: prev_txs_hash, spent_outs_txins: spent_outs_txins)} end # Fetch all prev_txs that will be needed for validation # Used for optimization in tx validators def prev_txs_hash @prev_tx_hash ||= ( - inputs = block.tx.map {|tx| tx.in }.flatten + inputs = block.tx[1..-1].map {|tx| tx.in }.flatten txs = store.get_txs(inputs.map{|i| i.prev_out.reverse_hth }) Hash[*txs.map {|tx| [tx.hash, tx] }.flatten] ) end + def spent_outs_txins + @spent_outs_txins ||= ( + next_ins = store.get_txins_for_txouts(block.tx[1..-1].map(&:in).flatten.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] }) + # OPTIMIZE normally next_ins is empty, but in case of some reorgs this could be pain, becouse get_tx is heavy + # and all we need is a few joins (but some general abstraction is needed for that in storage) + next_ins.select {|i| i.get_tx.blk_id } + ) + end + def next_bits_required retarget = (Bitcoin.network[:retarget_interval] || Bitcoin::RETARGET_INTERVAL) index = (prev_block.depth + 1) / retarget @@ -234,10 +243,13 @@ def validate(opts = {}) # setup new validator for given +tx+, validating context with +store+. # also needs the +block+ to find prev_outs for chains of tx inside one block. - # opts+ may include :tx_cache which should be hash with transactiotns including prev_txs + # opts+ may include: + # * :tx_cache which should be hash with transactiotns including prev_txs + # * :spent_outs_txins txins for txouts that were already spent def initialize(tx, store, block = nil, opts = {}) @tx, @store, @block, @errors = tx, store, block, [] @tx_cache = opts[:tx_cache] + @spent_outs_txins = opts[:spent_outs_txins] end # check that tx hash matches data @@ -308,9 +320,11 @@ def signatures # check that none of the prev_outs are already spent in the main chain or in the current block def not_spent + # if we received cached spents, use it + return @spent_outs_txins.empty? if @spent_outs_txins + # find all spent txouts - # OPTIMIZE: these could be fetched in one query for all transactions and cached - next_ins = store.get_txins_for_txouts(tx.in.map.with_index {|txin, idx| [prev_txs[idx].hash, txin.prev_out_index] }) + next_ins = store.get_txins_for_txouts(tx.in.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] }) # no txouts found spending these txins, we can safely return true return true if next_ins.empty? diff --git a/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.bin b/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.bin new file mode 100644 index 00000000..9351d0b0 Binary files /dev/null and b/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.bin differ diff --git a/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.json b/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.json new file mode 100644 index 00000000..9974bbaa --- /dev/null +++ b/spec/bitcoin/fixtures/peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658.json @@ -0,0 +1,41 @@ +{ + "hash":"00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658", + "ver":1, + "prev_block":"0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3", + "mrkl_root":"1bdae84eff34e15399335fc2a48c70fa6b0b9caf972e38b0e3bda106d223f668", + "time":1345400356, + "bits":469827583, + "nonce":1915373966, + "n_tx":1, + "size":267, + "tx":[ + { + "hash":"1bdae84eff34e15399335fc2a48c70fa6b0b9caf972e38b0e3bda106d223f668", + "ver":1, + "vin_sz":1, + "vout_sz":1, + "lock_time":0, + "size":114, + "in":[ + { + "prev_out":{ + "hash":"0000000000000000000000000000000000000000000000000000000000000000", + "n":4294967295 + }, + "coinbase":"04242e3150021a02062f503253482f" + } + ], + "out":[ + { + "value":"24.99750000", + "scriptPubKey":"02e5d9735f12cc4adfce708445c9e109dc945a135377bddf6a6f856757c88eecb3 OP_CHECKSIG" + } + ], + "time":1345399924 + } + ], + "mrkl_tree":[ + "1bdae84eff34e15399335fc2a48c70fa6b0b9caf972e38b0e3bda106d223f668" + ], + "signature":"c1205c2ed92288dfe9fd051c9c1933be7c209dc72ca093bd4d1fb1f5afdae4352002c56aea5b3a730d329821d2f68043a02ae71ae5f9c7946a53c1e3ad69f860c8910021024530" +} \ No newline at end of file diff --git a/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.bin b/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.bin new file mode 100644 index 00000000..61bc300a Binary files /dev/null and b/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.bin differ diff --git a/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.json b/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.json new file mode 100644 index 00000000..7daa2741 --- /dev/null +++ b/spec/bitcoin/fixtures/peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3.json @@ -0,0 +1,41 @@ +{ + "hash":"0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3", + "ver":1, + "prev_block":"0000000000000000000000000000000000000000000000000000000000000000", + "mrkl_root":"3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2", + "time":1345084287, + "bits":486604799, + "nonce":2179302059, + "n_tx":1, + "size":230, + "tx":[ + { + "hash":"3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2", + "ver":1, + "vin_sz":1, + "vout_sz":1, + "lock_time":0, + "size":148, + "in":[ + { + "prev_out":{ + "hash":"0000000000000000000000000000000000000000000000000000000000000000", + "n":4294967295 + }, + "coinbase":"04ffff001d020f274b4d61746f6e69732030372d4155472d3230313220506172616c6c656c2043757272656e6369657320416e642054686520526f61646d617020546f204d6f6e65746172792046726565646f6d" + } + ], + "out":[ + { + "value":"0.00000000", + "scriptPubKey":"" + } + ], + "time":1345083810 + } + ], + "mrkl_tree":[ + "3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2" + ], + "signature":"" +} \ No newline at end of file diff --git a/spec/bitcoin/helpers/fake_blockchain.rb b/spec/bitcoin/helpers/fake_blockchain.rb new file mode 100644 index 00000000..8b8151f4 --- /dev/null +++ b/spec/bitcoin/helpers/fake_blockchain.rb @@ -0,0 +1,183 @@ +require_relative '../spec_helper' +require 'fileutils' + +Bitcoin::NETWORKS[:fake] = { + :project => :bitcoin, + :no_difficulty => true, + :magic_head => "fake", + :address_version => "00", + :p2sh_version => "05", + :privkey_version => "80", + :default_port => 78333, + :coinbase_maturity => 0, + :protocol_version => 70001, + :max_money => 21_000_000 * 100_000_000, + :dns_seeds => [], + :genesis_hash => "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943", + :proof_of_work_limit => 553713663, + :alert_pubkeys => [], + :known_nodes => [], + :checkpoints => {}, + :min_tx_fee => 10_000, + :min_relay_tx_fee => 10_000, +} + + + +# Small utility to generate fake blocks mostly to be able to test performance +# They are full from the start, so that we don't have to import 100K blocks to check +# how performance looks when storing or validating 1K transactions +class FakeBlockchain + + # Initialize fake blockchain and generate +num_blocks+ starting blocks with given + # +opts+ (see #generate). + def initialize(num = 50, opts = {}) + Bitcoin.network = :fake + if File.exist? block_path(0) + genesis = Bitcoin::P::Block.new File.read block_path 0 + Bitcoin.network[:genesis_hash] = genesis.hash + else + STDERR.puts "\nFake blockchain not present, generating (go take a nap)..." + depth = 0 + FileUtils.mkdir_p fixtures_path "fake_chain" + generate(num, opts) do |blk| + File.open(block_path(depth),'w') {|f| f.write blk.to_payload } + depth += 1 + end + end + end + + # Generate fake blockchain with +num+ number of blocks + # Blocks are provided as an argument to the block given to the method + # fake_chain.generate(5) {|b| save_block(b) } + def generate(num = 50, opts = {}) + srand 1337 + + default_opts = { + block_size: 950_000, # minimum block size + num_keys: 1000, # number of different keys being used + genesis_timestamp: Time.new(2009).to_i, + verbose: true, # print out debug information + } + + opts = default_opts.merge(opts) + + to_spend = [] # table of outputs that we can spend + lost_count = 0 # keeping track of lost coins + keys = Array.new(opts[:num_keys]) { Bitcoin::Key.generate } + timestamp = opts[:genesis_timestamp] + + genesis = Bitcoin::Builder.build_block do |blk| + blk.time timestamp + blk.prev_block "00"*32 + blk.tx do |t| + t.input {|i| i.coinbase } + t.output {|o| o.value 50*Bitcoin::COIN; o.script {|s| s.recipient keys[0].addr } } + end + end + Bitcoin.network[:genesis_hash] = genesis.hash + yield(genesis) + + to_spend << {tx: genesis.tx[0], tx_idx: 0, key: keys[0], value: 50e8} + + prev_block = genesis + + + num.times do |blk_i| + + timestamp += 600 + t0 = Time.now + + block = Bitcoin::Builder.build_block do |blk| + blk.time timestamp + blk.prev_block prev_block.hash + key0 = keys.sample + tx0 = blk.tx do |t| + t.input {|i| i.coinbase } + t.output {|o| o.value 50e8; o.script {|s| s.recipient key0.addr } } + end + + # We "lose" some coins, that is we decide never to spend some outputs + # It's to keep utxo growing without making block generation time growing + lost_count += to_spend.size + to_spend = to_spend.reject.with_index {|x,i| i==0 ? false : (((to_spend.size - i) / to_spend.size.to_f)**2 * rand > rand*0.2) } + lost_count -= to_spend.size + + # many txs vs many tx outputs in given block + many_outputs_prob = 0.5 * (rand ** 3) + + total_tx_size = 0 + + # generate tranasctions + loop do + # we want utxo to keep growing so we use many inputs only with some small probability + ins = to_spend[(rand(to_spend.size)).to_i..-1].sample(rand < 0.01 ? (rand(50) + 1) : 1) + total = ins.map{|i| i[:value]}.inject(:+) + next if total < 20_000 + + new_outs = [] + + tx = blk.tx do |t| + + # generate inputs + ins.map do |input| + t.input do |i| + i.prev_out input[:tx] + i.prev_out_index input[:tx_idx] + i.signature_key input[:key] + end + end + # remove outputs that we just used + ins.each {|i| to_spend.delete i} + + fee = 10_000 + # helper to split value randomly in a half + half_split = ->(v) { split = [rand, 0.1].max; [v*split, v*(1-split)] } + # helper to split value randomly to many pieces + value_split = ->(v, depth=0) {(depth < 10 && rand > 0.2) ? half_split[v].map{|x| value_split[x, depth+1]} : [v] } + + if rand < many_outputs_prob + # every now and then there are many outptus + out_values = value_split[total-fee].flatten.map {|x| x.round(8)} + out_values.each.with_index do |v,i| + key = keys.sample + t.output {|o| o.value v; o.script {|s| s.recipient key.addr }} + new_outs << {tx_idx: i, key: key, value: v} + end + else + # most txs seem to have 2 outputs + k1 = keys.sample + k2 = keys.sample + v1, v2 = half_split[total-fee] + t.output {|o| o.value v1; o.script {|s| s.recipient k1.addr }} + t.output {|o| o.value v2; o.script {|s| s.recipient k2.addr }} + new_outs << {tx_idx: 0, key: k1, value: v2} + new_outs << {tx_idx: 1, key: k2, value: v2} + end + end + + new_outs.each {|o| to_spend << {tx: tx}.merge(o) } # fun fact: the opposite merge is way slower + + total_tx_size += tx.to_payload.size + break if total_tx_size > opts[:block_size] + end + + # coinbase + to_spend << {tx: tx0, tx_idx: 0, key: key0, value: 50e8} + end + puts "depth #{blk_i+1}/#{num} \t txcount: #{block.tx.size} \t size: #{block.to_payload.size} \t utxo count: #{to_spend.size + lost_count} (#{to_spend.size}) \t ttg: #{'%.2f' % (Time.now - t0)}s" if opts[:verbose] + yield(block) + prev_block = block + end + true + end + + def block(depth) + Bitcoin::Protocol::Block.new File.read block_path depth + end + + def block_path(depth) + fixtures_path "fake_chain/#{depth}.blk" + end + +end diff --git a/spec/bitcoin/namecoin_spec.rb b/spec/bitcoin/namecoin_spec.rb index d2f6a79a..516994ef 100644 --- a/spec/bitcoin/namecoin_spec.rb +++ b/spec/bitcoin/namecoin_spec.rb @@ -89,7 +89,7 @@ def set_rand rand; @rand = rand; end before do Bitcoin.network = :namecoin - class Bitcoin::Validation::Block; def difficulty; true; end; end + Bitcoin.network[:no_difficulty] = true class Bitcoin::Validation::Block; def min_timestamp; true; end; end Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32) [:name_new, :name_firstupdate, :name_update].each {|type| diff --git a/spec/bitcoin/performance/storage_spec.rb b/spec/bitcoin/performance/storage_spec.rb new file mode 100644 index 00000000..a48e8018 --- /dev/null +++ b/spec/bitcoin/performance/storage_spec.rb @@ -0,0 +1,41 @@ +# encoding: ascii-8bit + +require_relative '../spec_helper' +require_relative '../helpers/fake_blockchain' +require 'benchmark' + +[ + [:sequel, :postgres] +].compact.each do |options| + + next unless storage = setup_db(*options) + + describe "#{storage.backend_name} block storage" do + + before do + @store = storage + @store.reset + @store.log.level = :error + @fake_chain = FakeBlockchain.new 10 + end + + it "block storage" do + blocks = (0..10).to_a.map{|i| @fake_chain.block(i) } + + bm = Benchmark.measure do + bm = Benchmark.bm do |b| + blocks.each.with_index do |blk,i| + b.report("storing fake block ##{i}") do + depth, chain = @store.new_block blk + chain.should == 0 + end + end + end + end + puts '-'*80 + puts "TOTAL #{bm.format}" + end + + + end +end diff --git a/spec/bitcoin/protocol/block_spec.rb b/spec/bitcoin/protocol/block_spec.rb index 129d436e..fe51ffe4 100644 --- a/spec/bitcoin/protocol/block_spec.rb +++ b/spec/bitcoin/protocol/block_spec.rb @@ -153,6 +153,29 @@ .message.should.include?("Block merkle root mismatch!") end + it "should work on peercoin" do + Bitcoin.network = :peercoin # change to peercoin + peercoin_block = "peercoin-genesis-block-0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3" + Bitcoin::Protocol::Block.from_json(fixtures_file(peercoin_block + '.json')) + .to_payload.should == fixtures_file(peercoin_block + '.bin') + + json = Bitcoin::Protocol::Block.new(fixtures_file(peercoin_block + '.bin')).to_json + Bitcoin::Protocol::Block.from_json(json) + .to_payload.should == fixtures_file(peercoin_block + '.bin') + Bitcoin::Protocol::Block.from_json(json).hash == peercoin_block.split("-").last + + peercoin_block = "peercoin-block-00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658" + Bitcoin::Protocol::Block.from_json(fixtures_file(peercoin_block + '.json')) + .to_payload.should == fixtures_file(peercoin_block + '.bin') + + json = Bitcoin::Protocol::Block.new(fixtures_file(peercoin_block + '.bin')).to_json + Bitcoin::Protocol::Block.from_json(json) + .to_payload.should == fixtures_file(peercoin_block + '.bin') + Bitcoin::Protocol::Block.from_json(json).hash == peercoin_block.split("-").last + + + Bitcoin.network = :bitcoin # change back to bitcoin + end end it '#header_to_json' do diff --git a/spec/bitcoin/spec_helper.rb b/spec/bitcoin/spec_helper.rb index a80bace2..960e2182 100644 --- a/spec/bitcoin/spec_helper.rb +++ b/spec/bitcoin/spec_helper.rb @@ -20,11 +20,15 @@ require 'bitcoin' +def fixtures_path(relative_path) + File.join(File.dirname(__FILE__), 'fixtures', relative_path) +end + def fixtures_file(relative_path) - basedir = File.join(File.dirname(__FILE__), 'fixtures') - Bitcoin::Protocol.read_binary_file( File.join(basedir, relative_path) ) + Bitcoin::Protocol.read_binary_file( fixtures_path(relative_path) ) end + include Bitcoin::Builder # create block for given +prev+ block diff --git a/spec/bitcoin/storage/models_spec.rb b/spec/bitcoin/storage/models_spec.rb index 830a2c5b..a71f5dcc 100644 --- a/spec/bitcoin/storage/models_spec.rb +++ b/spec/bitcoin/storage/models_spec.rb @@ -22,7 +22,7 @@ describe "Storage::Models (#{options[0].to_s.capitalize}Store, #{options[1]})" do before do - class Bitcoin::Validation::Block; def difficulty; true; end; end + Bitcoin.network[:no_difficulty] = true Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32) @store = storage @@ -44,12 +44,7 @@ def @store.in_sync?; true; end end after do - class Bitcoin::Validation::Block - def difficulty - return true if Bitcoin.network_name == :testnet3 - block.bits == next_bits_required || [block.bits, next_bits_required] - end - end + Bitcoin.network.delete :no_difficulty end describe "Block" do diff --git a/spec/bitcoin/storage/reorg_spec.rb b/spec/bitcoin/storage/reorg_spec.rb index 018da0b1..55a75968 100644 --- a/spec/bitcoin/storage/reorg_spec.rb +++ b/spec/bitcoin/storage/reorg_spec.rb @@ -208,9 +208,9 @@ def @store.in_sync?; true; end # see https://bitcointalk.org/index.php?topic=46370.0 it "should pass reorg unit tests" do - # Disable difficulty checks. Hackish, should be replaced with some sane API.** - class Bitcoin::Validation::Block; def difficulty; true; end; end Bitcoin.network = :bitcoin + # Disable difficulty check + Bitcoin.network[:no_difficulty] = true @store.import "./spec/bitcoin/fixtures/reorg/blk_0_to_4.dat" @store.get_depth.should == 4 @store.get_head.hash.should =~ /000000002f264d65040/ @@ -228,14 +228,8 @@ class Bitcoin::Validation::Block; def difficulty; true; end; end balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000 balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0 balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000 + Bitcoin.network.delete :no_difficulty Bitcoin.network = :testnet - # Re-enable difficulty checks. Hackish, should be replaced with some sane API. - class Bitcoin::Validation::Block - def difficulty - return true if Bitcoin.network_name == :testnet3 - block.bits == next_bits_required || [block.bits, next_bits_required] - end - end end end diff --git a/spec/bitcoin/storage/storage_spec.rb b/spec/bitcoin/storage/storage_spec.rb index 6103b8e9..6ef4799f 100644 --- a/spec/bitcoin/storage/storage_spec.rb +++ b/spec/bitcoin/storage/storage_spec.rb @@ -24,7 +24,7 @@ describe "Storage::Backends::#{options[0].to_s.capitalize}Store (#{options[1]})" do before do - class Bitcoin::Validation::Block; def difficulty; true; end; end + Bitcoin.network[:no_difficulty] = true Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32) @store = storage @@ -46,12 +46,7 @@ def @store.in_sync?; true; end end after do - class Bitcoin::Validation::Block - def difficulty - return true if Bitcoin.network_name == :testnet3 - block.bits == next_bits_required || [block.bits, next_bits_required] - end - end + Bitcoin.network.delete :no_difficulty end it "should get backend name" do