<commit_before>"""
Craft, sign and broadcast Bitcoin transactions.
Interface with Bitcoind.
"""

import os
import sys
import binascii
import json
import hashlib
import re
import time
import decimal
import logging

import requests
from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent
from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec, is_sec_compressed, EncodingError
from Crypto.Cipher import ARC4
from bitcoin.core.script import CScript
from bitcoin.core import x
from bitcoin.core.key import CPubKey

from . import (config, exceptions, util, blockchain, script)

class InputError (Exception):
    pass

# Constants
OP_RETURN = b'\x6a'
OP_PUSHDATA1 = b'\x4c'
OP_DUP = b'\x76'
OP_HASH160 = b'\xa9'
OP_EQUALVERIFY = b'\x88'
OP_CHECKSIG = b'\xac'
OP_1 = b'\x51'
OP_2 = b'\x52'
OP_3 = b'\x53'
OP_CHECKMULTISIG = b'\xae'

D = decimal.Decimal

def pubkeyhash_to_pubkey(pubkeyhash):
    # TODO: convert to python-bitcoinlib.
    raw_transactions = blockchain.searchrawtransactions(pubkeyhash)
    for tx in raw_transactions:
        for vin in tx['vin']:
            scriptsig = vin['scriptSig']
            asm = scriptsig['asm'].split(' ')
            pubkey = asm[1]
            if pubkeyhash == script.pubkey_to_pubkeyhash(binascii.unhexlify(bytes(pubkey, 'utf-8'))):
                return pubkey
    raise exceptions.AddressError('Public key for address ‘{}’ not published in blockchain.'.format(pubkeyhash))
def multisig_pubkeyhashes_to_pubkeys(address):
    signatures_required, pubkeyhashes, signatures_possible = util.extract_array(address)
    pubkeys = [pubkeyhash_to_pubkey(pubkeyhash) for pubkeyhash in pubkeyhashes]
    return util.construct_array(signatures_required, pubkeys, signatures_possible)

def print_coin(coin):
    return 'amount: {}; txid: {}; vout: {}; confirmations: {}'.format(coin['amount'], coin['txid'], coin['vout'], coin.get('confirmations', '?')) # simplify and make deterministic


# COMMON
def get_block_count():
    return int(util.rpc('getblockcount', []))
def get_block_hash(block_index):
    return util.rpc('getblockhash', [block_index])
def get_raw_transaction (tx_hash, verbose=1):
    return util.rpc('getrawtransaction', [tx_hash, verbose])
def get_block (block_hash):
    return util.rpc('getblock', [block_hash])
def get_block_hash (block_index):
    return util.rpc('getblockhash', [block_index])
def decode_raw_transaction (unsigned_tx_hex):
    return util.rpc('decoderawtransaction', [unsigned_tx_hex])
def get_info():
    return util.rpc('getinfo', [])

# UNCOMMON
def is_valid (address):
    return util.rpc('validateaddress', [address])['isvalid']
def is_mine (address):
    return util.rpc('validateaddress', [address])['ismine']
def sign_raw_transaction (unsigned_tx_hex):
    return util.rpc('signrawtransaction', [unsigned_tx_hex])
def send_raw_transaction (tx_hex):
    return util.rpc('sendrawtransaction', [tx_hex])
def get_private_key (address):
    return util.rpc('dumpprivkey', [address])

def get_wallet ():
    for group in util.rpc('listaddressgroupings', []):
        for bunch in group:
            yield bunch
def get_mempool ():
    return util.rpc('getrawmempool', [])
def list_unspent ():
    return util.rpc('listunspent', [0, 999999])


def var_int (i):
    if i < 0xfd:
        return (i).to_bytes(1, byteorder='little')
    elif i <= 0xffff:
        return b'\xfd' + (i).to_bytes(2, byteorder='little')
    elif i <= 0xffffffff:
        return b'\xfe' + (i).to_bytes(4, byteorder='little')
    else:
        return b'\xff' + (i).to_bytes(8, byteorder='little')

