示例#1
0
def test_sanity_tests():
    seed = bytes.fromhex(
        "1077a46dc8545d372f22d9e110ae6c5c2bf7620fe9c4c911f5404d112233e1aa270567dd3554092e051ba3ba86c303590b0309116ac89964ff284db2219d7511"
    )
    first_bip32 = BIP32.from_seed(seed)
    sec_bip32 = BIP32.from_xpriv(
        "xprv9s21ZrQH143K3o4KUs47P2x9afhH31ekMo2foNTYwrU9wwZ8g5EatR9bn6YmCacdvnHWMnPFUqieQrnunrzuF5UfgGbhbEW43zRnhpPDBUL"
    )
    assert first_bip32.get_master_xpriv() == sec_bip32.get_master_xpriv()
    assert first_bip32.get_master_xpub() == sec_bip32.get_master_xpub()
    # Fuzz it a bit
    for i in range(50):
        path = [int.from_bytes(os.urandom(3), "big") for _ in range(5)]
        h_path = [
            HARDENED_INDEX + int.from_bytes(os.urandom(3), "big")
            for _ in range(5)
        ]
        mixed_path = [int.from_bytes(os.urandom(3), "big") for _ in range(5)]
        for i in mixed_path:
            if int.from_bytes(os.urandom(32), "big") % 2:
                i += HARDENED_INDEX
        assert first_bip32.get_xpriv_from_path(
            path) == sec_bip32.get_xpriv_from_path(path)
        assert first_bip32.get_xpub_from_path(
            path) == sec_bip32.get_xpub_from_path(path)
        assert first_bip32.get_xpriv_from_path(
            h_path) == sec_bip32.get_xpriv_from_path(h_path)
        assert first_bip32.get_xpub_from_path(
            h_path) == sec_bip32.get_xpub_from_path(h_path)
        assert first_bip32.get_xpriv_from_path(
            mixed_path) == sec_bip32.get_xpriv_from_path(mixed_path)
        assert first_bip32.get_xpub_from_path(
            mixed_path) == sec_bip32.get_xpub_from_path(mixed_path)
    # Taken from iancoleman's website
    bip32 = BIP32.from_seed(
        bytes.fromhex(
            "ac8c2377e5cde867d7e420fbe04d8906309b70d51b8fe58d6844930621a9bc223929155dcfebb4da9d62c86ec0d15adf936a663f4f0cf39cbb0352e7dac073d6"
        ))
    assert bip32.get_master_xpriv() == bip32.get_xpriv_from_path(
        []
    ) == "xprv9s21ZrQH143K2GzaKJsW7DQsxeDpY3zqgusaSx6owWGC19k4mhwnVAsm4qPsCw43NkY2h1BzVLyxWHt9NKF86QRyBj53vModdGcNxtpD6KX"
    assert bip32.get_master_xpub() == bip32.get_xpub_from_path(
        []
    ) == "xpub661MyMwAqRbcEm53RLQWUMMcWg4JwWih48oBFLWRVqoAsx5DKFG32yCEv8iH29TWpmo5KTcpsjXcea6Zx4Hc6PAbGnHjEDCf3yHbj7qdpnf"
    # Sanity checks for m/0'/0'/14/0'/18
    xpriv = bip32.get_xpriv_from_path(
        [HARDENED_INDEX, HARDENED_INDEX, 14, HARDENED_INDEX, 18])
    xpub = bip32.get_xpub_from_path(
        [HARDENED_INDEX, HARDENED_INDEX, 14, HARDENED_INDEX, 18])
    assert xpriv == "xprvA2YVbLvEeKaPedw7F6RLwG3RgYnTq1xGCyDNMgZNWdEQnSUBQmKEuLyA6TSPsggt5xvyJHLD9L25tNLpQiP4Q8ZkQNo8ueAgeYj5zYq8hSm"
    assert xpub == "xpub6FXqzrT8Uh8gs81aM7xMJPzAEacxEUg7aC8yA4xz4xmPfEoKxJdVT9Hdwm3LwVQrSos2rhGDt8aGGHvdLr5LLAjK8pXFkbSpzGoGTXjd4z9"
    # Now if we our master is m/0'/0'/14, we should derive the same keys for
    # m/0'/18 !
    xpriv2 = bip32.get_xpriv_from_path([HARDENED_INDEX, HARDENED_INDEX, 14])
    assert xpriv2 == "xprv9yQJmvQMywM5i7UNuZ4RQ1A9rEMwAJCExPardkmBCB46S3vBqNEatSwLUrwLNLHBu1Kd9aGxGKDD5YAfs6hRzpYthciAHjtGadxgV2PeqY9"
    bip32 = BIP32.from_xpriv(xpriv2)
    assert bip32.get_master_xpriv() == xpriv2
    assert bip32.get_xpriv_from_path([HARDENED_INDEX, 18]) == xpriv
    assert bip32.get_xpub_from_path([HARDENED_INDEX, 18]) == xpub
