Esempio n. 1
0
 def _add_unspent_txo(self, utxo, height):
     """
     Add a UTXO as returned by rpc's listunspent call to the wallet.
     Note that these are returned as little endian outpoint txids, so
     are converted.
     params:
         utxo: single utxo dict as returned by listunspent
         current_blockheight: blockheight as integer, used to
         set the block in which a confirmed utxo is included.
     """
     txid = hextobin(utxo['txid'])
     script = hextobin(utxo['scriptPubKey'])
     value = int(Decimal(str(utxo['amount'])) * Decimal('1e8'))
     self.add_utxo(txid, int(utxo['vout']), script, value, height)
 def get_all_transactions(self):
     """ Get all transactions (spending or receiving) that
     are currently recorded by our blockchain interface as relating
     to this wallet, as a list.
     """
     res = []
     processed_txids = []
     for r in self.bci._yield_transactions(
         self.get_wallet_name()):
         txid = r["txid"]
         if txid not in processed_txids:
             tx = self.bci.get_transaction(hextobin(txid))
             res.append(self.bci.get_deser_from_gettransaction(tx))
             processed_txids.append(txid)
     return res
def test_fidelity_bond_seen(valid, fidelity_bond_proof, maker_nick,
                            taker_nick):
    proof = FidelityBondProof(
        maker_nick, taker_nick,
        hextobin(fidelity_bond_proof['certificate-pubkey']),
        fidelity_bond_proof['certificate-expiry'],
        hextobin(fidelity_bond_proof['certificate-signature']),
        (hextobin(fidelity_bond_proof['txid']), fidelity_bond_proof['vout']),
        hextobin(fidelity_bond_proof['utxo-pubkey']),
        fidelity_bond_proof['locktime'])
    serialized = proof._serialize_proof_msg(
        fidelity_bond_proof['nick-signature'])

    ob = get_ob()
    ob.msgchan.nick = taker_nick
    ob.on_fidelity_bond_seen(maker_nick, fidelity_bond_cmd_list[0], serialized)
    rows = ob.db.execute("SELECT * FROM fidelitybonds;").fetchall()
    assert len(rows) == 1
    assert rows[0]["counterparty"] == maker_nick
    assert rows[0]["takernick"] == taker_nick
    try:
        parsed_proof = FidelityBondProof.parse_and_verify_proof_msg(
            rows[0]["counterparty"], rows[0]["takernick"], rows[0]["proof"])
    except ValueError:
        parsed_proof = None
    if valid:
        assert parsed_proof is not None
        assert parsed_proof.utxo[0] == hextobin(fidelity_bond_proof["txid"])
        assert parsed_proof.utxo[1] == fidelity_bond_proof["vout"]
        assert parsed_proof.locktime == fidelity_bond_proof["locktime"]
        assert parsed_proof.cert_expiry == fidelity_bond_proof[
            "certificate-expiry"]
        assert parsed_proof.utxo_pub == hextobin(
            fidelity_bond_proof["utxo-pubkey"])
    else:
        assert parsed_proof is None