def op_push (i):
    if i < 0x4c:
        return (i).to_bytes(1, byteorder='little')              # Push i bytes.
    elif i <= 0xff:
        return b'\x4c' + (i).to_bytes(1, byteorder='little')    # OP_PUSHDATA1
    elif i <= 0xffff:
        return b'\x4d' + (i).to_bytes(2, byteorder='little')    # OP_PUSHDATA2
    else:
        return b'\x4e' + (i).to_bytes(4, byteorder='little')    # OP_PUSHDATA4


def get_multisig_script(address):

    # Unpack multi‐sig address.
    signatures_required, pubkeys, signatures_possible = util.extract_array(address)

    # Required signatures.
    if signatures_required == 1:
        op_required = OP_1
    elif signatures_required == 2:
        op_required = OP_2
    elif signatures_required == 3:
        op_required = OP_3
    else:
        raise InputError('Required signatures must be 1, 2 or 3.')

    # Required signatures.
    if signatures_possible == 1:
        op_total = OP_1
    elif signatures_possible == 2:
        op_total = OP_2
    elif signatures_possible == 3:
        op_total = OP_3
    else:
        raise InputError('Total possible signatures must be 1, 2 or 3.')

    # Construct script.
    script = op_required                                # Required signatures
    for public_key in pubkeys:
        public_key = binascii.unhexlify(public_key)
        script += op_push(len(public_key))              # Push bytes of public key
        script += public_key                            # Data chunk (fake) public key
    script += op_total                                  # Total signatures
    script += OP_CHECKMULTISIG                          # OP_CHECKMULTISIG

    return script

def get_monosig_script(address):

    # Construct script.
    pubkeyhash = util.base58_check_decode(address, config.ADDRESSVERSION)
    script = OP_DUP                                     # OP_DUP
    script += OP_HASH160                                # OP_HASH160
    script += op_push(20)                               # Push 0x14 bytes
    script += pubkeyhash                                # pubKeyHash
    script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
    script += OP_CHECKSIG                               # OP_CHECKSIG

    return script

def make_fully_valid(pubkey):
    assert len(pubkey) == 31    # One sign byte and one nonce byte required (for 33 bytes).

    cpubkey = CPubKey(b'')
    random_bytes = hashlib.sha256(pubkey).digest()      # Deterministically generated, for unit tests.
    sign = (random_bytes[0] & 0b1) + 2                  # 0x02 or 0x03
    nonce = initial_nonce = random_bytes[1]

    while not cpubkey.is_fullyvalid:
        # Increment nonce.
        nonce += 1
        assert nonce != initial_nonce

        # Construct a possibly fully valid public key.
        possibly_fully_valid_pubkey = bytes([sign]) + pubkey + bytes([nonce % 256])
        cpubkey = CPubKey(possibly_fully_valid_pubkey)

    fully_valid_pubkey = possibly_fully_valid_pubkey
    assert len(fully_valid_pubkey) == 33
    return fully_valid_pubkey


