def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec,
                    address_b58, c_address_b58):
            secret_exponent = int(exp_hex, 16)
            sec = binascii.unhexlify(public_pair_sec)
            c_sec = binascii.unhexlify(c_public_pair_sec)

            self.assertEqual(
                secret_exponent_to_wif(secret_exponent, compressed=False), wif)
            self.assertEqual(
                secret_exponent_to_wif(secret_exponent, compressed=True),
                c_wif)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertFalse(compressed)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                c_wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertTrue(compressed)

            public_pair = public_pair_for_secret_exponent(
                generator_secp256k1, secret_exponent)

            pk_public_pair = public_pair_from_sec(sec)
            compressed = is_sec_compressed(sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertFalse(is_sec_compressed(sec))
            self.assertEqual(
                public_pair_to_sec(pk_public_pair, compressed=False), sec)

            pk_public_pair = public_pair_from_sec(c_sec)
            compressed = is_sec_compressed(c_sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertTrue(compressed)
            self.assertEqual(
                public_pair_to_sec(pk_public_pair, compressed=True), c_sec)

            bca = public_pair_to_bitcoin_address(pk_public_pair,
                                                 compressed=True)
            self.assertEqual(bca, c_address_b58)

            self.assertEqual(
                bitcoin_address_to_ripemd160_sha_sec(c_address_b58),
                public_pair_to_ripemd160_sha_sec(pk_public_pair,
                                                 compressed=True))

            bca = public_pair_to_bitcoin_address(pk_public_pair,
                                                 compressed=False)
            self.assertEqual(bca, address_b58)

            self.assertEqual(
                bitcoin_address_to_ripemd160_sha_sec(address_b58),
                public_pair_to_ripemd160_sha_sec(pk_public_pair,
                                                 compressed=False))
Example #2
0
        def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec,
                    address_b58, c_address_b58):
            secret_exponent = int(exp_hex, 16)
            sec = h2b(public_pair_sec)
            c_sec = h2b(c_public_pair_sec)

            self.assertEqual(
                secret_exponent_to_wif(secret_exponent, compressed=False), wif)
            self.assertEqual(
                secret_exponent_to_wif(secret_exponent, compressed=True),
                c_wif)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertFalse(compressed)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                c_wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertTrue(compressed)

            public_pair = secret_exponent * secp256k1_generator

            pk_public_pair = sec_to_public_pair(sec)
            compressed = is_sec_compressed(sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertFalse(is_sec_compressed(sec))
            self.assertEqual(
                public_pair_to_sec(pk_public_pair, compressed=False), sec)

            pk_public_pair = sec_to_public_pair(c_sec)
            compressed = is_sec_compressed(c_sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertTrue(compressed)
            self.assertEqual(
                public_pair_to_sec(pk_public_pair, compressed=True), c_sec)

            bca = public_pair_to_bitcoin_address(pk_public_pair,
                                                 compressed=True)
            self.assertEqual(bca, c_address_b58)

            self.assertEqual(
                bitcoin_address_to_hash160_sec(c_address_b58),
                public_pair_to_hash160_sec(pk_public_pair, compressed=True))

            bca = public_pair_to_bitcoin_address(pk_public_pair,
                                                 compressed=False)
            self.assertEqual(bca, address_b58)

            self.assertEqual(
                bitcoin_address_to_hash160_sec(address_b58),
                public_pair_to_hash160_sec(pk_public_pair, compressed=False))
Example #3
0
 def from_sec(class_, sec):
     """
     Create a key from an sec bytestream (which is an encoding of a public pair).
     """
     public_pair = sec_to_public_pair(sec)
     return Key(public_pair=public_pair,
                prefer_uncompressed=not is_sec_compressed(sec))
Example #4
0
def pubkey_to_address(pubkey_hex):
    sec = binascii.unhexlify(pubkey_hex)
    compressed = encoding.is_sec_compressed(sec)
    public_pair = encoding.sec_to_public_pair(sec)
    address_prefix = b'\x6f' if config.TESTNET else b'\x00'
    return encoding.public_pair_to_bitcoin_address(
        public_pair, compressed=compressed, address_prefix=address_prefix)
Example #5
0
 def from_sec(class_, sec, netcode=None):
     """
     Create a key from an sec bytestream (which is an encoding of a public pair).
     """
     public_pair = sec_to_public_pair(sec)
     return class_(public_pair=public_pair,
                   is_compressed=is_sec_compressed(sec),
                   netcode=netcode)
Example #6
0
def add_sec_annotations(a1, data, address_prefix):
    pair = sec_to_public_pair(data)
    is_compressed = is_sec_compressed(data)
    a1.append(
        "SEC for %scompressed %s" %
        ("" if is_compressed else "un",
         public_pair_to_bitcoin_address(
             pair, compressed=is_compressed, address_prefix=address_prefix)))
 def do_test(as_public_pair, as_sec, is_compressed, as_hash160_sec, as_bitcoin_address):
     self.assertEqual(encoding.sec_to_public_pair(as_sec), as_public_pair)
     self.assertEqual(encoding.public_pair_to_sec(as_public_pair, compressed=is_compressed), as_sec)
     self.assertEqual(encoding.is_sec_compressed(as_sec), is_compressed)
     self.assertEqual(encoding.public_pair_to_hash160_sec(as_public_pair, compressed=is_compressed),
                      as_hash160_sec)
     self.assertEqual(encoding.hash160_sec_to_bitcoin_address(as_hash160_sec), as_bitcoin_address)
     self.assertEqual(encoding.public_pair_to_bitcoin_address(as_public_pair, compressed=is_compressed), as_bitcoin_address)
     self.assertTrue(encoding.is_valid_bitcoin_address(as_bitcoin_address))
     bad_address = as_bitcoin_address[:17] + chr(ord(as_bitcoin_address[17]) + 1) + as_bitcoin_address[18:]
     self.assertFalse(encoding.is_valid_bitcoin_address(bad_address))
Example #8
0
 def do_test(as_public_pair, as_sec, is_compressed, as_hash160_sec, as_bitcoin_address):
     self.assertEqual(encoding.sec_to_public_pair(as_sec), as_public_pair)
     self.assertEqual(encoding.public_pair_to_sec(as_public_pair, compressed=is_compressed), as_sec)
     self.assertEqual(encoding.is_sec_compressed(as_sec), is_compressed)
     self.assertEqual(encoding.public_pair_to_hash160_sec(as_public_pair, compressed=is_compressed),
                      as_hash160_sec)
     self.assertEqual(encoding.hash160_sec_to_bitcoin_address(as_hash160_sec), as_bitcoin_address)
     self.assertEqual(encoding.public_pair_to_bitcoin_address(as_public_pair, compressed=is_compressed), as_bitcoin_address)
     self.assertTrue(encoding.is_valid_bitcoin_address(as_bitcoin_address))
     bad_address = as_bitcoin_address[:17] + chr(ord(as_bitcoin_address[17]) + 1) + as_bitcoin_address[18:]
     self.assertFalse(encoding.is_valid_bitcoin_address(bad_address))
        def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58):
            secret_exponent = int(exp_hex, 16)
            sec = h2b(public_pair_sec)
            c_sec = h2b(c_public_pair_sec)

            self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=False), wif)
            self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=True), c_wif)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertFalse(compressed)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(c_wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertTrue(compressed)

            public_pair = secret_exponent * secp256k1_generator

            pk_public_pair = sec_to_public_pair(sec)
            compressed = is_sec_compressed(sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertFalse(is_sec_compressed(sec))
            self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=False), sec)

            pk_public_pair = sec_to_public_pair(c_sec)
            compressed = is_sec_compressed(c_sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertTrue(compressed)
            self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=True), c_sec)

            bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True)
            self.assertEqual(bca, c_address_b58)

            self.assertEqual(bitcoin_address_to_hash160_sec(c_address_b58),
                             public_pair_to_hash160_sec(pk_public_pair, compressed=True))

            bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False)
            self.assertEqual(bca, address_b58)

            self.assertEqual(bitcoin_address_to_hash160_sec(address_b58),
                             public_pair_to_hash160_sec(pk_public_pair, compressed=False))
        def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58):
            secret_exponent = int(exp_hex, 16)
            sec = binascii.unhexlify(public_pair_sec)
            c_sec = binascii.unhexlify(c_public_pair_sec)

            self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=False), wif)
            self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=True), c_wif)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertFalse(compressed)

            exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(c_wif)
            self.assertEqual(exponent, secret_exponent)
            self.assertTrue(compressed)

            public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent)

            pk_public_pair = public_pair_from_sec(sec)
            compressed = is_sec_compressed(sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertFalse(is_sec_compressed(sec))
            self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=False), sec)

            pk_public_pair = public_pair_from_sec(c_sec)
            compressed = is_sec_compressed(c_sec)
            self.assertEqual(pk_public_pair, public_pair)
            self.assertTrue(compressed)
            self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=True), c_sec)

            bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True)
            self.assertEqual(bca, c_address_b58)

            self.assertEqual(bitcoin_address_to_ripemd160_sha_sec(c_address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=True))

            bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False)
            self.assertEqual(bca, address_b58)

            self.assertEqual(bitcoin_address_to_ripemd160_sha_sec(address_b58), public_pair_to_ripemd160_sha_sec(pk_public_pair, compressed=False))
