MIN_RELAY_TX_FEE = 10000
-class Transaction:
-
- def __init__(self, raw):
- self.raw = raw
- self.deserialize()
- self.inputs = self.d['inputs']
- self.outputs = self.d['outputs']
- self.outputs = map(lambda x: (x['address'],x['value']), self.outputs)
- self.input_info = None
- self.is_complete = True
-
- @classmethod
- def from_io(klass, inputs, outputs):
- raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
- self = klass(raw)
- self.is_complete = False
- self.inputs = inputs
- self.outputs = outputs
- extras = []
- for i in self.inputs:
- e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') }
- extras.append(e)
- self.input_info = extras
- return self
-
- def __str__(self):
- return self.raw
-
- @classmethod
- def multisig_script(klass, public_keys, num=None):
- n = len(public_keys)
- if num is None: num = n
- # supports only "2 of 2", and "2 of 3" transactions
- assert num <= n and n in [2,3]
-
- if num==2:
- s = '52'
- elif num == 3:
- s = '53'
- else:
- raise
-
- for k in public_keys:
- s += var_int(len(k)/2)
- s += k
- if n==2:
- s += '52'
- elif n==3:
- s += '53'
- else:
- raise
- s += 'ae'
-
- return s
-
- @classmethod
- def serialize( klass, inputs, outputs, for_sig = None ):
-
- s = int_to_hex(1,4) # version
- s += var_int( len(inputs) ) # number of inputs
- for i in range(len(inputs)):
- txin = inputs[i]
- s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash
- s += int_to_hex(txin['index'],4) # prev index
-
- if for_sig is None:
- signatures = txin['signatures']
- pubkeys = txin['pubkeys']
- if not txin.get('redeemScript'):
- pubkey = pubkeys[0]
- sig = signatures[0]
- sig = sig + '01' # hashtype
- script = op_push(len(sig)/2)
- script += sig
- script += op_push(len(pubkey)/2)
- script += pubkey
- else:
- script = '00' # op_0
- for sig in signatures:
- sig = sig + '01'
- script += op_push(len(sig)/2)
- script += sig
-
- redeem_script = klass.multisig_script(pubkeys,2)
- script += op_push(len(redeem_script)/2)
- script += redeem_script
-
- elif for_sig==i:
- if txin.get('redeemScript'):
- script = txin['redeemScript'] # p2sh uses the inner script
- else:
- script = txin['raw_output_script'] # scriptsig
- else:
- script=''
- s += var_int( len(script)/2 ) # script length
- s += script
- s += "ffffffff" # sequence
-
- s += var_int( len(outputs) ) # number of outputs
- for output in outputs:
- addr, amount = output
- s += int_to_hex( amount, 8) # amount
- addrtype, hash_160 = bc_address_to_hash_160(addr)
- if addrtype == 0:
- script = '76a9' # op_dup, op_hash_160
- script += '14' # push 0x14 bytes
- script += hash_160.encode('hex')
- script += '88ac' # op_equalverify, op_checksig
- elif addrtype == 5:
- script = 'a9' # op_hash_160
- script += '14' # push 0x14 bytes
- script += hash_160.encode('hex')
- script += '87' # op_equal
- else:
- raise
-
- s += var_int( len(script)/2 ) # script length
- s += script # script
- s += int_to_hex(0,4) # lock time
- if for_sig is not None and for_sig != -1:
- s += int_to_hex(1, 4) # hash type
- return s
-
-
- def for_sig(self,i):
- return self.serialize(self.inputs, self.outputs, for_sig = i)
-
-
- def hash(self):
- return Hash(self.raw.decode('hex') )[::-1].encode('hex')
-
-
-
- def sign(self, keypairs):
- import deserialize
- is_complete = True
- print_error("tx.sign(), keypairs:", keypairs)
-
- for i, txin in enumerate(self.inputs):
-
- # if the input is multisig, parse redeem script
- redeem_script = txin.get('redeemScript')
- num, redeem_pubkeys = deserialize.parse_redeemScript(redeem_script) if redeem_script else (1, [txin.get('redeemPubkey')])
-
- # add pubkeys
- txin["pubkeys"] = redeem_pubkeys
- # get list of already existing signatures
- signatures = txin.get("signatures",[])
- # continue if this txin is complete
- if len(signatures) == num:
- continue
-
- tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
- for pubkey in redeem_pubkeys:
- # check if we have the corresponding private key
- if pubkey in keypairs.keys():
- # add signature
- sec = keypairs[pubkey]
- compressed = is_compressed(sec)
- pkey = regenerate_key(sec)
- secexp = pkey.secret
- private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
- public_key = private_key.get_verifying_key()
- sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
- assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
- signatures.append( sig.encode('hex') )
- print_error("adding signature for", pubkey)
-
- txin["signatures"] = signatures
- is_complete = is_complete and len(signatures) == num
-
- self.is_complete = is_complete
- self.raw = self.serialize( self.inputs, self.outputs )
-
-
- def deserialize(self):
- import deserialize
- vds = deserialize.BCDataStream()
- vds.write(self.raw.decode('hex'))
- self.d = deserialize.parse_Transaction(vds)
- return self.d
-
-
- def has_address(self, addr):
- found = False
- for txin in self.inputs:
- if addr == txin.get('address'):
- found = True
- break
- for txout in self.outputs:
- if addr == txout[0]:
- found = True
- break
- return found
-
-
- def get_value(self, addresses, prevout_values):
- # return the balance for that tx
- is_relevant = False
- is_send = False
- is_pruned = False
- is_partial = False
- v_in = v_out = v_out_mine = 0
-
- for item in self.inputs:
- addr = item.get('address')
- if addr in addresses:
- is_send = True
- is_relevant = True
- key = item['prevout_hash'] + ':%d'%item['prevout_n']
- value = prevout_values.get( key )
- if value is None:
- is_pruned = True
- else:
- v_in += value
- else:
- is_partial = True
-
- if not is_send: is_partial = False
-
- for item in self.outputs:
- addr, value = item
- v_out += value
- if addr in addresses:
- v_out_mine += value
- is_relevant = True
-
- if is_pruned:
- # some inputs are mine:
- fee = None
- if is_send:
- v = v_out_mine - v_out
- else:
- # no input is mine
- v = v_out_mine
-
- else:
- v = v_out_mine - v_in
-
- if is_partial:
- # some inputs are mine, but not all
- fee = None
- is_send = v < 0
- else:
- # all inputs are mine
- fee = v_out - v_in
-
- return is_relevant, is_send, v, fee
-
- def as_dict(self):
- import json
- out = {
- "hex":self.raw,
- "complete":self.is_complete
- }
- if not self.is_complete:
- extras = []
- for i in self.inputs:
- e = { 'txid':i['tx_hash'], 'vout':i['index'],
- 'scriptPubKey':i.get('raw_output_script'),
- 'KeyID':i.get('KeyID'),
- 'redeemScript':i.get('redeemScript'),
- 'signatures':i.get('signatures'),
- 'pubkeys':i.get('pubkeys'),
- }
- extras.append(e)
- self.input_info = extras
-
- if self.input_info:
- out['input_info'] = json.dumps(self.input_info).replace(' ','')
-
- return out
-
-
- def requires_fee(self, verifier):
- # see https://en.bitcoin.it/wiki/Transaction_fees
- threshold = 57600000
- size = len(self.raw)/2
- if size >= 10000:
- return True
-
- for o in self.outputs:
- value = o[1]
- if value < 1000000:
- return True
- sum = 0
- for i in self.inputs:
- age = verifier.get_confirmations(i["tx_hash"])[0]
- sum += i["value"] * age
- priority = sum / size
- print_error(priority, threshold)
- return priority < threshold
-
-
def test_bip32(seed, sequence):