def serialise (block_index, encoding, inputs, destination_outputs, data_output=None, change_output=None, dust_return_public_key=None):
    s  = (1).to_bytes(4, byteorder='little')                # Version

    # Number of inputs.
    s += var_int(int(len(inputs)))

    # List of Inputs.
    for i in range(len(inputs)):
        txin = inputs[i]
        s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1]         # TxOutHash
        s += txin['vout'].to_bytes(4, byteorder='little')   # TxOutIndex

        script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(script)))                      # Script length
        s += script                                         # Script
        s += b'\xff' * 4                                    # Sequence

    # Number of outputs.
    n = 0
    n += len(destination_outputs)
    if data_output:
        data_array, value = data_output
        for data_chunk in data_array: n += 1
    else:
        data_array = []
    if change_output: n += 1
    s += var_int(n)

    # Destination output.
    for destination, value in destination_outputs:
        s += value.to_bytes(8, byteorder='little')          # Value

        if util.is_multisig(destination):
            script = get_multisig_script(destination)
        else:
            script = get_monosig_script(destination)

        s += var_int(int(len(script)))                      # Script length
        s += script

    # Data output.
    for data_chunk in data_array:
        data_array, value = data_output
        s += value.to_bytes(8, byteorder='little')        # Value

        if util.enabled('multisig_addresses', block_index):   # Protocol change.
            data_chunk = config.PREFIX + data_chunk

        # Initialise encryption key (once per output).
        key = ARC4.new(binascii.unhexlify(inputs[0]['txid']))  # Arbitrary, easy‐to‐find, unique key.

        if encoding == 'multisig':
            # Get data (fake) public key.
            if util.enabled('multisig_addresses', block_index):   # Protocol change.
                pad_length = (33 * 2) - 1 - 2 - 2 - len(data_chunk)
                assert pad_length >= 0
                data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
                data_chunk = key.encrypt(data_chunk)
                data_pubkey_1 = make_fully_valid(data_chunk[:31])
                data_pubkey_2 = make_fully_valid(data_chunk[31:])

                # Construct script.
                script = OP_1                                   # OP_1
                script += op_push(33)                           # Push bytes of data chunk (fake) public key    (1/2)
                script += data_pubkey_1                         # (Fake) public key                  (1/2)
                script += op_push(33)                           # Push bytes of data chunk (fake) public key    (2/2)
                script += data_pubkey_2                         # (Fake) public key                  (2/2)
                script += op_push(len(dust_return_public_key))  # Push bytes of source public key
                script += dust_return_public_key                       # Source public key
                script += OP_3                                  # OP_3
                script += OP_CHECKMULTISIG                      # OP_CHECKMULTISIG
            else:
                pad_length = 33 - 1 - len(data_chunk)
                assert pad_length >= 0
                data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
                # Construct script.
                script = OP_1                                   # OP_1
                script += op_push(len(dust_return_public_key))  # Push bytes of source public key
                script += dust_return_public_key                       # Source public key
                script += op_push(len(data_chunk))              # Push bytes of data chunk (fake) public key
                script += data_chunk                            # (Fake) public key
                script += OP_2                                  # OP_2
                script += OP_CHECKMULTISIG                      # OP_CHECKMULTISIG
        elif encoding == 'opreturn':
            if util.enabled('multisig_addresses', block_index):   # Protocol change.
                data_chunk = key.encrypt(data_chunk)
            script = OP_RETURN                                  # OP_RETURN
            script += op_push(len(data_chunk))                  # Push bytes of data chunk (NOTE: OP_SMALLDATA?)
            script += data_chunk                                # Data
        elif encoding == 'pubkeyhash':
            pad_length = 20 - 1 - len(data_chunk)
            assert pad_length >= 0
            data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
            data_chunk = key.encrypt(data_chunk)
            # Construct script.
            script = OP_DUP                                     # OP_DUP
            script += OP_HASH160                                # OP_HASH160
            script += op_push(20)                               # Push 0x14 bytes
            script += data_chunk                                # (Fake) pubKeyHash
            script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
            script += OP_CHECKSIG                               # OP_CHECKSIG
        else:
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

        s += var_int(int(len(script)))                      # Script length
        s += script

    # Change output.
    if change_output:
        change_address, change_value = change_output
        s += change_value.to_bytes(8, byteorder='little')   # Value

        if util.is_multisig(change_address):
            script = get_multisig_script(change_address)
        else:
            script = get_monosig_script(change_address)

        s += var_int(int(len(script)))                      # Script length
        s += script

    s += (0).to_bytes(4, byteorder='little')                # LockTime
    return s

def input_value_weight(amount):
    # Prefer outputs less than dust size, then bigger is better.
    if amount * config.UNIT <= config.DEFAULT_REGULAR_DUST_SIZE:
        return 0
    else:
        return 1 / amount