Example #11
0
 def address_h160_from_script (script):
     s = disassemble(script).split(' ')
     if 'OP_HASH160' in s:
         p = s.index('OP_HASH160')
         if len(s) > p+1:
             return h2b(s[p+1])
     elif 'OP_CHECKSIG' in s:
         p = s.index('OP_CHECKSIG')
         if len(s[p-1]) in (66, 130):
             # public key
             sec = h2b(s[p-1])
             return public_pair_to_hash160_sec(sec_to_public_pair(sec), is_sec_compressed(sec))
     else:
         logger.warn("Can't parse address from script: %s" % (s))
         return None
Example #12
0
 def bitcoin_address_for_script(self, is_test=False):
     try:
         r = self.match_script_to_templates()
         if len(r) != 1 or len(r[0]) != 2:
             return None
         if r[0][0] == opcodes.OP_PUBKEYHASH:
             return hash160_sec_to_bitcoin_address(r[0][1], is_test=is_test)
         if r[0][0] == opcodes.OP_PUBKEY:
             sec = r[0][1]
             return public_pair_to_bitcoin_address(
                 sec_to_public_pair(sec),
                 compressed=is_sec_compressed(sec),
                 is_test=is_test)
     except SolvingError:
         pass
     return None
Example #13
0
 def bitcoin_address_for_script(self, is_test=False):
     try:
         r = self.match_script_to_templates()
         if len(r) != 1 or len(r[0]) != 2:
             return None
         if r[0][0] == opcodes.OP_PUBKEYHASH:
             return hash160_sec_to_bitcoin_address(r[0][1], is_test=is_test)
         if r[0][0] == opcodes.OP_PUBKEY:
             sec = r[0][1]
             return public_pair_to_bitcoin_address(
                 sec_to_public_pair(sec),
                 compressed=is_sec_compressed(sec),
                 is_test=is_test)
     except SolvingError:
         pass
     return None
