Ejemplo n.º 1
0
def sign(utxo, priv, destaddrs, utxo_address_type):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create multiple utxos.
    utxo_address_type must be one of p2sh-p2wpkh/p2wpkh/p2pkh.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True,
                                 utxo_address_type=utxo_address_type)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    estfee = estimate_tx_fee(1, len(destaddrs), txtype=utxo_address_type)
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share*len(destaddrs)
    assert fee >= estfee
    log.info("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    tx = btc.make_shuffled_tx(ins, outs, version=2, locktime=compute_tx_locktime())
    amtforsign = amt if utxo_address_type != "p2pkh" else None
    rawpriv, _ = BTCEngine.wif_to_privkey(priv)
    if utxo_address_type == "p2wpkh":
        native = utxo_address_type
    else:
        native = False
    success, msg = btc.sign(tx, 0, rawpriv, amount=amtforsign, native=native)
    assert success, msg
    return tx
def sign(utxo, priv, destaddrs, segwit=True):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create a large
    number of utxos. If segwit=True the (single) utxo is assumed to
    be of type segwit p2sh/p2wpkh.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True, segwit=segwit)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    # TODO extend to other utxo types
    txtype = 'p2sh-p2wpkh' if segwit else 'p2pkh'
    estfee = estimate_tx_fee(1, len(destaddrs), txtype=txtype)
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share * len(destaddrs)
    assert fee >= estfee
    log.info("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    tx = btc.mktx(ins, outs)
    amtforsign = amt if segwit else None
    rawpriv, _ = BTCEngine.wif_to_privkey(priv)
    success, msg = btc.sign(tx, 0, rawpriv, amount=amtforsign)
    assert success, msg
    return tx
Ejemplo n.º 3
0
def get_utxo_info(upriv, utxo_binary=False):
    """Verify that the input string parses correctly as (utxo, priv)
    and return that. If `utxo_binary` is true, the first element of
    that return tuple is the standard internal form
    (txid-in-binary, index-as-int).
    """
    try:
        u, priv = upriv.split(',')
        u = u.strip()
        priv = priv.strip()
        success, utxo_bin = utxostr_to_utxo(u)
        assert success, utxo
    except:
        #not sending data to stdout in case privkey info
        jmprint("Failed to parse utxo information for utxo", "error")
        raise
    try:
        # see note below for why keytype is ignored, and note that
        # this calls read_privkey to validate.
        raw, _ = BTCEngine.wif_to_privkey(priv)
    except:
        jmprint("failed to parse privkey, make sure it's WIF compressed format.", "error")
        raise
    utxo_to_return = utxo_bin if utxo_binary else u
    return utxo_to_return, priv
Ejemplo n.º 4
0
 def generate_single_podle_sig(u, priv, i):
     """Make a podle entry for key priv at index i, using a dummy utxo value.
     This calls the underlying 'raw' code based on the class PoDLE, not the
     library 'generate_podle' which intelligently searches and updates commitments.
     """
     # Convert priv from wif; note that wallet type
     # isn't relevant since we only work with pubkeys in PoDLE:
     rawpriv, _ = BTCEngine.wif_to_privkey(priv)
     podle = PoDLE(u, rawpriv)
     r = podle.generate_podle(i)
     return (r['P'], r['P2'], r['sig'], r['e'], r['commit'])
Ejemplo n.º 5
0
 def generate_single_podle_sig(u, priv, i):
     """Make a podle entry for key priv at index i, using a dummy utxo value.
     This calls the underlying 'raw' code based on the class PoDLE, not the
     library 'generate_podle' which intelligently searches and updates commitments.
     """
     #Convert priv from wif; require P2SH-P2WPKH keys
     rawpriv, keytype = BTCEngine.wif_to_privkey(priv)
     assert keytype == BTC_P2SH_P2WPKH
     podle = PoDLE(u, rawpriv)
     r = podle.generate_podle(i)
     return (r['P'], r['P2'], r['sig'],
             r['e'], r['commit'])
Ejemplo n.º 6
0
def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"):
    """For each (utxo, privkey), first
    convert the privkey and convert to address,
    then use the blockchain instance to look up
    the utxo and check that its address field matches.
    If retrieve is True, return the set of utxos and their values.
    If segwit is true, assumes a p2sh wrapped p2wpkh, i.e.
    native segwit is NOT currently supported here. If segwit
    is false, p2pkh is assumed.
    """
    results = []
    for u, priv in utxo_datas:
        success, utxostr = utxo_to_utxostr(u)
        if not success:
            jmprint("Invalid utxo format: " + str(u), "error")
            sys.exit(EXIT_FAILURE)
        jmprint('validating this utxo: ' + utxostr, "info")
        # as noted in `ImportWalletMixin` code comments, there is not
        # yet a functional auto-detection of key type from WIF, so the
        # second argument is ignored; we assume p2sh-p2wpkh if segwit=True,
        # p2pkh if segwit=False, and p2wpkh if segwit="native" (slightly
        # ugly, just done for backwards compat.).
        if utxo_address_type == "p2wpkh":
            engine = BTC_P2WPKH
        elif utxo_address_type == "p2sh-p2wpkh":
            engine = BTC_P2SH_P2WPKH
        elif utxo_address_type == "p2pkh":
            engine = BTC_P2PKH
        else:
            raise Exception("Invalid argument: " + str(utxo_address_type))
        rawpriv, _ = BTCEngine.wif_to_privkey(priv)
        addr = engine.privkey_to_address(rawpriv)
        jmprint('claimed address: ' + addr, "info")
        res = jm_single().bc_interface.query_utxo_set([u])
        if len(res) != 1 or None in res:
            jmprint("utxo not found on blockchain: " + utxostr, "error")
            return False
        if res[0]['address'] != addr:
            jmprint(
                "privkey corresponds to the wrong address for utxo: " +
                utxostr, "error")
            jmprint(
                "blockchain returned address: {}".format(res[0]['address']),
                "error")
            jmprint("your privkey gave this address: " + addr, "error")
            return False
        if retrieve:
            results.append((u, res[0]['value']))
    jmprint('all utxos validated OK', "success")
    if retrieve:
        return results
    return True
Ejemplo n.º 7
0
def test_wif_privkeys_invalid(setup_keys):
    #first try to create wif privkey from key of wrong length
    bad_privs = [b'\x01\x02' * 17]  #some silly private key but > 33 bytes

    #next try to create wif with correct length but wrong compression byte
    bad_privs.append(b'\x07' * 32 + b'\x02')

    for priv in bad_privs:
        with pytest.raises(Exception) as e_info:
            fake_wif = BTCEngine.privkey_to_wif(priv)

    #Create a wif with wrong length
    bad_wif1 = btc.bin_to_b58check(b'\x01\x02' * 34, b'\x80')
    #Create a wif with wrong compression byte
    bad_wif2 = btc.bin_to_b58check(b'\x07' * 33, b'\x80')
    for bw in [bad_wif1, bad_wif2]:
        with pytest.raises(Exception) as e_info:
            fake_priv, keytype = BTCEngine.wif_to_privkey(bw)

    #Some invalid b58 from bitcoin repo;
    #none of these are valid as any kind of key or address
    with open(os.path.join(testdir, "base58_keys_invalid.json"), "r") as f:
        json_data = f.read()
    invalid_key_list = json.loads(json_data)
    for k in invalid_key_list:
        bad_key = k[0]
        for netval in ["mainnet", "testnet"]:
            #if using pytest -s ; sanity check to see what's actually being tested
            print('testing this key: ' + bad_key)
            #should throw exception
            with pytest.raises(Exception) as e_info:
                from_wif_key, keytype = BTCEngine.wif_to_privkey(bad_key)
                #in case the b58 check encoding is valid, we should
                #also check if the leading version byte is in the
                #expected set, and throw an error if not.
                if chr(btc.get_version_byte(bad_key)) not in b'\x80\xef':
                    raise Exception("Invalid version byte")
Ejemplo n.º 8
0
def validate_utxo_data(utxo_datas, retrieve=False, utxo_address_type="p2wpkh"):
    """For each (utxo, privkey), first
    convert the privkey and convert to address,
    then use the blockchain instance to look up
    the utxo and check that its address field matches.
    If retrieve is True, return the set of utxos and their values.
    """
    results = []
    for u, priv in utxo_datas:
        success, utxostr = utxo_to_utxostr(u)
        if not success:
            jmprint("Invalid utxo format: " + str(u), "error")
            sys.exit(EXIT_FAILURE)
        jmprint('validating this utxo: ' + utxostr, "info")
        # as noted in `ImportWalletMixin` code comments, there is not
        # yet a functional auto-detection of key type from WIF, hence
        # the need for this additional switch:
        if utxo_address_type == "p2wpkh":
            engine = BTC_P2WPKH
        elif utxo_address_type == "p2sh-p2wpkh":
            engine = BTC_P2SH_P2WPKH
        elif utxo_address_type == "p2pkh":
            engine = BTC_P2PKH
        else:
            raise Exception("Invalid argument: " + str(utxo_address_type))
        rawpriv, _ = BTCEngine.wif_to_privkey(priv)
        addr = engine.privkey_to_address(rawpriv)
        jmprint('claimed address: ' + addr, "info")
        res = jm_single().bc_interface.query_utxo_set([u])
        if len(res) != 1 or None in res:
            jmprint("utxo not found on blockchain: " + utxostr, "error")
            return False
        returned_addr = engine.script_to_address(res[0]['script'])
        if returned_addr != addr:
            return print_failed_addr_match(utxostr, addr, returned_addr)
        if retrieve:
            results.append((u, res[0]['value']))
    jmprint('all utxos validated OK', "success")
    if retrieve:
        return results
    return True
Ejemplo n.º 9
0
def test_wif_privkeys_valid(setup_keys):
    with open(os.path.join(testdir, "base58_keys_valid.json"), "r") as f:
        json_data = f.read()
    valid_keys_list = json.loads(json_data)
    for a in valid_keys_list:
        key, hex_key, prop_dict = a
        if prop_dict["isPrivkey"]:
            netval = "testnet" if prop_dict["isTestnet"] else "mainnet"
            jm_single().config.set("BLOCKCHAIN", "network", netval)
            print('testing this key: ' + key)
            assert btc.get_version_byte(
                key) in b'\x80\xef', "not valid network byte"
            comp = prop_dict["isCompressed"]
            if not comp:
                # we only handle compressed keys
                continue
            from_wif_key, keytype = BTCEngine.wif_to_privkey(key)
            expected_key = hextobin(hex_key) + b"\x01"
            assert from_wif_key == expected_key, "Incorrect key decoding: " + \
                   str(from_wif_key) + ", should be: " + str(expected_key)
    jm_single().config.set("BLOCKCHAIN", "network", "testnet")
Ejemplo n.º 10
0
def validate_utxo_data(utxo_datas, retrieve=False, segwit=False):
    """For each txid: N, privkey, first
    convert the privkey and convert to address,
    then use the blockchain instance to look up
    the utxo and check that its address field matches.
    If retrieve is True, return the set of utxos and their values.
    If segwit is true, assumes a p2sh wrapped p2wpkh, i.e.
    native segwit is NOT currently supported here. If segwit
    is false, p2pkh is assumed.
    """
    results = []
    for u, priv in utxo_datas:
        jmprint('validating this utxo: ' + str(u), "info")
        # as noted in `ImportWalletMixin` code comments, there is not
        # yet a functional auto-detection of key type from WIF, so the
        # second argument is ignored; we assume p2sh-p2wpkh if segwit,
        # else we assume p2pkh.
        engine = BTC_P2SH_P2WPKH if segwit else BTC_P2PKH
        rawpriv, _ = BTCEngine.wif_to_privkey(priv)
        addr = engine.privkey_to_address(rawpriv)
        jmprint('claimed address: ' + addr, "info")
        res = jm_single().bc_interface.query_utxo_set([u])
        if len(res) != 1 or None in res:
            jmprint("utxo not found on blockchain: " + str(u), "error")
            return False
        if res[0]['address'] != addr:
            jmprint(
                "privkey corresponds to the wrong address for utxo: " + str(u),
                "error")
            jmprint(
                "blockchain returned address: {}".format(res[0]['address']),
                "error")
            jmprint("your privkey gave this address: " + addr, "error")
            return False
        if retrieve:
            results.append((u, res[0]['value']))
    jmprint('all utxos validated OK', "success")
    if retrieve:
        return results
    return True
Ejemplo n.º 11
0
def new_wallet_from_data(data, file_name):
    print("Creating new wallet file.")
    new_pw = cli_get_wallet_passphrase_check()
    if new_pw is False:
        return False

    storage = Storage(file_name, create=True, password=new_pw)
    wallet_cls = get_wallet_cls(wtype=get_configured_wallet_type(False))

    kwdata = {
        'entropy': data['entropy'],
        'timestamp': data.get('creation_time'),
        'max_mixdepth': get_max_mixdepth(data)
    }

    if 'entropy_ext' in data:
        kwdata['entropy_extension'] = data['entropy_ext']

    wallet_cls.initialize(storage, data['network'], **kwdata)
    wallet = wallet_cls(storage)

    if 'index_cache' in data:
        for md, indices in enumerate(data['index_cache']):
            wallet.set_next_index(md, BaseWallet.ADDRESS_TYPE_EXTERNAL,
                                  indices[0], force=True)
            wallet.set_next_index(md, BaseWallet.ADDRESS_TYPE_INTERNAL,
                                  indices[1], force=True)

    if 'imported' in data:
        for md in data['imported']:
            for privkey in data['imported'][md]:
                privkey += b'\x01'
                wif = BTCEngine.privkey_to_wif(privkey)
                wallet.import_private_key(md, wif)

    wallet.save()
    wallet.close()
    return True
Ejemplo n.º 12
0
def get_utxo_info(upriv):
    """Verify that the input string parses correctly as (utxo, priv)
    and return that.
    """
    try:
        u, priv = upriv.split(',')
        u = u.strip()
        priv = priv.strip()
        success, utxo = utxostr_to_utxo(u)
        assert success, utxo
    except:
        #not sending data to stdout in case privkey info
        jmprint("Failed to parse utxo information for utxo", "error")
        raise
    try:
        # see note below for why keytype is ignored, and note that
        # this calls read_privkey to validate.
        raw, _ = BTCEngine.wif_to_privkey(priv)
    except:
        jmprint(
            "failed to parse privkey, make sure it's WIF compressed format.",
            "error")
        raise
    return u, priv