def test_duplicate_fidelity_bond_rejected():

    fidelity_bond_info = (({
        "nick-signature":
        (b'0E\x02!\x00\xdbb\x15\x96\xa0\x87\xb8\x1d\xe05\xddV\xa1\x1bn\x8f' +
         b'q\x90&\x8cG@\x89"2\xb2\x81\x9b\xc00\xa5\xb6\x02 \x03\x14l\xd7BR\xba\x8c:\x88('
         +
         b'\x8e3l\xac\xf5`T\x87\xfa\xf5\xa9\x1f\x19\xc0\xb6\xe9\xbb\xdc\xc7y\x99'
         ),
        "certificate-signature":
        ("3045022100eb512af938113badb4d7b29e0c22061c51dadb113a9395e" +
         "9ed81a46103391213022029170de414964f07228c4f0d404b1386272bae337f0133f1329d948a"
         + "252fa2a0"),
        "certificate-pubkey":
        "0258efb077960d6848f001904857f062fa453de26c1ad8736f55497254f56e8a74",
        "certificate-expiry":
        1,
        "utxo-pubkey":
        "02f54f027377e84171296453828aa863c23fc4489453025f49bd3addfb3a359b3d",
        "txid":
        "84c88fafe0bb75f507fe3bfb29a93d10b2e80c15a63b2943c1a5fecb5a55cba2",
        "vout":
        0,
        "locktime":
        1640995200
    }, "J5A4k9ecQzRRDfBx", "J55VZ6U6ZyFDNeuv"), ({
        "nick-signature":
        (b'0E\x02!\x00\x80\xc6$\x0c\xa1\x15YS\xacHB\xb33\xfa~\x9f\xb9`\xb3' +
         b'\xfe\xed0\xadHq\xc1~\x03.B\xbb#\x02 #y~]\xd9\xbbX2\xc0\x1b\xe57\xf4\x0f\x1f'
         + b'\xd6$\x01\xf9\x15Z\xc9X\xa5\x18\xbe\x83\x1a&4Y\xd4'),
        "certificate-signature":
        ("304402205669ea394f7381e9abf0b3c013fac2b79d24c02feb86ff153" +
         "cff83c658d7cf7402200b295ace655687f80738f3733c1dc5f1e2b8f351c017a05b8bd31983dd"
         + "4d723f"),
        "certificate-pubkey":
        "031d1c006a6310dbdf57341efc19c3a43c402379d7ccd2480416cadc7579f973f7",
        "certificate-expiry":
        1,
        "utxo-pubkey":
        "02616c56412eb738a9eacfb0550b43a5a2e77e5d5205ea9e2ca8dfac34e50c9754",
        "txid":
        "84c88fafe0bb75f507fe3bfb29a93d10b2e80c15a63b2943c1a5fecb5a55cba2",
        "vout":
        1,
        "locktime":
        1893456000
    }, "J54LS6YyJPoseqFS", "J55VZ6U6ZyFDNeuv"))

    ob = get_ob()

    fidelity_bond_proof1, maker_nick1, taker_nick1 = fidelity_bond_info[0]
    proof = FidelityBondProof(
        maker_nick1, taker_nick1,
        hextobin(fidelity_bond_proof1['certificate-pubkey']),
        fidelity_bond_proof1['certificate-expiry'],
        hextobin(fidelity_bond_proof1['certificate-signature']),
        (hextobin(fidelity_bond_proof1['txid']), fidelity_bond_proof1['vout']),
        hextobin(fidelity_bond_proof1['utxo-pubkey']),
        fidelity_bond_proof1['locktime'])
    serialized1 = proof._serialize_proof_msg(
        fidelity_bond_proof1['nick-signature'])
    ob.msgchan.nick = taker_nick1

    ob.on_fidelity_bond_seen(maker_nick1, fidelity_bond_cmd_list[0],
                             serialized1)
    rows = ob.db.execute("SELECT * FROM fidelitybonds;").fetchall()
    assert len(rows) == 1

    #show the same fidelity bond message again, check it gets rejected as duplicate
    ob.on_fidelity_bond_seen(maker_nick1, fidelity_bond_cmd_list[0],
                             serialized1)
    rows = ob.db.execute("SELECT * FROM fidelitybonds;").fetchall()
    assert len(rows) == 1

    #show a different fidelity bond and check it does get accepted
    fidelity_bond_proof2, maker_nick2, taker_nick2 = fidelity_bond_info[1]
    proof2 = FidelityBondProof(
        maker_nick1, taker_nick1,
        hextobin(fidelity_bond_proof2['certificate-pubkey']),
        fidelity_bond_proof2['certificate-expiry'],
        hextobin(fidelity_bond_proof2['certificate-signature']),
        (hextobin(fidelity_bond_proof2['txid']), fidelity_bond_proof2['vout']),
        hextobin(fidelity_bond_proof2['utxo-pubkey']),
        fidelity_bond_proof2['locktime'])
    serialized2 = proof2._serialize_proof_msg(
        fidelity_bond_proof2['nick-signature'])
    ob.msgchan.nick = taker_nick2

    ob.on_fidelity_bond_seen(maker_nick2, fidelity_bond_cmd_list[0],
                             serialized2)
    rows = ob.db.execute("SELECT * FROM fidelitybonds;").fetchall()
    assert len(rows) == 2
