Exemple #1
0
 def _get_address(self, coin, address_n, multisig):
     if multisig:
         # check if we own the pubkey
         pubkey = BIP32(self.storage.get_node()).get_public_node(address_n).public_key
         try:
             pubkeys = [ public_ckd(n.node, list(n.address_n)).public_key for n in multisig.pubkeys ]
             sig_index = list(pubkeys).index(pubkey)
         except ValueError:
             return proto.Failure(code=proto_types.Failure_Other, message="Pubkey not found in multisig script")
         # convert script to P2SH address
         script = transaction.compile_script_multisig(multisig)
         h160 = tools.hash_160(script)
         address = tools.hash_160_to_bc_address(h160, coin.address_type_p2sh)
     else:
         address = BIP32(self.storage.get_node()).get_address(coin, address_n)
     self.layout.show_receiving_address(address)
     self.custom_message = True  # Yes button will redraw screen
     return proto.Address(address=address)
Exemple #2
0
def compile_script_multisig(multisig):
    # P2SH

    def n_to_op(n):
        if n < 1 or n > 15:
            raise Exception("Unsupported multisig type")
        return chr(0x50 + n)
        
    n = len(multisig.pubkeys)
    m = multisig.m
    
    script = n_to_op(m)

    for node in multisig.pubkeys:
        pubkey = public_ckd(node.node, list(node.address_n)).public_key

        script += op_push(len(pubkey))
        script += pubkey
    
    script += n_to_op(n)
    script += '\xae' # OP_CHECKMULTISIG
    
    return script
Exemple #3
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)
        tx_multisig_fingerprint = None

        # 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:
            multisig_fp = None
            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 ret2.tx.inputs[0].HasField('multisig'):
                    fp = multisig_fingerprint(ret2.tx.inputs[0].multisig)
                    if multisig_fp == None:
                        multisig_fp = fp
                    else:
                        if multisig_fp != fp:
                            multisig_fp = ''

                # 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)
                    if ret2.tx.inputs[0].script_type == proto_types.SPENDMULTISIG:
                        sign.serialize_input_multisig(ret2.tx.inputs[0], ret2.tx.inputs[0].multisig, secexp)
                    else:
                        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"))

                is_change = False

                # 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')
                    is_change = True

                if out.HasField('multisig'):
                    if multisig_fp != None and multisig_fp != '' and \
                       multisig_fingerprint(out.multisig) == multisig_fp:
                        is_change = True

                if is_change:
                    if out_change == None:
                        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
                    # confirm outputs (if not change and not op_return)
                    if out_change != o2 and out.script_type != proto_types.PAYTOOPRETURN:
                        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()

            if inp.script_type == proto_types.SPENDMULTISIG:
                # We're doing partial signature for multisig input
                signatures = inp.multisig.signatures

                # Find position of actual signature in 'signatures' list
                try:
                    pubkeys = [ public_ckd(n.node, list(n.address_n)).public_key for n in inp.multisig.pubkeys ]
                    sig_index = list(pubkeys).index(pubkey)
                except ValueError:
                    raise Exception(proto.Failure(code=proto_types.Failure_Other, message="Pubkey not found in multisig script"))

                # Put signature to proper place
                signatures[sig_index] = signature

                serialized_tx += outtx.serialize_input_multisig(inp, signatures, inp.multisig)

            else:
                # Standard paytoaddress input
                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, 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))