Example #14
0
def add_sec_annotations(a1, data, address_prefix):
    pair = sec_to_public_pair(data)
    is_compressed = is_sec_compressed(data)
    a1.append("SEC for %scompressed %s" % (
            "" if is_compressed else "un", public_pair_to_bitcoin_address(
                pair, compressed=is_compressed, address_prefix=address_prefix)))
Example #15
0
def pubkey_to_address(pubkey_hex):
    sec = binascii.unhexlify(pubkey_hex)
    compressed = encoding.is_sec_compressed(sec)
    public_pair = encoding.sec_to_public_pair(sec)
    return encoding.public_pair_to_bitcoin_address(public_pair, compressed=compressed)
Example #16
0
def pubkey_to_address(pubkey_hex):
    sec = binascii.unhexlify(pubkey_hex)
    compressed = encoding.is_sec_compressed(sec)
    public_pair = encoding.sec_to_public_pair(sec)
    return encoding.public_pair_to_bitcoin_address(public_pair,
                                                   compressed=compressed)
Example #17
0
def transaction(tx_info,
                encoding='auto',
                fee_per_kb=config.DEFAULT_FEE_PER_KB,
                regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
                multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
                op_return_value=config.DEFAULT_OP_RETURN_VALUE,
                exact_fee=None,
                fee_provided=0,
                public_key_hex=None,
                allow_unconfirmed_inputs=False):

    (source, destination_outputs, data) = tx_info

    # Data encoding methods.
    if data:
        if encoding == 'auto':
            if len(data) <= 40:
                # encoding = 'opreturn'
                encoding = 'multisig'  # BTCGuild isn’t mining OP_RETURN?!
            else:
                encoding = 'multisig'

        if encoding not in ('pubkeyhash', 'multisig', 'opreturn'):
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

    if exact_fee and not isinstance(exact_fee, int):
        raise exceptions.TransactionError('Exact fees must be in satoshis.')
    if not isinstance(fee_provided, int):
        raise exceptions.TransactionError('Fee provided must be in satoshis.')

    # If public key is necessary for construction of (unsigned) transaction,
    # either use the public key provided, or derive it from a private key
    # retrieved from wallet.
    public_key = None
    if encoding in ('multisig', 'pubkeyhash'):
        # If no public key was provided, derive from private key.
        if not public_key_hex:
            # Get private key.
            if config.UNITTEST:
                private_key_wif = config.UNITTEST_PRIVKEY[source]
            else:
                private_key_wif = rpc('dumpprivkey', [source])

            # Derive public key.
            public_key_hex = private_key_to_public_key(private_key_wif)

        #convert public key hex into public key pair (sec)
        try:
            sec = binascii.unhexlify(public_key_hex)
            is_compressed = is_sec_compressed(sec)
            public_key = sec
        except (EncodingError, binascii.Error):
            raise exceptions.InputError('Invalid private key.')

    # Protocol change.
    if encoding == 'pubkeyhash' and get_block_count(
    ) < 293000 and not config.TESTNET:
        raise exceptions.TransactionError(
            'pubkeyhash encoding unsupported before block 293000')

    # Validate source and all destination addresses.
    destinations = [address for address, value in destination_outputs]
    for address in destinations + [source]:
        if address:
            try:
                base58_decode(address, config.ADDRESSVERSION)
            except Exception:  # TODO
                raise exceptions.AddressError('Invalid Bitcoin address:',
                                              address)

    # Check that the source is in wallet.
    if not config.UNITTEST and encoding in ('multisig') and not public_key:
        if not rpc('validateaddress', [source])['ismine']:
            raise exceptions.AddressError('Not one of your Bitcoin addresses:',
                                          source)

    # Check that the destination output isn't a dust output.
    # Set null values to dust size.
    new_destination_outputs = []
    for address, value in destination_outputs:
        if encoding == 'multisig':
            if value == None: value = multisig_dust_size
            if not value >= multisig_dust_size:
                raise exceptions.TransactionError(
                    'Destination output is below the dust target value.')
        else:
            if value == None: value = regular_dust_size
            if not value >= regular_dust_size:
                raise exceptions.TransactionError(
                    'Destination output is below the dust target value.')
        new_destination_outputs.append((address, value))
    destination_outputs = new_destination_outputs

    # Divide data into chunks.
    if data:

        def chunks(l, n):
            """ Yield successive n‐sized chunks from l.
            """
            for i in range(0, len(l), n):
                yield l[i:i + n]

        if encoding == 'pubkeyhash':
            data_array = list(chunks(data + config.PREFIX,
                                     20 - 1))  # Prefix is also a suffix here.
        elif encoding == 'multisig':
            data_array = list(chunks(data, 33 - 1))
        elif encoding == 'opreturn':
            data_array = list(chunks(data, config.OP_RETURN_MAX_SIZE))
            assert len(
                data_array
            ) == 1  # Only one OP_RETURN output currently supported (OP_RETURN messages should all be shorter than 40 bytes, at the moment).
    else:
        data_array = []

    # Calculate total BTC to be sent.
    btc_out = 0
    if encoding == 'multisig': data_value = multisig_dust_size
    elif encoding == 'opreturn': data_value = op_return_value
    else: data_value = regular_dust_size  # Pay‐to‐PubKeyHash
    btc_out = sum([data_value for data_chunk in data_array])
    btc_out += sum([value for address, value in destination_outputs])

    # Get size of outputs.
    if encoding == 'multisig': data_output_size = 81  # 71 for the data
    elif encoding == 'opreturn': data_output_size = 90  # 80 for the data
    else: data_output_size = 25 + 9  # Pay‐to‐PubKeyHash (25 for the data?)
    outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) *
                                                            data_output_size)

    # Get inputs.
    unspent = get_unspent_txouts(source, normalize=True)
    unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs)
    logging.debug('Sorted UTXOs: {}'.format(
        [print_coin(coin) for coin in unspent]))

    inputs, btc_in = [], 0
    change_quantity = 0
    sufficient_funds = False
    final_fee = fee_per_kb
    for coin in unspent:
        logging.debug('New input: {}'.format(print_coin(coin)))
        inputs.append(coin)
        btc_in += round(coin['amount'] * config.UNIT)

        # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC).
        if exact_fee:
            final_fee = exact_fee
        else:
            size = 181 * len(inputs) + outputs_size + 10
            necessary_fee = (int(size / 1000) + 1) * fee_per_kb
            final_fee = max(fee_provided, necessary_fee)
            assert final_fee >= 1 * fee_per_kb

        # Check if good.
        change_quantity = btc_in - (btc_out + final_fee)
        logging.debug('Change quantity: {} BTC'.format(change_quantity /
                                                       config.UNIT))
        if change_quantity == 0 or change_quantity >= regular_dust_size:  # If change is necessary, must not be a dust output.
            sufficient_funds = True
            break
    if not sufficient_funds:
        # Approximate needed change, fee by with most recently calculated quantities.
        total_btc_out = btc_out + max(change_quantity, 0) + final_fee
        raise exceptions.BalanceError(
            'Insufficient {}s at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`.'
            .format(config.BTC_NAME, source, total_btc_out / config.UNIT,
                    config.BTC))

    # Construct outputs.
    if data: data_output = (data_array, data_value)
    else: data_output = None
    if change_quantity: change_output = (source, change_quantity)
    else: change_output = None

    # Serialise inputs and outputs.
    unsigned_tx = serialise(encoding,
                            inputs,
                            destination_outputs,
                            data_output,
                            change_output,
                            source=source,
                            public_key=public_key)
    unsigned_tx_hex = binascii.hexlify(unsigned_tx).decode('utf-8')
    return unsigned_tx_hex