Esempio n. 5
0
    def transaction_monitor(self):
        """Keeps track of any changes in the wallet (new transactions).
        Intended to be run as a twisted task.LoopingCall so that this
        Service is constantly in near-realtime sync with the blockchain.
        """

        if not self.update_blockheight():
            return

        txlist = self.bci.list_transactions(100)
        if not txlist:
            return

        new_txs = []
        for x in txlist:
            # process either (a) a completely new tx or
            # (b) a tx that reached unconf status but we are still
            # waiting for conf (active_txids)
            if "txid" not in x:
                continue
            if x['txid'] in self.active_txids or x['txid'] not in self.old_txs:
                new_txs.append(x)
        # reset for next polling event:
        self.old_txs = [x['txid'] for x in txlist if "txid" in x]

        for tx in new_txs:
            txid = tx["txid"]
            res = self.bci.get_transaction(hextobin(txid))
            if not res:
                continue
            confs = res["confirmations"]
            if not isinstance(confs, Integral):
                jlog.warning("Malformed gettx result: " + str(res))
                continue
            if confs < 0:
                jlog.info("Transaction: " + txid +
                          " has a conflict, abandoning.")
                continue
            if confs == 0:
                height = None
            else:
                height = self.current_blockheight - confs + 1

            txd = self.bci.get_deser_from_gettransaction(res)
            if txd is None:
                continue
            removed_utxos, added_utxos = self.wallet.process_new_tx(
                txd, height)
            if txid not in self.processed_txids:
                # apply checks to disable/freeze utxos to reused addrs if needed:
                self.check_for_reuse(added_utxos)
                # TODO note that this log message will be missed if confirmation
                # is absurdly fast, this is considered acceptable compared with
                # additional complexity.
                self.log_new_tx(removed_utxos, added_utxos, txid)
                self.processed_txids.append(txid)

            # first fire 'all' type callbacks, irrespective of if the
            # transaction pertains to anything known (but must
            # have correct label per above); filter on this Joinmarket wallet label,
            # or the external monitoring label:
            if (self.bci.is_address_labeled(tx, self.get_wallet_name())
                    or self.bci.is_address_labeled(
                        tx, self.EXTERNAL_WALLET_LABEL)):
                for f in self.callbacks["all"]:
                    # note we need no return value as we will never
                    # remove these from the list
                    f(txd, txid)

            # The tuple given as the second possible key for the dict
            # is such because txid is not always available
            # at the time of callback registration).
            possible_keys = [
                txid,
                tuple((x.scriptPubKey, x.nValue) for x in txd.vout)
            ]

            # note that len(added_utxos) > 0 is not a sufficient condition for
            # the tx being new, since wallet.add_new_utxos will happily re-add
            # a utxo that already exists; but this does not cause re-firing
            # of callbacks since we in these cases delete the callback after being
            # called once.
            # Note also that it's entirely possible that there are only removals,
            # not additions, to the utxo set, specifically in sweeps to external
            # addresses. In this case, since removal can by definition only
            # happen once, we must allow entries in self.active_txids through the
            # filter.
            if len(added_utxos) > 0 or len(removed_utxos) > 0 \
               or txid in self.active_txids:
                if confs == 0:
                    for k in possible_keys:
                        if k in self.callbacks["unconfirmed"]:
                            for f in self.callbacks["unconfirmed"][k]:
                                # True implies success, implies removal:
                                if f(txd, txid):
                                    self.callbacks["unconfirmed"][k].remove(f)
                                    # keep monitoring for conf > 0:
                                    self.active_txids.append(txid)
                elif confs > 0:
                    for k in possible_keys:
                        if k in self.callbacks["confirmed"]:
                            for f in self.callbacks["confirmed"][k]:
                                if f(txd, txid, confs):
                                    self.callbacks["confirmed"][k].remove(f)
                                    if txid in self.active_txids:
                                        self.active_txids.remove(txid)