Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ tmp/
log/
rdoc/
coverage/
spec/bitcoin/fixtures/fake_chain
*.conf
*.db
.rbx/
Expand Down
34 changes: 34 additions & 0 deletions lib/bitcoin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions lib/bitcoin/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion lib/bitcoin/network/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions lib/bitcoin/protocol/block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions lib/bitcoin/protocol/tx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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) }
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
19 changes: 8 additions & 11 deletions lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
26 changes: 20 additions & 6 deletions lib/bitcoin/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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"
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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":""
}
Loading