Example #18
0
def pubkey_to_address(pubkey_hex):
    sec = binascii.unhexlify(pubkey_hex)
    compressed = encoding.is_sec_compressed(sec)
    public_pair = encoding.sec_to_public_pair(sec)
    address_prefix = b'\x6f' if config.TESTNET else b'\x00'
    return encoding.public_pair_to_bitcoin_address(public_pair, compressed=compressed, address_prefix=address_prefix)
Example #19
0
def transaction (db, tx_info, encoding='auto', fee_per_kb=config.DEFAULT_FEE_PER_KB,
                 regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
                 multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
                 op_return_value=config.DEFAULT_OP_RETURN_VALUE,
                 exact_fee=None, fee_provided=0, self_public_key_hex=None,
                 allow_unconfirmed_inputs=False):

    block_index = util.last_block(db)['block_index']
    (source, destination_outputs, data) = tx_info


    '''Destinations'''

    # Destination outputs.
        # Replace multi‐sig addresses with multi‐sig pubkeys. Check that the
        # destination output isn’t a dust output. Set null values to dust size.
    destination_outputs_new = []
    for (address, value) in destination_outputs:

        # Value.
        if util.is_multisig(address):
            dust_size = multisig_dust_size
        else:
            dust_size = regular_dust_size

        if value == None:
            value = dust_size
        elif value < dust_size:
            raise exceptions.TransactionError('Destination output is dust.')

        # Address.
        util.validate_address(address)
        if util.is_multisig(address):
            destination_outputs_new.append((multisig_pubkeyhashes_to_pubkeys(address), value))
        else:
            destination_outputs_new.append((address, value))

    destination_outputs = destination_outputs_new
    destination_btc_out = sum([value for address, value in destination_outputs])


    '''Data'''

    # Data encoding methods (choose and validate).
    if data:
        if encoding == 'auto':
            if len(data) <= config.OP_RETURN_MAX_SIZE:
                # encoding = 'opreturn'
                encoding = 'multisig'   # BTCGuild isn’t mining OP_RETURN?!
            else:
                encoding = 'multisig'

        if encoding not in ('pubkeyhash', 'multisig', 'opreturn'):
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

    if exact_fee and not isinstance(exact_fee, int):
        raise exceptions.TransactionError('Exact fees must be in satoshis.')
    if not isinstance(fee_provided, int):
        raise exceptions.TransactionError('Fee provided must be in satoshis.')

    # Divide data into chunks.
    if data:
        def chunks(l, n):
            """ Yield successive n‐sized chunks from l.
            """
            for i in range(0, len(l), n): yield l[i:i+n]
        if util.enabled('multisig_addresses', block_index):   # Protocol change.
            if encoding == 'pubkeyhash':
                data_array = list(chunks(data, 20 - 1 - 8)) # Prefix is also a suffix here.
            elif encoding == 'multisig':
                data_array = list(chunks(data, (33 * 2) - 1 - 8 - 2 - 2)) # Two pubkeys, minus length byte, minus prefix, minus two nonces, minus two sign bytes
        else:
            data = config.PREFIX + data
            if encoding == 'pubkeyhash':
                data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here.
            elif encoding == 'multisig':
                data_array = list(chunks(data, 33 - 1))
        if encoding == 'opreturn':
            data_array = list(chunks(data, config.OP_RETURN_MAX_SIZE))
            assert len(data_array) == 1 # Only one OP_RETURN output currently supported (OP_RETURN messages should all be shorter than 40 bytes, at the moment).
    else:
        data_array = []

    # Data outputs.
    if encoding == 'multisig': data_value = multisig_dust_size
    elif encoding == 'opreturn': data_value = op_return_value
    else: data_value = regular_dust_size # Pay‐to‐PubKeyHash
    if data: data_output = (data_array, data_value)
    else: data_output = None
    data_btc_out = sum([data_value for data_chunk in data_array])


    '''Inputs'''

    # Source.
        # If public key is necessary for construction of (unsigned)
        # transaction, either use the public key provided, or derive it from a
        # private key retrieved from wallet.
    if source:
        util.validate_address(source)

    self_public_key = None
    if encoding in ('multisig', 'pubkeyhash'):
        if util.is_multisig(source):
            a, self_pubkeys, b = util.extract_array(multisig_pubkeyhashes_to_pubkeys(source))
            self_public_key = binascii.unhexlify(self_pubkeys[0])
        else:
            if not self_public_key_hex:
                # If public key was not provided, derive it from the private key.
                private_key_wif = backend.dumpprivkey(source)
                self_public_key_hex = private_key_to_public_key(private_key_wif)
            else:
                # If public key was provided, check that it matches the source address.
                if source != script.pubkey_to_pubkeyhash(binascii.unhexlify(self_public_key_hex)):
                    raise InputError('provided public key does not match the source address')

            # Convert hex public key into binary public key.
            try:
                self_public_key = binascii.unhexlify(self_public_key_hex)
                is_compressed = is_sec_compressed(self_public_key)
            except (EncodingError, binascii.Error):
                raise InputError('Invalid private key.')

    # Calculate collective size of outputs.
    if encoding == 'multisig': data_output_size = 81        # 71 for the data
    elif encoding == 'opreturn': data_output_size = 90      # 80 for the data
    else: data_output_size = 25 + 9                         # Pay‐to‐PubKeyHash (25 for the data?)
    outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size)

    # Get inputs.
    unspent = get_unspent_txouts(source)
    unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs)
    logging.debug('Sorted UTXOs: {}'.format([print_coin(coin) for coin in unspent]))

    inputs, btc_in = [], 0
    change_quantity = 0
    sufficient_funds = False
    final_fee = fee_per_kb
    for coin in unspent:
        logging.debug('New input: {}'.format(print_coin(coin)))
        inputs.append(coin)
        btc_in += round(coin['amount'] * config.UNIT)

        # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC).
        if exact_fee:
            final_fee = exact_fee
        else:
            size = 181 * len(inputs) + outputs_size + 10
            necessary_fee = (int(size / 1000) + 1) * fee_per_kb
            final_fee = max(fee_provided, necessary_fee)
            assert final_fee >= 1 * fee_per_kb

        # Check if good.
        btc_out = destination_btc_out + data_btc_out
        change_quantity = btc_in - (btc_out + final_fee)
        logging.debug('Change quantity: {} BTC'.format(change_quantity / config.UNIT))
        if change_quantity == 0 or change_quantity >= regular_dust_size: # If change is necessary, must not be a dust output.
            sufficient_funds = True
            break
    if not sufficient_funds:
        # Approximate needed change, fee by with most recently calculated quantities.
        total_btc_out = btc_out + max(change_quantity, 0) + final_fee
        raise BalanceError('Insufficient bitcoins at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`. (Unconfirmed coins cannot be spent from multi‐sig addresses.)'.format(source, total_btc_out / config.UNIT, config.BTC))


    '''Finish'''

    # Change output.
    if util.is_multisig(source):
        change_address = multisig_pubkeyhashes_to_pubkeys(source)
    else:
        change_address = source
    if change_quantity: change_output = (change_address, change_quantity)
    else: change_output = None


    # Serialise inputs and outputs.
    unsigned_tx = serialise(block_index, encoding, inputs, destination_outputs, data_output, change_output, dust_return_public_key=self_public_key)
    unsigned_tx_hex = binascii.hexlify(unsigned_tx).decode('utf-8')

    # Check that the constructed transaction isn’t doing anything funny.
    from lib import blocks
    (desired_source, desired_destination_outputs, desired_data) = tx_info
    desired_source = util.canonical_address(desired_source)
    desired_destination = util.canonical_address(desired_destination_outputs[0][0]) if desired_destination_outputs else ''
    # Include change in destinations for BTC transactions.
    if change_output and not desired_data and desired_destination != config.UNSPENDABLE:
        if desired_destination == '': desired_destination = desired_source
        else: desired_destination += '-{}'.format(desired_source)
    if desired_data == None: desired_data = b''
    parsed_source, parsed_destination, x, y, parsed_data = blocks.get_tx_info2(unsigned_tx_hex)
    if (desired_source, desired_destination, desired_data) != (parsed_source, parsed_destination, parsed_data):
        raise exceptions.TransactionError('constructed transaction does not parse correctly')

    return unsigned_tx_hex