def sort_unspent_txouts(unspent, allow_unconfirmed_inputs):
    # Get deterministic results (for multiAPIConsensus type requirements), sort by timestamp and vout index.
    # (Oldest to newest so the nodes don’t have to be exactly caught up to each other for consensus to be achieved.)
    # searchrawtransactions doesn’t support unconfirmed transactions
    try:
        unspent = sorted(unspent, key=util.sortkeypicker(['ts', 'vout']))
    except KeyError: # If timestamp isn’t given.
        pass

    # Sort by amount.
    unspent = sorted(unspent,key=lambda x:input_value_weight(x['amount']))

    # Remove unconfirmed txouts, if desired.
    if allow_unconfirmed_inputs:
        # Hackish: Allow only inputs which are either already confirmed or were seen only recently. (Skip outputs from slow‐to‐confirm transanctions.)
        try:
            unspent = [coin for coin in unspent if (coin['confirmations'] > 0 or (time.time() - coin['ts']) < 6 * 3600)] # Cutoff: six hours
        except (KeyError, TypeError):
            pass
    else:
        unspent = [coin for coin in unspent if coin['confirmations'] > 0]

    return unspent
class AltcoinSupportError (Exception): pass
def private_key_to_public_key (private_key_wif):
    if config.TESTNET:
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET]
    else:
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET]
    try:
        secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes)
    except EncodingError:
        raise AltcoinSupportError('pycoin: unsupported WIF prefix')
    public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent)
    public_key = public_pair_to_sec(public_pair, compressed=compressed)
    public_key_hex = binascii.hexlify(public_key).decode('utf-8')
    return public_key_hex

class BalanceError (Exception): pass
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

    # 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.')


    '''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 encoding == 'multisig':  # TODO: shoud be `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'''<commit_msg>minor bug [breaks test suite]<commit_after>"""
Craft, sign and broadcast Bitcoin transactions.
Interface with Bitcoind.
"""

import os
import sys
import binascii
import json
import hashlib
import re
import time
import decimal
import logging

import requests
from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent
from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec, is_sec_compressed, EncodingError
from Crypto.Cipher import ARC4
from bitcoin.core.script import CScript
from bitcoin.core import x
from bitcoin.core.key import CPubKey

from . import (config, exceptions, util, blockchain, script)

class InputError (Exception):
    pass

# Constants
OP_RETURN = b'\x6a'
OP_PUSHDATA1 = b'\x4c'
OP_DUP = b'\x76'
OP_HASH160 = b'\xa9'
OP_EQUALVERIFY = b'\x88'
OP_CHECKSIG = b'\xac'
OP_1 = b'\x51'
OP_2 = b'\x52'
OP_3 = b'\x53'
OP_CHECKMULTISIG = b'\xae'

D = decimal.Decimal

def pubkeyhash_to_pubkey(pubkeyhash):
    # TODO: convert to python-bitcoinlib.
    raw_transactions = blockchain.searchrawtransactions(pubkeyhash)
    for tx in raw_transactions:
        for vin in tx['vin']:
            scriptsig = vin['scriptSig']
            asm = scriptsig['asm'].split(' ')
            pubkey = asm[1]
            if pubkeyhash == script.pubkey_to_pubkeyhash(binascii.unhexlify(bytes(pubkey, 'utf-8'))):
                return pubkey
    raise exceptions.AddressError('Public key for address ‘{}’ not published in blockchain.'.format(pubkeyhash))
def multisig_pubkeyhashes_to_pubkeys(address):
    signatures_required, pubkeyhashes, signatures_possible = util.extract_array(address)
    pubkeys = [pubkeyhash_to_pubkey(pubkeyhash) for pubkeyhash in pubkeyhashes]
    return util.construct_array(signatures_required, pubkeys, signatures_possible)

