def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None, } if data_privkey is not None: if not virtualchain.is_singlesig(data_privkey): raise ValueError('Invalid data key info') pk_data = ecdsa_private_key(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if virtualchain.is_multisig(owner_privkey): pks = owner_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript(owner_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['owner_privkey'] = multisig_info ret['owner_addresses'] = [virtualchain.get_privkey_address(multisig_info)] elif virtualchain.is_singlesig(owner_privkey): pk_owner = ecdsa_private_key(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner ret['owner_addresses'] = [virtualchain.get_privkey_address(pk_owner)] else: raise ValueError('Invalid owner key info') if payment_privkey is None: return ret if virtualchain.is_multisig(payment_privkey): pks = payment_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript(payment_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['payment_privkey'] = multisig_info ret['payment_addresses'] = [virtualchain.get_privkey_address(multisig_info)] elif virtualchain.is_singlesig(payment_privkey): pk_payment = ecdsa_private_key(payment_privkey).to_hex() ret['payment_privkey'] = pk_payment ret['payment_addresses'] = [virtualchain.get_privkey_address(pk_payment)] else: raise ValueError('Invalid payment key info') ret['data_pubkey'] = ecdsa_private_key(ret['data_privkey']).public_key().to_hex() ret['data_pubkeys'] = [ret['data_pubkey']] return ret
def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None } if data_privkey is not None: if not is_singlesig(data_privkey): raise ValueError("Invalid data key info") pk_data = virtualchain.BitcoinPrivateKey(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if is_multisig(owner_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in owner_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( owner_privkey['redeem_script']) ret['owner_privkey'] = virtualchain.make_multisig_info(m, pks) elif is_singlesig(owner_privkey): pk_owner = virtualchain.BitcoinPrivateKey(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner else: raise ValueError("Invalid owner key info") if payment_privkey is not None: if is_multisig(payment_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in payment_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( payment_privkey['redeem_script']) ret['payment_privkey'] = virtualchain.make_multisig_info(m, pks) elif is_singlesig(payment_privkey): pk_payment = virtualchain.BitcoinPrivateKey( payment_privkey).to_hex() ret['payment_privkey'] = pk_payment else: raise ValueError("Invalid payment key info") return ret
def sign_multisig(hash_hex, redeem_script, secret_keys): assert len(redeem_script) > 0 m, pk_hexes = virtualchain.parse_multisig_redeemscript(redeem_script) privs = {} for sk in secret_keys: pk = virtualchain.ecdsalib.ecdsa_private_key(sk).public_key().to_hex() compressed_pubkey = keylib.key_formatting.compress(pk) uncompressed_pubkey = keylib.key_formatting.decompress(pk) privs[compressed_pubkey] = sk privs[uncompressed_pubkey] = sk used_keys, sigs = [], [] for pk in pk_hexes: if pk not in privs: continue if len(used_keys) == m: break assert pk not in used_keys, 'Tried to reuse key {}'.format(pk) sk_hex = privs[pk] used_keys.append(pk) b64sig = virtualchain.ecdsalib.sign_digest(hash_hex, sk_hex) sighex = binascii.hexlify(base64.b64decode(b64sig)) sigs.append(sighex) assert len( used_keys) == m, 'Missing private keys (used {}, required {})'.format( len(used_keys), m) return base64.b64encode( virtualchain.btc_script_serialize([None] + sigs + [redeem_script]))
def get_privkey_info_params(privkey_info, config_path=CONFIG_PATH): """ Get the parameters that characterize a private key info bundle: the number of private keys, and the number of signatures required to make a valid transaction. * for single private keys, this is (1, 1) * for multisig info dicts, this is (m, n) Return (m, n) on success Return (None, None) on failure """ if privkey_info is None: from .backend.blockchain import get_block_height key_config = (2, 3) log.warning('No private key info given, assuming {} key config'.format( key_config)) return key_config if virtualchain.is_singlesig(privkey_info): return (1, 1) elif virtualchain.is_multisig(privkey_info): m, pubs = virtualchain.parse_multisig_redeemscript( privkey_info['redeem_script']) if m is None or pubs is None: return None, None return m, len(pubs) return None, None
def get_privkey_info_params(privkey_info, config_path=CONFIG_PATH): """ Get the parameters that characterize a private key info bundle: the number of private keys, and the number of signatures required to make a valid transaction. * for single private keys, this is (1, 1) * for multisig info dicts, this is (m, n) Return (m, n) on success Return (None, None) on failure """ if privkey_info is None: from .backend.blockchain import get_block_height key_config = (2, 3) log.warning('No private key info given, assuming {} key config'.format(key_config)) return key_config if virtualchain.is_singlesig( privkey_info ): return (1, 1) elif virtualchain.is_multisig( privkey_info ): m, pubs = virtualchain.parse_multisig_redeemscript(privkey_info['redeem_script']) if m is None or pubs is None: return None, None return m, len(pubs) return None, None
def verify_multisig(address, hash_hex, scriptSig): script_parts = virtualchain.btc_script_deserialize(scriptSig) if len(script_parts) < 2: log.warn("Verfiying multisig failed, couldn't grab script parts") return False redeem_script = script_parts[-1] script_sigs = script_parts[1:-1] if virtualchain.btc_make_p2sh_address(redeem_script) != address: log.warn(("Address {} does not match the public key in the" + " provided scriptSig: provided redeemscript = {}").format( address, redeem_script)) return False m, pk_hexes = virtualchain.parse_multisig_redeemscript(redeem_script) if len(script_sigs) != m: log.warn("Failed to validate multi-sig, not correct number of signatures: have {}, require {}".format( len(script_sigs), m)) return False cur_pk = 0 for cur_sig in script_sigs: sig64 = base64.b64encode(binascii.unhexlify(cur_sig)) sig_passed = False while not sig_passed: if cur_pk >= len(pk_hexes): log.warn("Failed to validate multi-signature, ran out of pks to check") return False sig_passed = virtualchain.ecdsalib.verify_digest(hash_hex, pk_hexes[cur_pk], sig64) cur_pk += 1 return True
def sign_multisig(hash_hex, redeem_script, secret_keys): assert len(redeem_script) > 0 m, pk_hexes = virtualchain.parse_multisig_redeemscript(redeem_script) privs = {} for sk in secret_keys: pk = virtualchain.ecdsalib.ecdsa_private_key(sk).public_key().to_hex() compressed_pubkey = keylib.key_formatting.compress(pk) uncompressed_pubkey = keylib.key_formatting.decompress(pk) privs[compressed_pubkey] = sk privs[uncompressed_pubkey] = sk used_keys, sigs = [],[] for pk in pk_hexes: if pk not in privs: continue if len(used_keys) == m: break assert pk not in used_keys, 'Tried to reuse key {}'.format(pk) sk_hex = privs[pk] used_keys.append(pk) b64sig = virtualchain.ecdsalib.sign_digest(hash_hex, sk_hex) sighex = binascii.hexlify(base64.b64decode(b64sig)) sigs.append(sighex) assert len(used_keys) == m, 'Missing private keys (used {}, required {})'.format(len(used_keys), m) return base64.b64encode(virtualchain.btc_script_serialize([None] + sigs + [redeem_script]))
def tx_estimate_signature_len_bytes(privkey_info): if virtualchain.is_singlesig(privkey_info): return 73 else: m, _ = virtualchain.parse_multisig_redeemscript( privkey_info['redeem_script'] ) siglengths = 74 * m scriptlen = len(privkey_info['redeem_script']) / 2 return 6 + scriptlen + siglengths
def tx_estimate_signature_len_bytes(privkey_info): if virtualchain.is_singlesig(privkey_info): return 73 else: m, _ = virtualchain.parse_multisig_redeemscript( privkey_info['redeem_script']) siglengths = 74 * m scriptlen = len(privkey_info['redeem_script']) / 2 return 6 + scriptlen + siglengths
def _convert_key(given_privkey, key_type): if virtualchain.is_multisig(given_privkey): pks = given_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( given_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['{}_privkey'.format(key_type)] = multisig_info ret['{}_addresses'.format(key_type)] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(given_privkey): pk = ecdsa_private_key(given_privkey).to_hex() ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [ virtualchain.get_privkey_address(pk) ] elif virtualchain.btc_is_singlesig_segwit(given_privkey): pk = virtualchain.make_segwit_info( virtualchain.get_singlesig_privkey(given_privkey)) ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [pk['address']] elif virtualchain.btc_is_multisig_segwit(given_privkey): pks = given_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( given_privkey['redeem_script']) assert m <= len(pks) pk = virtualchain.make_multisig_segwit_info(m, pks) ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [pk['address']] else: raise ValueError('Invalid owner key info')
def tx_sign_multisig(tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction TODO: move to virtualchain """ # sign in the right order. map all possible public keys to their private key privs = {} for pk in private_keys: pubk = keylib.ECPrivateKey(pk).public_key().to_hex() compressed_pubkey = keylib.key_formatting.compress(pubk) uncompressed_pubkey = keylib.key_formatting.decompress(pubk) privs[compressed_pubkey] = pk privs[uncompressed_pubkey] = pk m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys, sigs = [], [] for public_key in public_keys: if public_key not in privs: continue if len(used_keys) == m: break assert public_key not in used_keys, 'Tried to reuse key {}'.format( public_key) pk_str = privs[public_key] used_keys.append(public_key) sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode) sigs.append(sig) assert len( used_keys) == m, 'Missing private keys (used {}, required {})'.format( len(used_keys), m) return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
def tx_sign_multisig(blockstack_tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction """ # sign in the right order privs = dict([ (virtualchain.BitcoinPrivateKey(str(pk_str)).public_key().to_hex(), str(pk_str)) for pk_str in private_keys ]) m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys = [] sigs = [] for ki in xrange(0, len(public_keys)): if not privs.has_key(public_keys[ki]): continue if len(used_keys) == m: break assert public_keys[ ki] not in used_keys, "Tried to reuse key %s" % public_keys[ki] pk_str = privs[public_keys[ki]] used_keys.append(public_keys[ki]) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = bitcoin.multisign(blockstack_tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, "Missing private keys" return bitcoin.apply_multisignatures(blockstack_tx, idx, str(redeem_script), sigs)
def tx_sign_multisig(tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction TODO: move to virtualchain """ # sign in the right order privs = { virtualchain.BitcoinPrivateKey(str(pk)).public_key().to_hex(): str(pk) for pk in private_keys } m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys, sigs = [], [] for public_key in public_keys: if public_key not in privs: continue if len(used_keys) == m: break assert public_key not in used_keys, 'Tried to reuse key {}'.format( public_key) pk_str = privs[public_key] used_keys.append(public_key) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode) # sig = bitcoin.multisign(tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, 'Missing private keys' return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None, } if data_privkey is not None: if not virtualchain.is_singlesig(data_privkey): raise ValueError('Invalid data key info') pk_data = ecdsa_private_key(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if virtualchain.is_multisig(owner_privkey): pks = owner_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( owner_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['owner_privkey'] = multisig_info ret['owner_addresses'] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(owner_privkey): pk_owner = ecdsa_private_key(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner ret['owner_addresses'] = [ virtualchain.get_privkey_address(pk_owner) ] else: raise ValueError('Invalid owner key info') if payment_privkey is None: return ret if virtualchain.is_multisig(payment_privkey): pks = payment_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( payment_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['payment_privkey'] = multisig_info ret['payment_addresses'] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(payment_privkey): pk_payment = ecdsa_private_key(payment_privkey).to_hex() ret['payment_privkey'] = pk_payment ret['payment_addresses'] = [ virtualchain.get_privkey_address(pk_payment) ] else: raise ValueError('Invalid payment key info') ret['data_pubkey'] = ecdsa_private_key( ret['data_privkey']).public_key().to_hex() ret['data_pubkeys'] = [ret['data_pubkey']] return ret
def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None, } if data_privkey is not None: if not is_singlesig(data_privkey): raise ValueError('Invalid data key info') pk_data = virtualchain.BitcoinPrivateKey(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if is_multisig(owner_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in owner_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( owner_privkey['redeem_script']) ret['owner_privkey'] = virtualchain.make_multisig_info(m, pks) ret['owner_addresses'] = [ret['owner_privkey']['address']] elif is_singlesig(owner_privkey): pk_owner = virtualchain.BitcoinPrivateKey(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner ret['owner_addresses'] = [ virtualchain.BitcoinPrivateKey( pk_owner).public_key().address() ] else: raise ValueError('Invalid owner key info') if payment_privkey is None: return ret if is_multisig(payment_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in payment_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( payment_privkey['redeem_script']) ret['payment_privkey'] = virtualchain.make_multisig_info(m, pks) ret['payment_addresses'] = [ret['payment_privkey']['address']] elif is_singlesig(payment_privkey): pk_payment = virtualchain.BitcoinPrivateKey(payment_privkey).to_hex() ret['payment_privkey'] = pk_payment ret['payment_addresses'] = [ virtualchain.BitcoinPrivateKey(pk_payment).public_key().address() ] else: raise ValueError('Invalid payment key info') ret['data_pubkey'] = ECPrivateKey( ret['data_privkey']).public_key().to_hex() ret['data_pubkeys'] = [ret['data_pubkey']] return ret
def get_compressed_and_decompressed_private_key_info(privkey_info): """ Get the compressed and decompressed versions of private keys and addresses Return {'compressed_addr': ..., 'compressed_private_key_info': ..., 'decompressed_addr': ..., 'decompressed_private_key_info': ...} on success """ if virtualchain.is_multisig( privkey_info) or virtualchain.btc_is_multisig_segwit(privkey_info): # get both compressed and decompressed addresses privkeys = privkey_info['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( privkey_info['redeem_script']) privkeys_hex = [ecdsa_private_key(pk).to_hex() for pk in privkeys] decompressed_privkeys = map( lambda pk: pk if len(pk) == 64 else pk[:-2], privkeys_hex) compressed_privkeys = map( lambda pk: pk if len(pk) == 66 and pk[:-2] == '01' else pk, privkeys_hex) decompressed_multisig = virtualchain.make_multisig_info( m, decompressed_privkeys, compressed=True) compressed_multisig = virtualchain.make_multisig_info( m, compressed_privkeys, compressed=False) decompressed_addr = virtualchain.address_reencode( decompressed_multisig['address']) compressed_addr = virtualchain.address_reencode( compressed_multisig['address']) return { 'decompressed_private_key_info': decompressed_multisig, 'compressed_private_key_info': compressed_multisig, 'compressed_addr': compressed_addr, 'decompressed_addr': decompressed_addr } elif virtualchain.is_singlesig( privkey_info) or virtualchain.btc_is_singlesig_segwit( privkey_info): pk = virtualchain.get_singlesig_privkey(privkey_info) # get both compressed and decompressed addresses compressed_pk = None decompressed_pk = None if len(pk) == 66 and pk.endswith('01'): compressed_pk = pk decompressed_pk = pk[:-2] else: compressed_pk = pk decompressed_pk = pk + '01' compressed_pubk = ecdsa_private_key( compressed_pk).public_key().to_hex() decompressed_pubk = ecdsa_private_key( decompressed_pk).public_key().to_hex() compressed_addr = virtualchain.address_reencode( keylib.public_key_to_address(compressed_pubk)) decompressed_addr = virtualchain.address_reencode( keylib.public_key_to_address(decompressed_pubk)) return { 'decompressed_private_key_info': decompressed_pk, 'compressed_private_key_info': compressed_pk, 'compressed_addr': compressed_addr, 'decompressed_addr': decompressed_addr } else: raise ValueError("Invalid key bundle")