Example #20
0
def transaction (tx_info, encoding='auto', fee_per_kb=config.DEFAULT_FEE_PER_KB,
                 regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
                 multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
                 op_return_value=config.DEFAULT_OP_RETURN_VALUE, exact_fee=None,
                 fee_provided=0, public_key_hex=None,
                 allow_unconfirmed_inputs=False):

    (source, destination_outputs, data) = tx_info

    # Data encoding methods.
    if data:
        if encoding == 'auto':
            if len(data) <= 40:
                # encoding = 'opreturn'
                encoding = 'multisig'   # BTCGuild isn’t mining OP_RETURN?!
            else:
                encoding = 'multisig'

        if encoding not in ('pubkeyhash', 'multisig', 'opreturn'):
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

    if exact_fee and not isinstance(exact_fee, int):
        raise exceptions.TransactionError('Exact fees must be in satoshis.')
    if not isinstance(fee_provided, int):
        raise exceptions.TransactionError('Fee provided must be in satoshis.')

    # If public key is necessary for construction of (unsigned) transaction,
    # either use the public key provided, or derive it from a private key
    # retrieved from wallet.
    public_key = None
    if encoding in ('multisig', 'pubkeyhash'):
        # If no public key was provided, derive from private key.
        if not public_key_hex:
            # Get private key.
            if config.UNITTEST:
                private_key_wif = config.UNITTEST_PRIVKEY[source]
            else:
                private_key_wif = rpc('dumpprivkey', [source])

            # Derive public key.
            public_key_hex = private_key_to_public_key(private_key_wif)

        #convert public key hex into public key pair (sec)
        try:
            sec = binascii.unhexlify(public_key_hex)
            is_compressed = is_sec_compressed(sec)
            public_key = sec
        except (EncodingError, binascii.Error):
            raise exceptions.InputError('Invalid private key.')

    # Protocol change.
    if encoding == 'pubkeyhash' and get_block_count() < 293000 and not config.TESTNET:
        raise exceptions.TransactionError('pubkeyhash encoding unsupported before block 293000')

    # Validate source and all destination addresses.
    destinations = [address for address, value in destination_outputs]
    for address in destinations + [source]:
        if address:
            try:
                base58_decode(address, config.ADDRESSVERSION)
            except Exception:   # TODO
                raise exceptions.AddressError('Invalid Bitcoin address:', address)

    # Check that the source is in wallet.
    if not config.UNITTEST and encoding in ('multisig') and not public_key:
        if not rpc('validateaddress', [source])['ismine']:
            raise exceptions.AddressError('Not one of your Bitcoin addresses:', source)

    # Check that the destination output isn't a dust output.
    # Set null values to dust size.
    new_destination_outputs = []
    for address, value in destination_outputs:
        if encoding == 'multisig':
            if value == None: value = multisig_dust_size
            if not value >= multisig_dust_size:
                raise exceptions.TransactionError('Destination output is below the dust target value.')
        else:
            if value == None: value = regular_dust_size
            if not value >= regular_dust_size:
                raise exceptions.TransactionError('Destination output is below the dust target value.')
        new_destination_outputs.append((address, value))
    destination_outputs = new_destination_outputs

    # Divide data into chunks.
    if data:
        def chunks(l, n):
            """ Yield successive n‐sized chunks from l.
            """
            for i in range(0, len(l), n): yield l[i:i+n]
        if encoding == 'pubkeyhash':
            data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here.
        elif encoding == 'multisig':
            data_array = list(chunks(data, 33 - 1))
        elif encoding == 'opreturn':
            data_array = list(chunks(data, config.OP_RETURN_MAX_SIZE))
            assert len(data_array) == 1 # Only one OP_RETURN output currently supported (OP_RETURN messages should all be shorter than 40 bytes, at the moment).
    else:
        data_array = []

    # Calculate total BTC to be sent.
    btc_out = 0
    if encoding == 'multisig': data_value = multisig_dust_size
    elif encoding == 'opreturn': data_value = op_return_value
    else: data_value = regular_dust_size # Pay‐to‐PubKeyHash
    btc_out = sum([data_value for data_chunk in data_array])
    btc_out += sum([value for address, value in destination_outputs])

    # Get size of outputs.
    if encoding == 'multisig': data_output_size = 81        # 71 for the data
    elif encoding == 'opreturn': data_output_size = 90      # 80 for the data
    else: data_output_size = 25 + 9                         # Pay‐to‐PubKeyHash (25 for the data?)
    outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size)

    # Get inputs.
    unspent = get_unspent_txouts(source, normalize=True)
    unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs)
    logging.debug('Sorted UTXOs: {}'.format([print_coin(coin) for coin in unspent]))

    inputs, btc_in = [], 0
    change_quantity = 0
    sufficient_funds = False
    final_fee = fee_per_kb
    for coin in unspent:
        logging.debug('New input: {}'.format(print_coin(coin)))
        inputs.append(coin)
        btc_in += round(coin['amount'] * config.UNIT)

        # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC).
        if exact_fee:
            final_fee = exact_fee
        else:
            size = 181 * len(inputs) + outputs_size + 10
            necessary_fee = (int(size / 1000) + 1) * fee_per_kb
            final_fee = max(fee_provided, necessary_fee)
            assert final_fee >= 1 * fee_per_kb

        # Check if good.
        change_quantity = btc_in - (btc_out + final_fee)
        logging.debug('Change quantity: {} BTC'.format(change_quantity / config.UNIT))
        if change_quantity == 0 or change_quantity >= regular_dust_size: # If change is necessary, must not be a dust output.
            sufficient_funds = True
            break
    if not sufficient_funds:
        # Approximate needed change, fee by with most recently calculated quantities.
        total_btc_out = btc_out + max(change_quantity, 0) + final_fee
        raise exceptions.BalanceError('Insufficient {}s at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`.'.format(config.BTC_NAME, source, total_btc_out / config.UNIT, config.BTC))

    # Construct outputs.
    if data: data_output = (data_array, data_value)
    else: data_output = None
    if change_quantity: change_output = (source, change_quantity)
    else: change_output = None

    # Serialise inputs and outputs.
    unsigned_tx = serialise(encoding, inputs, destination_outputs, data_output, change_output, source=source, public_key=public_key)
    unsigned_tx_hex = binascii.hexlify(unsigned_tx).decode('utf-8')
    return unsigned_tx_hex