示例#2
0
def parse_key(key: str) -> BIP32:
    """
    Try to parse an extended key, whether it is in xpub, xpriv or mnemonic format.
    """
    try:
        private_key = BIP32.from_xpriv(key)
        print('🔑  Read master private key successfully')
        return private_key
    except Exception:
        pass

    try:
        public_key = BIP32.from_xpub(key)
        print('🔑  Read master public key successfully')
        return public_key
    except Exception:
        pass

    try:
        language = Mnemonic.detect_language(key)
        seed = Mnemonic(language).to_seed(key)
        private_key = BIP32.from_seed(seed)
        print('🔑  Read mnemonic successfully')
        return private_key
    except Exception:
        pass

    raise ValueError(
        'The key is invalid or the format isn\'t recognized. Make sure it\'s a mnemonic, xpriv or xpub.'
    )
示例#3
0
    def __init__(self,
                 xpriv,
                 xpubs,
                 emergency_pubkeys,
                 bitcoin_conf_path,
                 cosigning_url,
                 sigserver_url,
                 acked_addresses,
                 current_index=0,
                 birthdate=None):
        """
        We need the xpub of all the other stakeholders to derive their pubkeys.

        :param xpriv: Who am I ? Has to correspond to one of the following
                      xpub. As str.
        :param xpubs: A list of the xpub of all the stakeholders (as str), in
                      the following order: 1) first trader 2) second trader
                      3) first "normie" stakeholder 4) second "normie"
                      stakeholder.
        :param emergency_pubkeys: A list of the four offline keys of the
                                  stakeholders, as bytes.
        :param bitcoin_conf_path: Path to bitcoin.conf.
        :param cosigning_url: The url of the cosigning server.
        :param sigserver_url: The url of the server to post / get the sigs from
                              other stakeholders.
        :param acked_addresses: Addresses to which we accept to spend.
        :param birthdate: The timestamp at which this wallet has been created.
                          If not passed, will assume newly-created wallet.
        """
        assert len(xpubs) == 4
        self.our_bip32 = BIP32.from_xpriv(xpriv)
        self.keychains = []
        for xpub in xpubs:
            if xpub != self.our_bip32.get_master_xpub():
                self.keychains.append(BIP32.from_xpub(xpub))
            else:
                self.keychains.append(None)
        self.all_xpubs = xpubs
        self.emergency_pubkeys = emergency_pubkeys
        # Ok, shitload of indexes. The current one is the lower bound of the
        # range we will import to bitcoind as watchonly. The max one is the
        # upper bond, the current "gen" one is to generate new addresses.
        self.current_index = current_index
        self.current_gen_index = self.current_index
        self.max_index = current_index + 500
        self.index_treshold = self.max_index

        self.birthdate = int(time.time()) if birthdate is None else birthdate

        self.bitcoind = BitcoindApi(bitcoin_conf_path)

        # First of all, watch the emergency vault
        self.watch_emergency_vault()
        # And store the corresponding address..
        txo = emergency_txout(self.emergency_pubkeys, 0)
        self.emergency_address = str(
            CBitcoinAddress.from_scriptPubKey(txo.scriptPubKey))

        # The cosigning server, asked for its signature for the spend_tx
        self.cosigner = CosigningApi(cosigning_url)
        self.cosigner_pubkey = self.cosigner.get_pubkey()

        # The "sig" server, used to store and exchange signatures between
        # vaults and which provides us a feerate.
        # Who am I ?
        stk_id = self.keychains.index(None) + 1
        self.sigserver = ServerApi(sigserver_url, stk_id)

        self.vault_addresses = []
        self.unvault_addresses = []
        self.update_watched_addresses()

        # We keep track of each vault, see below when we fill it for details
        # about what it contains. Basically all the transactions, the
        # signatures and some useful fields (like "are all txs signed ?").
        self.vaults = []
        self.vaults_lock = threading.Lock()

        # Poll for funds until we die
        self.funds_poller_stop = threading.Event()
        self.funds_poller = threading.Thread(target=self.poll_for_funds,
                                             daemon=True)
        self.funds_poller.start()

        # Poll for spends until we die
        self.acked_addresses = acked_addresses
        self.acked_spends = []
        self.spends_poller_stop = threading.Event()
        self.spends_poller = threading.Thread(target=self.poll_for_spends,
                                              daemon=True)
        self.spends_poller.start()

        # Don't start polling for signatures just yet, we don't have any vault!
        self.update_sigs_stop = threading.Event()
        self.update_sigs_thread =\
            threading.Thread(target=self.update_all_signatures, daemon=True)

        self.stopped = False
