def do_sign(self, msg): # Basic checks passed, let's sign that shit! version = 1 lock_time = 0 serialized_tx = '' coin = coindef.types[msg.coin_name] outtx = StreamTransactionSerialize(len(msg.inputs), len(msg.outputs), version, lock_time) # Sign inputs index = 0 self.layout.show_progress(index, len(msg.inputs), clear=True, logo=logo) for inp in msg.inputs: self.layout.show_progress(index, len(msg.inputs), clear=False) tx = StreamTransactionSign(index, len(msg.inputs), len(msg.outputs), version, lock_time) for i in msg.inputs: print '.', if i == inp: address = self.bip32.get_address(coin, list(i.address_n)) private_key = self.bip32.get_private_node(list(i.address_n)).private_key print "ADDRESS", address print "PRIVKEY", binascii.hexlify(private_key) secexp = string_to_number(private_key) tx.serialize_input(i, address, secexp) else: tx.serialize_input(i) for o in msg.outputs: print '.', tx.serialize_output(compile_TxOutput(o)) (signature, pubkey) = tx.sign() serialized_tx += outtx.serialize_input(inp, signature, pubkey) print "SIGNATURE", binascii.hexlify(signature) print "PUBKEY", binascii.hexlify(pubkey) index += 1 for out in msg.outputs: print '.', serialized_tx += outtx.serialize_output(compile_TxOutput(out)) self.layout.show_logo() self.set_main_state() return proto.TxRequest(request_type=proto_types.TXFINISHED, serialized=proto_types.TxRequestSerializedType(serialized_tx=serialized_tx))
def workflow(self, msg): if msg.inputs_count < 1: raise Exception(proto.Failure(message='Transaction must have at least one input')) if msg.outputs_count < 1: raise Exception(proto.Failure(message='Transaction must have at least one output')) bip32 = BIP32(self.iface.storage.get_node()) coin = coindef.types[msg.coin_name] version = 1 lock_time = 0 serialized_tx = '' signature = None checkhash = None to_spend = 0 spending = 0 change_amount = 0 outtx = StreamTransactionSerialize(msg.inputs_count, msg.outputs_count, version, lock_time) # foreach I: for i in range(msg.inputs_count): # Request I req = proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=i)) # Fill values from previous round if i > 0: req.serialized.serialized_tx = serialized_tx req.serialized.signature = signature req.serialized.signature_index = i - 1 serialized_tx = '' ret = yield req inp = ret.tx.inputs[0] # ----------- Calculate amount of I: amount = None # Request prevhash I, META ret = yield(proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType( tx_hash=inp.prev_hash))) amount_hash = StreamTransactionHash(ret.tx.inputs_cnt, ret.tx.outputs_cnt, version, lock_time) # foreach prevhash I: for i2 in range(ret.tx.inputs_cnt): # Request prevhash I ret2 = yield(proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType( request_index=i2, tx_hash=inp.prev_hash))) amount_hash.serialize_input(ret2.tx.inputs[0]) # foreach prevhash O: for o2 in range(ret.tx.outputs_cnt): # Request prevhash O ret2 = yield(proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType( request_index=o2, tx_hash=inp.prev_hash))) amount_hash.serialize_output(ret2.tx.bin_outputs[0]) if inp.prev_index == o2: # Store amount of I amount = ret2.tx.bin_outputs[0].amount to_spend += amount # Calculate hash of streamed tx, compare to prevhash I if inp.prev_hash != amount_hash.calc_txid()[::-1]: raise Exception(proto.Failure(message="Provided input data doesn't match to prev_hash")) # ------------- End of streaming amounts # Add META to StreamTransactionSign sign = StreamTransactionSign(i, msg.inputs_count, msg.outputs_count, version, lock_time) # Calculate hash for each input, then compare to checkhash check = StreamTransactionHash(msg.inputs_count, msg.outputs_count, version, lock_time, True) # foreach I: for i2 in range(msg.inputs_count): # Request I ret2 = yield(proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=i2))) check.serialize_input(ret2.tx.inputs[0]) # If I == I-to-be-signed: if i2 == i: # Fill scriptsig address = bip32.get_address(coin, list(ret2.tx.inputs[0].address_n)) private_key = bip32.get_private_node(list(ret2.tx.inputs[0].address_n)).private_key print "ADDRESS", address print "PRIVKEY", binascii.hexlify(private_key) secexp = string_to_number(private_key) sign.serialize_input(ret2.tx.inputs[0], address, secexp) else: # Add I to StreamTransactionSign sign.serialize_input(ret2.tx.inputs[0]) # foreach O: out_change = None for o2 in range(msg.outputs_count): # Request O ret2 = yield(proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=o2))) out = ret2.tx.outputs[0] if len(list(out.address_n)) and out.HasField('address'): raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Cannot have both address and address_n for the output")) # Calculate proper address for given address_n if len(list(out.address_n)): if out_change == None: out.address = bip32.get_address(coin, list(out.address_n)) out.ClearField('address_n') out_change = o2 # Remember which output is supposed to be a change else: raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Only one change output allowed")) if i == 0: change_amount = out.amount # If I=0: if i == 0: spending += out.amount print "SENDING", out.amount, "TO", out.address if out_change != o2: # confirm non change output self.iface.layout.show_output(coin, out.address, out.amount) ret = yield proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput) if not isinstance(ret, proto.ButtonAck): raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Signing aborted")) check.serialize_output(compile_TxOutput(out)) # Add O to StreamTransactionSign sign.serialize_output(compile_TxOutput(out)) if i == 0: checkhash = check.calc_txid() else: if check.calc_txid() != checkhash: raise Exception(proto.Failure(message='Serialization check failed')) # Sign StreamTransactionSign (signature, pubkey) = sign.sign() serialized_tx += outtx.serialize_input(inp, signature, pubkey) print "SIGNATURE", binascii.hexlify(signature) print "PUBKEY", binascii.hexlify(pubkey) if spending > to_spend: raise Exception(proto.Failure(code=proto_types.Failure_NotEnoughFunds, message="Not enough funds")) est_size = estimate_size_kb(msg.inputs_count, msg.outputs_count) maxfee = coin.maxfee_kb * est_size fee = to_spend - spending if fee > maxfee: self.iface.layout.show_high_fee(fee, coin) ret = yield proto.ButtonRequest(code=proto_types.ButtonRequest_FeeOverThreshold) if not isinstance(ret, proto.ButtonAck): raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Signing aborted")) self.iface.layout.show_send_tx(to_spend - change_amount - fee, coin) ret = yield proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx) if not isinstance(ret, proto.ButtonAck): raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Signing aborted")) # Serialize outputs for o2 in range(msg.outputs_count): # Request O req = proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=o2), serialized=proto_types.TxRequestSerializedType(serialized_tx=serialized_tx)) if o2 == 0: # Fill signature of last input req.serialized.signature = signature req.serialized.signature_index = i serialized_tx = '' ret2 = yield req out = ret2.tx.outputs[0] if len(list(out.address_n)) and out.HasField('address'): raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Cannot have both address and address_n for the output")) # Calculate proper address for given address_n if len(list(out.address_n)): out.address = bip32.get_address(coin, list(out.address_n)) out.ClearField('address_n') serialized_tx += outtx.serialize_output(compile_TxOutput(out)) yield proto.TxRequest(request_type=proto_types.TXFINISHED, serialized=proto_types.TxRequestSerializedType(serialized_tx=serialized_tx))