def print_coin(coin):
    return 'amount: {}; txid: {}; vout: {}; confirmations: {}'.format(coin['amount'], coin['txid'], coin['vout'], coin.get('confirmations', '?')) # simplify and make deterministic


# COMMON
def get_block_count():
    return int(util.rpc('getblockcount', []))
def get_block_hash(block_index):
    return util.rpc('getblockhash', [block_index])
def get_raw_transaction (tx_hash, verbose=1):
    return util.rpc('getrawtransaction', [tx_hash, verbose])
def get_block (block_hash):
    return util.rpc('getblock', [block_hash])
def get_block_hash (block_index):
    return util.rpc('getblockhash', [block_index])
def decode_raw_transaction (unsigned_tx_hex):
    return util.rpc('decoderawtransaction', [unsigned_tx_hex])
def get_info():
    return util.rpc('getinfo', [])

# UNCOMMON
def is_valid (address):
    return util.rpc('validateaddress', [address])['isvalid']
def is_mine (address):
    return util.rpc('validateaddress', [address])['ismine']
def sign_raw_transaction (unsigned_tx_hex):
    return util.rpc('signrawtransaction', [unsigned_tx_hex])
def send_raw_transaction (tx_hex):
    return util.rpc('sendrawtransaction', [tx_hex])
def get_private_key (address):
    return util.rpc('dumpprivkey', [address])

def get_wallet ():
    for group in util.rpc('listaddressgroupings', []):
        for bunch in group:
            yield bunch
def get_mempool ():
    return util.rpc('getrawmempool', [])
def list_unspent ():
    return util.rpc('listunspent', [0, 999999])


def var_int (i):
    if i < 0xfd:
        return (i).to_bytes(1, byteorder='little')
    elif i <= 0xffff:
        return b'\xfd' + (i).to_bytes(2, byteorder='little')
    elif i <= 0xffffffff:
        return b'\xfe' + (i).to_bytes(4, byteorder='little')
    else:
        return b'\xff' + (i).to_bytes(8, byteorder='little')

def op_push (i):
    if i < 0x4c:
        return (i).to_bytes(1, byteorder='little')              # Push i bytes.
    elif i <= 0xff:
        return b'\x4c' + (i).to_bytes(1, byteorder='little')    # OP_PUSHDATA1
    elif i <= 0xffff:
        return b'\x4d' + (i).to_bytes(2, byteorder='little')    # OP_PUSHDATA2
    else:
        return b'\x4e' + (i).to_bytes(4, byteorder='little')    # OP_PUSHDATA4


def get_multisig_script(address):

    # Unpack multi‐sig address.
    signatures_required, pubkeys, signatures_possible = util.extract_array(address)

    # Required signatures.
    if signatures_required == 1:
        op_required = OP_1
    elif signatures_required == 2:
        op_required = OP_2
    elif signatures_required == 3:
        op_required = OP_3
    else:
        raise InputError('Required signatures must be 1, 2 or 3.')

    # Required signatures.
    if signatures_possible == 1:
        op_total = OP_1
    elif signatures_possible == 2:
        op_total = OP_2
    elif signatures_possible == 3:
        op_total = OP_3
    else:
        raise InputError('Total possible signatures must be 1, 2 or 3.')

    # Construct script.
    script = op_required                                # Required signatures
    for public_key in pubkeys:
        public_key = binascii.unhexlify(public_key)
        script += op_push(len(public_key))              # Push bytes of public key
        script += public_key                            # Data chunk (fake) public key
    script += op_total                                  # Total signatures
    script += OP_CHECKMULTISIG                          # OP_CHECKMULTISIG

    return script

def get_monosig_script(address):

    # Construct script.
    pubkeyhash = util.base58_check_decode(address, config.ADDRESSVERSION)
    script = OP_DUP                                     # OP_DUP
    script += OP_HASH160                                # OP_HASH160
    script += op_push(20)                               # Push 0x14 bytes
    script += pubkeyhash                                # pubKeyHash
    script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
    script += OP_CHECKSIG                               # OP_CHECKSIG

    return script

