示例#1
0
    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))
示例#2
0
    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))