Class: Bitcoin::Wallet::TxDP

Inherits:
Object
  • Object
show all
Defined in:
lib/bitcoin/wallet/txdp.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tx = []) ⇒ TxDP

Returns a new instance of TxDP.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/bitcoin/wallet/txdp.rb', line 6

def initialize tx = []
  @id = Bitcoin.int_to_base58(rand(1e14))
  @tx = tx
  @inputs = []
  return  unless tx.any?
  @tx[0].in.each_with_index do |input, i|
    prev_out_hash = input.prev_out.reverse_hth
    prev_tx = @tx[1..-1].find {|tx| tx.hash == prev_out_hash}
    raise "prev tx #{prev_out_hash} not found"  unless prev_tx
    prev_out = prev_tx.out[input.prev_out_index]
    raise "prev out ##{input.prev_out_index} not found in tx #{@tx.hash}"  unless prev_out
    out_script = Bitcoin::Script.new(prev_out.pk_script)
    out_script.get_addresses.each do |addr|
      add_sig(i, prev_out.value, addr, input.script_sig)
    end
  end
end

Instance Attribute Details

#idObject

Returns the value of attribute id.



5
6
7
# File 'lib/bitcoin/wallet/txdp.rb', line 5

def id
  @id
end

#inputsObject

Returns the value of attribute inputs.



5
6
7
# File 'lib/bitcoin/wallet/txdp.rb', line 5

def inputs
  @inputs
end

#txObject

Returns the value of attribute tx.



5
6
7
# File 'lib/bitcoin/wallet/txdp.rb', line 5

def tx
  @tx
end

Class Method Details

.parse(str) ⇒ Object



115
116
117
# File 'lib/bitcoin/wallet/txdp.rb', line 115

def self.parse str
  new.parse str
end

Instance Method Details

#add_sig(in_idx, value, addr, sig) ⇒ Object



24
25
26
27
# File 'lib/bitcoin/wallet/txdp.rb', line 24

def add_sig(in_idx, value, addr, sig)
  sig = sig ? [[addr, sig.unpack("H*")[0]]] : []
  @inputs[in_idx] = [value, sig]
end

#parse(str) ⇒ Object



72
73
74
75
76
77
78
79
80
81
# File 'lib/bitcoin/wallet/txdp.rb', line 72

def parse str
  str.match(/-+BEGIN-TRANSACTION-(.*?)-+$(.*?)END-TRANSACTION-#{$1}/m) do |m|
    _, id, content = *m
    txdist, *inputs = content.split(/_TXINPUT_/)
    @id = id
    @txdist = parse_txdist(txdist)
    inputs.each {|input| parse_input(input) }
  end
  self
end

#parse_input(input) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/bitcoin/wallet/txdp.rb', line 95

def parse_input input
  m = input.match(/(\d+)_(\d+\.\d+)\n(.*)/m)
  _, idx, value, sigs = *m
  value = (value.sub('.','').to_i)
  sigs = parse_sigs(sigs)
  @inputs[idx.to_i] = [value, sigs]
end

#parse_sigs(sigs) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/bitcoin/wallet/txdp.rb', line 103

def parse_sigs sigs
  return nil  unless sigs["_SIG_"]
  sigs = sigs.split("_SIG_").map do |s|
    if s == ""
      nil
    else
      m = s.match(/(.*?)_(\d+)_(.*?)\n(.*)/m)
      [$1, $4.gsub("\n", '').gsub('-', '')]
    end
  end.compact
end

#parse_txdist(txdist) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/bitcoin/wallet/txdp.rb', line 83

def parse_txdist txdist
  _, magic, txdp_id, size, serialized_tx = *txdist.match(/_TXDIST_(.*?)_(.*?)_(.*?)$(.*)/m)
  raise "Wrong network magic"  unless [magic].pack("H*") == Bitcoin.network[:magic_head]
  tx = Bitcoin::P::Tx.new(nil)
  rest = [serialized_tx.gsub!("\n", '')].pack("H*")
  while rest = tx.parse_data(rest)
    @tx << tx
    break  if rest == true
    tx = Bitcoin::P::Tx.new(nil)
  end
end

#serializeObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/bitcoin/wallet/txdp.rb', line 49

def serialize
  lines = []
  lines << "-----BEGIN-TRANSACTION-#{@id}".ljust(80, '-')
  size = [@tx.first.to_payload.bytesize].pack("C").ljust(2, "\x00").reverse_hth
  lines << "_TXDIST_#{Bitcoin.network[:magic_head].unpack("H*")[0]}_#{@id}_#{size}"
  tx = @tx.map(&:to_payload).join.unpack("H*")[0]
  tx_str = ""; tx.split('').each_with_index{|c,i| tx_str << (i % 80 == 0 ? "\n#{c}" : c)}
  lines << tx_str.strip
  @inputs.each_with_index do |input, idx|
    lines << "_TXINPUT_#{idx.to_s.rjust(2, '0')}_#{"%.8f" % (input[0].to_f / 1e8)}"
    next  unless input[1]
    input[1].each do |sig|
      size = [sig[1]].pack("H*").bytesize
      size = [size].pack("C").ljust(2, "\x00").reverse_hth
      lines << "_SIG_#{sig[0]}_#{idx.to_s.rjust(2, '0')}_#{size}"
      sig_str = ""; sig[1].split('').each_with_index{|c,i| sig_str << (i % 80 == 0 ? "\n#{c}" : c)}
      lines << sig_str.strip
    end
  end
  lines << "-------END-TRANSACTION-#{@id}".ljust(80, '-')
  lines.join("\n")
end

#sign_inputsObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/bitcoin/wallet/txdp.rb', line 29

def sign_inputs
  @inputs.each_with_index do |txin, i|
    input = @tx[0].in[i]
    prev_out_hash = input.prev_out.reverse_hth
    prev_tx = @tx[1..-1].find {|tx| tx.hash == prev_out_hash}
    raise "prev tx #{prev_out_hash} not found"  unless prev_tx
    prev_out = prev_tx.out[input.prev_out_index]
    raise "prev out ##{input.prev_out_index} not found in tx #{@tx.hash}"  unless prev_out
    out_script = Bitcoin::Script.new(prev_out.pk_script)
    out_script.get_addresses.each do |addr|
      sig = yield(@tx[0], prev_tx, i, addr)
      if sig
        @inputs[i][1] ||= []
        @inputs[i][1] << [addr, sig]
        break
      end
    end
  end
end