Class: OnChain::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/onchain/transaction.rb

Class Method Summary collapse

Class Method Details

.add_fee_to_tx(fee, fee_addr, tx) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/onchain/transaction.rb', line 43

def add_fee_to_tx(fee, fee_addr, tx)
  
  # Add wallet fee
  if fee > 0 and (fee - 10000) > 0
    
    # Take the miners fee from the wallet fees
    fee = fee - 10000
    
    # Check for affiliate
    if fee_addr.kind_of?(Array)
      affil_fee = fee / 2
      txout1 = Bitcoin::Protocol::TxOut.new(affil_fee, Bitcoin::Script.to_address_script(fee_addr[0]))
      txout2 = Bitcoin::Protocol::TxOut.new(affil_fee, Bitcoin::Script.to_address_script(fee_addr[1]))
      tx.add_out(txout1)
      tx.add_out(txout2)
    else
      txout = Bitcoin::Protocol::TxOut.new(fee, Bitcoin::Script.to_address_script(fee_addr))
      tx.add_out(txout)
    end
  end
  
end

.calculate_fee(amount, fee_percent, min_fee_satoshi) ⇒ Object



115
116
117
118
119
120
121
122
123
124
# File 'lib/onchain/transaction.rb', line 115

def calculate_fee(amount, fee_percent, min_fee_satoshi)
  
  fee = (amount * (fee_percent / 100.0)).to_i
  
  if fee < min_fee_satoshi
    return min_fee_satoshi
  end
  
  return fee
end

.create_single_address_transaction(orig_addr, dest_addr, amount, fee_percent, fee_addr, min_fee_satoshi) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/onchain/transaction.rb', line 4

def create_single_address_transaction(orig_addr, dest_addr, amount, fee_percent, fee_addr, min_fee_satoshi)

  tx = Bitcoin::Protocol::Tx.new
  
  fee = calculate_fee(amount, fee_percent, min_fee_satoshi)
  
  total_amount = amount + fee
  
  unspents, indexes, change = OnChain::BlockChain.get_unspent_for_amount(
    [orig_addr], total_amount)

  
  # Process the unpsent outs.
  unspents.each_with_index do |spent, index|

    txin = Bitcoin::Protocol::TxIn.new([ spent[0] ].pack('H*').reverse, spent[1])
    txin.script_sig = OnChain.hex_to_bin(spent[2])
    tx.add_in(txin)
  end
  txout = Bitcoin::Protocol::TxOut.new(amount, Bitcoin::Script.to_address_script(dest_addr))
  
  tx.add_out(txout)
  
  # Add wallet fee
  add_fee_to_tx(fee, fee_addr, tx)

  # Send the change back.
  if change > 0
    
    txout = Bitcoin::Protocol::TxOut.new(change, Bitcoin::Script.to_address_script(orig_addr))
  
    tx.add_out(txout)
  end

  inputs_to_sign = get_inputs_to_sign(tx)
  
  return OnChain::bin_to_hex(tx.to_payload), inputs_to_sign
end

.create_transaction(redemption_scripts, address, amount_in_satoshi, miners_fee) ⇒ Object

Given a send address and an amount produce a transaction and a list of hashes that need to be signed.

The transaction will be in hex format.

The list of hashes that need to be signed will be in this format

[input index]=> { :hash => hash }

i.e.