def make_fully_valid(pubkey):
    assert len(pubkey) == 31    # One sign byte and one nonce byte required (for 33 bytes).

    cpubkey = CPubKey(b'')
    random_bytes = hashlib.sha256(pubkey).digest()      # Deterministically generated, for unit tests.
    sign = (random_bytes[0] & 0b1) + 2                  # 0x02 or 0x03
    nonce = initial_nonce = random_bytes[1]

    while not cpubkey.is_fullyvalid:
        # Increment nonce.
        nonce += 1
        assert nonce != initial_nonce

        # Construct a possibly fully valid public key.
        possibly_fully_valid_pubkey = bytes([sign]) + pubkey + bytes([nonce % 256])
        cpubkey = CPubKey(possibly_fully_valid_pubkey)

    fully_valid_pubkey = possibly_fully_valid_pubkey
    assert len(fully_valid_pubkey) == 33
    return fully_valid_pubkey


def serialise (block_index, encoding, inputs, destination_outputs, data_output=None, change_output=None, dust_return_public_key=None):
    s  = (1).to_bytes(4, byteorder='little')                # Version

    # Number of inputs.
    s += var_int(int(len(inputs)))

    # List of Inputs.
    for i in range(len(inputs)):
        txin = inputs[i]
        s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1]         # TxOutHash
        s += txin['vout'].to_bytes(4, byteorder='little')   # TxOutIndex

        script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(script)))                      # Script length
        s += script                                         # Script
        s += b'\xff' * 4                                    # Sequence

    # Number of outputs.
    n = 0
    n += len(destination_outputs)
    if data_output:
        data_array, value = data_output
        for data_chunk in data_array: n += 1
    else:
        data_array = []
    if change_output: n += 1
    s += var_int(n)

    # Destination output.
    for destination, value in destination_outputs:
        s += value.to_bytes(8, byteorder='little')          # Value

        if util.is_multisig(destination):
            script = get_multisig_script(destination)
        else:
            script = get_monosig_script(destination)

        s += var_int(int(len(script)))                      # Script length
        s += script

    # Data output.
    for data_chunk in data_array:
        data_array, value = data_output
        s += value.to_bytes(8, byteorder='little')        # Value

        if util.enabled('multisig_addresses', block_index):   # Protocol change.
            data_chunk = config.PREFIX + data_chunk

        # Initialise encryption key (once per output).
        key = ARC4.new(binascii.unhexlify(inputs[0]['txid']))  # Arbitrary, easy‐to‐find, unique key.

        if encoding == 'multisig':
            # Get data (fake) public key.
            if util.enabled('multisig_addresses', block_index):   # Protocol change.
                pad_length = (33 * 2) - 1 - 2 - 2 - len(data_chunk)
                assert pad_length >= 0
                data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
                data_chunk = key.encrypt(data_chunk)
                data_pubkey_1 = make_fully_valid(data_chunk[:31])
                data_pubkey_2 = make_fully_valid(data_chunk[31:])

                # Construct script.
                script = OP_1                                   # OP_1
                script += op_push(33)                           # Push bytes of data chunk (fake) public key    (1/2)
                script += data_pubkey_1                         # (Fake) public key                  (1/2)
                script += op_push(33)                           # Push bytes of data chunk (fake) public key    (2/2)
                script += data_pubkey_2                         # (Fake) public key                  (2/2)
                script += op_push(len(dust_return_public_key))  # Push bytes of source public key
                script += dust_return_public_key                       # Source public key
                script += OP_3                                  # OP_3
                script += OP_CHECKMULTISIG                      # OP_CHECKMULTISIG
            else:
                pad_length = 33 - 1 - len(data_chunk)
                assert pad_length >= 0
                data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
                # Construct script.
                script = OP_1                                   # OP_1
                script += op_push(len(dust_return_public_key))  # Push bytes of source public key
                script += dust_return_public_key                       # Source public key
                script += op_push(len(data_chunk))              # Push bytes of data chunk (fake) public key
                script += data_chunk                            # (Fake) public key
                script += OP_2                                  # OP_2
                script += OP_CHECKMULTISIG                      # OP_CHECKMULTISIG
        elif encoding == 'opreturn':
            if util.enabled('multisig_addresses', block_index):   # Protocol change.
                data_chunk = key.encrypt(data_chunk)
            script = OP_RETURN                                  # OP_RETURN
            script += op_push(len(data_chunk))                  # Push bytes of data chunk (NOTE: OP_SMALLDATA?)
            script += data_chunk                                # Data
        elif encoding == 'pubkeyhash':
            pad_length = 20 - 1 - len(data_chunk)
            assert pad_length >= 0
            data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
            data_chunk = key.encrypt(data_chunk)
            # Construct script.
            script = OP_DUP                                     # OP_DUP
            script += OP_HASH160                                # OP_HASH160
            script += op_push(20)                               # Push 0x14 bytes
            script += data_chunk                                # (Fake) pubKeyHash
            script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
            script += OP_CHECKSIG                               # OP_CHECKSIG
        else:
            raise exceptions.TransactionError('Unknown encoding‐scheme.')

        s += var_int(int(len(script)))                      # Script length
        s += script

    # Change output.
    if change_output:
        change_address, change_value = change_output
        s += change_value.to_bytes(8, byteorder='little')   # Value

        if util.is_multisig(change_address):
            script = get_multisig_script(change_address)
        else:
            script = get_monosig_script(change_address)

        s += var_int(int(len(script)))                      # Script length
        s += script

    s += (0).to_bytes(4, byteorder='little')                # LockTime
    return s