示例#4
0
def test_sanity_tests():
    seed = bytes.fromhex(
        "1077a46dc8545d372f22d9e110ae6c5c2bf7620fe9c4c911f5404d112233e1aa270567dd3554092e051ba3ba86c303590b0309116ac89964ff284db2219d7511"
    )
    first_bip32 = BIP32.from_seed(seed)
    sec_bip32 = BIP32.from_xpriv(
        "xprv9s21ZrQH143K3o4KUs47P2x9afhH31ekMo2foNTYwrU9wwZ8g5EatR9bn6YmCacdvnHWMnPFUqieQrnunrzuF5UfgGbhbEW43zRnhpPDBUL"
    )
    assert first_bip32.get_master_xpriv() == sec_bip32.get_master_xpriv()
    assert first_bip32.get_master_xpub() == sec_bip32.get_master_xpub()
    # Fuzz it a bit
    for i in range(50):
        path = [int.from_bytes(os.urandom(3), "big") for _ in range(5)]
        h_path = [
            HARDENED_INDEX + int.from_bytes(os.urandom(3), "big")
            for _ in range(5)
        ]
        mixed_path = [int.from_bytes(os.urandom(3), "big") for _ in range(5)]
        for i in mixed_path:
            if int.from_bytes(os.urandom(32), "big") % 2:
                i += HARDENED_INDEX
        assert first_bip32.get_xpriv_from_path(
            path) == sec_bip32.get_xpriv_from_path(path)
        assert first_bip32.get_xpub_from_path(
            path) == sec_bip32.get_xpub_from_path(path)
        assert first_bip32.get_xpriv_from_path(
            h_path) == sec_bip32.get_xpriv_from_path(h_path)
        assert first_bip32.get_xpub_from_path(
            h_path) == sec_bip32.get_xpub_from_path(h_path)
        assert first_bip32.get_xpriv_from_path(
            mixed_path) == sec_bip32.get_xpriv_from_path(mixed_path)
        assert first_bip32.get_xpub_from_path(
            mixed_path) == sec_bip32.get_xpub_from_path(mixed_path)
    # Taken from iancoleman's website
    bip32 = BIP32.from_seed(
        bytes.fromhex(
            "ac8c2377e5cde867d7e420fbe04d8906309b70d51b8fe58d6844930621a9bc223929155dcfebb4da9d62c86ec0d15adf936a663f4f0cf39cbb0352e7dac073d6"
        ))
    assert bip32.get_master_xpriv() == bip32.get_xpriv_from_path(
        []
    ) == "xprv9s21ZrQH143K2GzaKJsW7DQsxeDpY3zqgusaSx6owWGC19k4mhwnVAsm4qPsCw43NkY2h1BzVLyxWHt9NKF86QRyBj53vModdGcNxtpD6KX"
    assert bip32.get_master_xpub() == bip32.get_xpub_from_path(
        []
    ) == "xpub661MyMwAqRbcEm53RLQWUMMcWg4JwWih48oBFLWRVqoAsx5DKFG32yCEv8iH29TWpmo5KTcpsjXcea6Zx4Hc6PAbGnHjEDCf3yHbj7qdpnf"
    # Sanity checks for m/0'/0'/14/0'/18
    xpriv = bip32.get_xpriv_from_path(
        [HARDENED_INDEX, HARDENED_INDEX, 14, HARDENED_INDEX, 18])
    xpub = bip32.get_xpub_from_path(
        [HARDENED_INDEX, HARDENED_INDEX, 14, HARDENED_INDEX, 18])
    assert xpriv == "xprvA2YVbLvEeKaPedw7F6RLwG3RgYnTq1xGCyDNMgZNWdEQnSUBQmKEuLyA6TSPsggt5xvyJHLD9L25tNLpQiP4Q8ZkQNo8ueAgeYj5zYq8hSm"
    assert xpub == "xpub6FXqzrT8Uh8gs81aM7xMJPzAEacxEUg7aC8yA4xz4xmPfEoKxJdVT9Hdwm3LwVQrSos2rhGDt8aGGHvdLr5LLAjK8pXFkbSpzGoGTXjd4z9"
    # Now if we our master is m/0'/0'/14, we should derive the same keys for
    # m/0'/18 !
    xpriv2 = bip32.get_xpriv_from_path([HARDENED_INDEX, HARDENED_INDEX, 14])
    assert xpriv2 == "xprv9yQJmvQMywM5i7UNuZ4RQ1A9rEMwAJCExPardkmBCB46S3vBqNEatSwLUrwLNLHBu1Kd9aGxGKDD5YAfs6hRzpYthciAHjtGadxgV2PeqY9"
    bip32 = BIP32.from_xpriv(xpriv2)
    assert bip32.get_master_xpriv() == xpriv2
    assert bip32.get_xpriv_from_path([HARDENED_INDEX, 18]) == xpriv
    assert bip32.get_xpub_from_path([HARDENED_INDEX, 18]) == xpub
    # We should recognize the networks..
    # .. for xprivs:
    bip32 = BIP32.from_xpriv(
        "xprv9wHokC2KXdTSpEepFcu53hMDUHYfAtTaLEJEMyxBPAMf78hJg17WhL5FyeDUQH5KWmGjGgEb2j74gsZqgupWpPbZgP6uFmP8MYEy5BNbyET"
    )
    assert bip32.network == "main"
    bip32 = BIP32.from_xpriv(
        "tprv8ZgxMBicQKsPeCBsMzQCCb5JcW4S49MVL3EwhdZMF1RF71rgisZU4ZRvrHX6PZQEiNUABDLvYqpx8Lsccq8aGGR59qHAoLoE3iXYuDa8JTP"
    )
    assert bip32.network == "test"
    # .. for xpubs:
    bip32 = BIP32.from_xpub(
        "xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU"
    )
    assert bip32.network == "main"
    bip32 = BIP32.from_xpub(
        "tpubD6NzVbkrYhZ4WN3WiKRjeo2eGyYNiKNg8vcQ1UjLNJJaDvoFhmR1XwJsbo5S4vicSPoWQBThR3Rt8grXtP47c1AnoiXMrEmFdRZupxJzH1j"
    )
    assert bip32.network == "test"
    # We should create valid network encoding..
    assert BIP32.from_seed(os.urandom(32),
                           "test").get_master_xpub().startswith("tpub")
    assert BIP32.from_seed(os.urandom(32),
                           "test").get_master_xpriv().startswith("tprv")
    assert BIP32.from_seed(os.urandom(32),
                           "main").get_master_xpub().startswith("xpub")
    assert BIP32.from_seed(os.urandom(32),
                           "main").get_master_xpriv().startswith("xprv")