0][:hash => ‘345435345…’
0][:hash => ‘122133445.…’


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/onchain/transaction.rb', line 173

def create_transaction(redemption_scripts, address, amount_in_satoshi, miners_fee)

  total_amount = miners_fee
  
  total_amount = total_amount + amount_in_satoshi
  
  addresses = redemption_scripts.map { |rs| 
    OnChain::Sweeper.generate_address_of_redemption_script(rs)
  }
  
  unspents, indexes, change = OnChain::BlockChain.get_unspent_for_amount(addresses, total_amount)
  
  # OK, let's build a transaction.
  tx = Bitcoin::Protocol::Tx.new
  
  # Process the unpsent outs.
  unspents.each_with_index do |spent, index|

    script = redemption_scripts[indexes[index]]
    
    txin = Bitcoin::Protocol::TxIn.new([ spent[0] ].pack('H*').reverse, spent[1])
    txin.script_sig = OnChain::hex_to_bin(script)
    tx.add_in(txin)
  end
  
  # Do we have enough in the fund.
  #if(total_amount > btc_balance)
  #  raise 'Balance is not enough to cover payment'
  #end

  txout = Bitcoin::Protocol::TxOut.new(amount_in_satoshi, 
      Bitcoin::Script.to_address_script(address))
  
  tx.add_out(txout)
  
  change_address = addresses[0]

  # Send the change back.
  if change > 0
  
    txout = Bitcoin::Protocol::TxOut.new(change, 
      Bitcoin::Script.to_address_script(change_address))
  
    tx.add_out(txout)
  end

  inputs_to_sign = get_inputs_to_sign tx

  return OnChain::bin_to_hex(tx.to_payload), inputs_to_sign
end

.create_transaction_with_fee(redemption_scripts, address, amount, fee_percent, fee_addr) ⇒ Object

Like create_single_address_transaction but for multi sig wallets.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/onchain/transaction.rb', line 67

def create_transaction_with_fee(redemption_scripts, address, amount, fee_percent, fee_addr)

  fee = calculate_fee(amount, fee_percent)
  
  total_amount = amount + fee
  
  addresses = redemption_scripts.map { |rs| 
    OnChain::Sweeper.generate_address_of_redemption_script(rs)
  }
  
  unspents, indexes, change = OnChain::BlockChain.get_unspent_for_amount(addresses, total_amount)
  
  # OK, let's build a transaction.
  tx = Bitcoin::Protocol::Tx.new
  
  # Process the unpsent outs.
  unspents.each_with_index do |spent, index|

    script = redemption_scripts[indexes[index]]
    
    txin = Bitcoin::Protocol::TxIn.new([ spent[0] ].pack('H*').reverse, spent[1])
    txin.script_sig = OnChain::hex_to_bin(script)
    tx.add_in(txin)
  end
  
  # Add wallet fee
  add_fee_to_tx(fee, fee_addr, tx)

  txout = Bitcoin::Protocol::TxOut.new(amount, Bitcoin::Script.to_address_script(address))
  
  tx.add_out(txout)
  
  change_address = addresses[0]

  # Send the change back.
  if change > 0
  
    txout = Bitcoin::Protocol::TxOut.new(change, 
      Bitcoin::Script.to_address_script(change_address))
  
    tx.add_out(txout)
  end

  inputs_to_sign = get_inputs_to_sign tx

  return OnChain::bin_to_hex(tx.to_payload), inputs_to_sign
end

.do_we_have_all_the_signatures(sig_list) ⇒ Object

Run through the signature list and check it is all signed.



270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/onchain/transaction.rb', line 270

def do_we_have_all_the_signatures(sig_list)
  
  sig_list.each do |input|
    input.each_key do |public_key|
      if input[public_key]['hash'] == nil or input[public_key]['sig'] == nil
        return false
      end
    end
  end
  
  return true
end

.get_inputs_to_sign(tx) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/onchain/transaction.rb', line 140

def get_inputs_to_sign(tx)
  inputs_to_sign = []
  tx.in.each_with_index do |txin, index|
    hash = tx.signature_hash_for_input(index, txin.script, 1)
    
    script = Bitcoin::Script.new txin.script
    
    pubkeys = get_public_keys_from_script(script)
    pubkeys.each do |key|
      
      if inputs_to_sign[index] == nil
        inputs_to_sign[index] = {}
      end
      inputs_to_sign[index][key] = {'hash' => OnChain::bin_to_hex(hash)}
    end
  end
  return inputs_to_sign
end

.get_public_keys_from_script(script) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/onchain/transaction.rb', line 126

def get_public_keys_from_script(script)

  if script.is_hash160?
    return [Bitcoin.hash160_to_address(script.get_hash160)]
  end
  
  pubs = []
  script.get_multisig_pubkeys.each do |pub|
    pub_hex = OnChain.bin_to_hex(pub)
    pubs << Bitcoin.hash160_to_address(Bitcoin.hash160(pub_hex))
  end
  return pubs
end

.sign_transaction(transaction_hex, sig_list, pubkey = nil) ⇒ Object

Given a transaction in hex string format, apply the given signature list to it.

Signatures should be in the format

[0]=> {’hash’ => ‘345435345.…’, ‘sig’ => ‘435fgdf4553…’} [0]=> {’hash’ => ‘122133445.…’, ‘sig’ => ‘435fgdf4553…’}



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/onchain/transaction.rb', line 232

def sign_transaction(transaction_hex, sig_list, pubkey = nil)
  
  tx = Bitcoin::Protocol::Tx.new OnChain::hex_to_bin(transaction_hex)
  
  tx.in.each_with_index do |txin, index|
    
    sigs = []
    
    rscript = Bitcoin::Script.new txin.script
    
    pub_keys = get_public_keys_from_script(rscript)
    pub_keys.each do |hkey|
      
      if sig_list[index][hkey] != nil and sig_list[index][hkey]['sig'] != nil
        
        # Add the signature to the list.
        sigs << OnChain.hex_to_bin(sig_list[index][hkey]['sig'])
        
      end
    end
    
    if sigs.count > 0
      in_script = Bitcoin::Script.new txin.script
      if in_script.is_hash160?
        sig = sigs[0]
        txin.script = Bitcoin::Script.to_pubkey_script_sig(sig, OnChain.hex_to_bin(pubkey))
      else
        txin.script = Bitcoin::Script.to_p2sh_multisig_script_sig(rscript.to_payload, sigs)
      end
    end
  
    #raise "Signature error " + index.to_s  if ! tx.verify_input_signature(index, in_script.to_payload)
  end
  
  return OnChain::bin_to_hex(tx.to_payload)
end