def input_value_weight(amount):
    # Prefer outputs less than dust size, then bigger is better.
    if amount * config.UNIT <= config.DEFAULT_REGULAR_DUST_SIZE:
        return 0
    else:
        return 1 / amount

def sort_unspent_txouts(unspent, allow_unconfirmed_inputs):
    # Get deterministic results (for multiAPIConsensus type requirements), sort by timestamp and vout index.
    # (Oldest to newest so the nodes don’t have to be exactly caught up to each other for consensus to be achieved.)
    # searchrawtransactions doesn’t support unconfirmed transactions
    try:
        unspent = sorted(unspent, key=util.sortkeypicker(['ts', 'vout']))
    except KeyError: # If timestamp isn’t given.
        pass

    # Sort by amount.
    unspent = sorted(unspent,key=lambda x:input_value_weight(x['amount']))

    # Remove unconfirmed txouts, if desired.
    if allow_unconfirmed_inputs:
        # Hackish: Allow only inputs which are either already confirmed or were seen only recently. (Skip outputs from slow‐to‐confirm transanctions.)
        try:
            unspent = [coin for coin in unspent if (coin['confirmations'] > 0 or (time.time() - coin['ts']) < 6 * 3600)] # Cutoff: six hours
        except (KeyError, TypeError):
            pass
    else:
        unspent = [coin for coin in unspent if coin['confirmations'] > 0]

    return unspent
class AltcoinSupportError (Exception): pass
def private_key_to_public_key (private_key_wif):
    if config.TESTNET:
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET]
    else:
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET]
    try:
        secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes)
    except EncodingError:
        raise AltcoinSupportError('pycoin: unsupported WIF prefix')
    public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent)
    public_key = public_pair_to_sec(public_pair, compressed=compressed)
    public_key_hex = binascii.hexlify(public_key).decode('utf-8')
    return public_key_hex

class BalanceError (Exception): pass
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.')
{'fraction_non_alphanumeric': 0.09319994200376976, 'fraction_numerical': 0.011773234739741918, 'mean_word_length': 2.236602534021586, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 50, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6292740', 'n_tokens_mistral': 10468, 'n_tokens_neox': 9862, 'n_words': 2450}