示例#5
0
    def SignFundingTx(self, request, context):
        """BOLT #3 - Funding Transaction
        Sign the funding transaction
        TODO this must be done after we know the remote signature on the
        commitment tx - need an API call that provides this to the
        signer.
        """

        node_id = request.node_id.data
        node = self.nodes.get(node_id)
        logger.debug("SignFundingTx node:%s" % node_id)

        xpub = node.bip32_key.get_xpriv_from_path('m/0/0')
        onchain_bip32_key = BIP32.from_xpriv(xpub)

        tx = request.tx
        tx = Transaction.import_raw(tx.raw_tx_bytes, self.network)

        reply = remotesigner_pb2.SignFundingTxReply()
        witnesses = remotesigner_pb2.Witness()

        local_prv = None
        local_key = None
        logger.debug(request)
        logger.debug(request.tx.raw_tx_bytes.hex())
        for i, input_desc in enumerate(request.tx.input_descs):
            key_index = input_desc.key_loc.key_index
            spend_type = input_desc.spend_type
            amount = input_desc.prev_output.value_sat
            channel_nonce = input_desc.close_info.channel_nonce.data
            commitment_point = input_desc.close_info.commitment_point.data
            channel = node.channels.get(channel_nonce)

            if key_index != 0:
                local_prv = onchain_bip32_key.get_privkey_from_path("m/%d" %
                                                                    key_index)
                local_key = Key(import_key=local_prv, is_private=True)
            elif channel:
                if commitment_point:
                    local_priv_key = derive_priv_key(
                        commitment_point, payment_basepoint,
                        channel.basepoints.keys.payment_basepoint_secret)
                    local_key = Key(import_key=local_priv_key, is_private=True)
                else:
                    local_key = Key(import_key=channel.basepoints.keys.
                                    payment_basepoint_secret,
                                    is_private=True)
            else:
                witnesses.signature.data = b''
                witnesses.pubkey.data = b''
                reply.witnesses.extend([witnesses])
                continue

            spend_type = spend_type_to_string(spend_type)
            tx.inputs[i].script_type = spend_type
            logger.debug("funding signature: %s" % spend_type)
            tx.inputs[i].witness_type = "segwit"
            tx.inputs[i].sigs_required = 1
            tx.inputs[i].value = amount
            if spend_type == 'p2pkh':
                tx.witness_type = 'legacy'
            else:
                tx.witness_type = 'segwit'
            tx.sign(keys=[local_key], tid=i)

            witnesses.signature.data = tx.inputs[i].witnesses[0]
            witnesses.pubkey.data = tx.inputs[i].witnesses[1]

            logger.debug("funding signature: %s" %
                         tx.inputs[i].signatures[0].as_der_encoded().hex())
            logger.debug("funding public key %s" %
                         tx.inputs[i].as_dict()['public_keys'])
            reply.witnesses.extend([witnesses])
        logger.debug(reply)
        return reply