Example #21
0
 def from_sec(class_, sec):
     """
     Create a key from an sec bytestream (which is an encoding of a public pair).
     """
     public_pair = sec_to_public_pair(sec)
     return Key(public_pair=public_pair, prefer_uncompressed=not is_sec_compressed(sec))
Example #22
0
File: Key.py Project: Zibbo/pycoin
 def from_sec(class_, sec, netcode="BTC"):
     """
     Create a key from an sec bytestream (which is an encoding of a public pair).
     """
     public_pair = sec_to_public_pair(sec)
     return class_(public_pair=public_pair, is_compressed=is_sec_compressed(sec), netcode=netcode)
Example #23
0
def transaction (db, tx_info, encoding='auto', fee_per_kb=config.DEFAULT_FEE_PER_KB,
                 regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
                 multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
                 op_return_value=config.DEFAULT_OP_RETURN_VALUE, exact_fee=None,
                 fee_provided=0, self_public_key_hex=None,
                 allow_unconfirmed_inputs=False):

    block_index = util.last_block(db)['block_index']

    (source, destination_outputs, data) = tx_info
    multisig_source = util.is_multisig(source)

    # Data encoding methods.
    if data:
        if encoding == 'auto':
            if len(data) <= config.OP_RETURN_MAX_SIZE:
                # encoding = 'opreturn'
                encoding = 'multisig'   # BTCGuild isn’t mining OP_RETURN?!
            else:
                encoding = 'multisig'

        if encoding not in ('pubkeyhash', 'multisig', 'opreturn'):
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

    if exact_fee and not isinstance(exact_fee, int):
        raise exceptions.TransactionError('Exact fees must be in satoshis.')
    if not isinstance(fee_provided, int):
        raise exceptions.TransactionError('Fee provided must be in satoshis.')

    # If public key is necessary for construction of (unsigned) transaction,
    # either use the public key provided, or derive it from a private key
    # retrieved from wallet.
    self_public_key = None
    if encoding in ('multisig', 'pubkeyhash') and not multisig_source:
        # If no public key was provided, derive from private key.
        if not self_public_key_hex:
            # Get private key.
            private_key_wif = get_private_key(source)

            # Derive public key.
            self_public_key_hex = private_key_to_public_key(private_key_wif)

        #convert public key hex into public key pair (sec)
        try:
            sec = binascii.unhexlify(self_public_key_hex)
            is_compressed = is_sec_compressed(sec)
            self_public_key = sec
        except (EncodingError, binascii.Error):
            raise InputError('Invalid private key.')

    # Protocol change.
    if encoding == 'pubkeyhash' and get_block_count() < 293000 and not config.TESTNET:
        raise exceptions.TransactionError('pubkeyhash encoding unsupported before block 293000')

    # Validate source and destination addresses.
    destinations = [address for address, value in destination_outputs]
    for address in destinations + [source]:
        if address:
            util.validate_address(address, block_index)

    # Check that the source is in wallet.
    if encoding in ('multisig') and not self_public_key and not multisig_source:
        if not is_mine(source):
            raise exceptions.AddressError('Not one of your Bitcoin addresses:', source)

    # Check that the destination output isn't a dust output.
    # Set null values to dust size.
    new_destination_outputs = []
    for address, value in destination_outputs:
        if encoding == 'multisig':
            if value == None: value = multisig_dust_size
            if not value >= multisig_dust_size:
                raise exceptions.TransactionError('Destination output is below the dust target value.')
        else:
            if value == None: value = regular_dust_size
            if not value >= regular_dust_size:
                raise exceptions.TransactionError('Destination output is below the dust target value.')
        new_destination_outputs.append((address, value))
    destination_outputs = new_destination_outputs

    # Divide data into chunks.
    if data:
        def chunks(l, n):
            """ Yield successive n‐sized chunks from l.
            """
            for i in range(0, len(l), n): yield l[i:i+n]
        if util.multisig_enabled(block_index):   # Protocol change.
            if encoding == 'pubkeyhash':
                data_array = list(chunks(data, 20 - 1 - 8)) # Prefix is also a suffix here.
            elif encoding == 'multisig':
                data_array = list(chunks(data, (33 * 2) - 1 - 8 - 2 - 2)) # Two pubkeys, minus length byte, minus prefix, minus two nonces, minus two sign bytes
        else:
            data = config.PREFIX + data
            if encoding == 'pubkeyhash':
                data_array = list(chunks(data + config.PREFIX, 20 - 1)) # Prefix is also a suffix here.
            elif encoding == 'multisig':
                data_array = list(chunks(data, 33 - 1))
        if encoding == 'opreturn':
            data_array = list(chunks(data, config.OP_RETURN_MAX_SIZE))
            assert len(data_array) == 1 # Only one OP_RETURN output currently supported (OP_RETURN messages should all be shorter than 40 bytes, at the moment).
    else:
        data_array = []

    # Calculate total BTC to be sent.
    btc_out = 0
    if encoding == 'multisig': data_value = multisig_dust_size
    elif encoding == 'opreturn': data_value = op_return_value
    else: data_value = regular_dust_size # Pay‐to‐PubKeyHash
    btc_out = sum([data_value for data_chunk in data_array])
    btc_out += sum([value for address, value in destination_outputs])

    # Get size of outputs.
    if encoding == 'multisig': data_output_size = 81        # 71 for the data
    elif encoding == 'opreturn': data_output_size = 90      # 80 for the data
    else: data_output_size = 25 + 9                         # Pay‐to‐PubKeyHash (25 for the data?)
    outputs_size = ((25 + 9) * len(destination_outputs)) + (len(data_array) * data_output_size)

    # Get inputs.
    unspent = get_unspent_txouts(source)
    unspent = sort_unspent_txouts(unspent, allow_unconfirmed_inputs)
    logging.debug('Sorted UTXOs: {}'.format([print_coin(coin) for coin in unspent]))

    inputs, btc_in = [], 0
    change_quantity = 0
    sufficient_funds = False
    final_fee = fee_per_kb
    for coin in unspent:
        logging.debug('New input: {}'.format(print_coin(coin)))
        inputs.append(coin)
        btc_in += round(coin['amount'] * config.UNIT)

        # If exact fee is specified, use that. Otherwise, calculate size of tx and base fee on that (plus provide a minimum fee for selling BTC).
        if exact_fee:
            final_fee = exact_fee
        else:
            size = 181 * len(inputs) + outputs_size + 10
            necessary_fee = (int(size / 1000) + 1) * fee_per_kb
            final_fee = max(fee_provided, necessary_fee)
            assert final_fee >= 1 * fee_per_kb

        # Check if good.
        change_quantity = btc_in - (btc_out + final_fee)
        logging.debug('Change quantity: {} BTC'.format(change_quantity / config.UNIT))
        if change_quantity == 0 or change_quantity >= regular_dust_size: # If change is necessary, must not be a dust output.
            sufficient_funds = True
            break
    if not sufficient_funds:
        # Approximate needed change, fee by with most recently calculated quantities.
        total_btc_out = btc_out + max(change_quantity, 0) + final_fee
        raise BalanceError('Insufficient bitcoins at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`. (Unconfirmed coins cannot be spent from multi‐sig addresses.)'.format(source, total_btc_out / config.UNIT, config.BTC))

    # Destination outputs. (Replace multi‐sig addresses with multi‐sig pubkeys.)
    destination_outputs_new = []
    for (destination, value) in destination_outputs:
        if util.is_multisig(destination):
            destination_outputs_new.append((multisig_pubkeyhashes_to_pubkeys(destination), value))
        else:
            destination_outputs_new.append((destination, value))
    destination_outputs = destination_outputs_new

    # Data outputs.
    if data: data_output = (data_array, data_value)
    else: data_output = None

    # Change output. (Change address is source address.)
    if util.is_multisig(source):
        change_address = multisig_pubkeyhashes_to_pubkeys(source)
    else:
        change_address = source
    if change_quantity: change_output = (change_address, change_quantity)
    else: change_output = None

    # Get `self_public_key`, if multi‐sig (for then it’s not passed as an argument).
    if multisig_source:
        a, self_pubkeys, b = util.extract_array(multisig_pubkeyhashes_to_pubkeys(source))
        self_public_key = binascii.unhexlify(self_pubkeys[0])

    # Serialise inputs and outputs.
    unsigned_tx = serialise(block_index, encoding, inputs, destination_outputs, data_output, change_output, self_public_key=self_public_key)
    unsigned_tx_hex = binascii.hexlify(unsigned_tx).decode('utf-8')

    # Check that the constructed transaction isn’t doing anything funny.
    from lib import blocks
    (desired_source, desired_destination_outputs, desired_data) = tx_info
    desired_source = util.canonical_address(desired_source)
    desired_destination = util.canonical_address(desired_destination_outputs[0][0]) if desired_destination_outputs else ''
    # Include change in destinations for BTC transactions.
    if change_output and not desired_data and desired_destination != config.UNSPENDABLE:
        if desired_destination == '': desired_destination = desired_source
        else: desired_destination += '-{}'.format(desired_source)
    if desired_data == None: desired_data = b''
    parsed_source, parsed_destination, x, y, parsed_data = blocks.get_tx_info2(unsigned_tx_hex)
    if (desired_source, desired_destination, desired_data) != (parsed_source, parsed_destination, parsed_data):
        raise exceptions.TransactionError('constructed transaction does not parse correctly')

    return unsigned_tx_hex