Ejemplo n.º 1
0
    def test_calcTicketProfits(self):
        class FakeAccount(account.Account):
            def __init__(self):
                pass

        db = KeyValueDatabase(":memory:").child("tmp")

        acct = FakeAccount()
        tDB = acct.ticketDB = db.child("tickets",
                                       datatypes=("TEXT", "BLOB"),
                                       blobber=account.UTXO)

        def newTinfo(poolFee, purchaseTxFee, spendTxFee, stakebase):
            return account.TicketInfo(
                status="",
                purchaseBlock=account.TinyBlock(ByteArray(0), 0),
                maturityHeight=0,
                expirationHeight=0,
                lotteryBlock=None,
                vote=None,
                revocation=None,
                poolFee=poolFee,
                purchaseTxFee=purchaseTxFee,
                spendTxFee=spendTxFee,
                stakebase=stakebase,
            )

        utxo = account.UTXO(
            address="",
            txHash=reversed(ByteArray("aa")),
            vout=0,
            scriptPubKey=None,
            satoshis=5,
        )

        tinfo = newTinfo(0, 1, 1, 1)
        utxo.tinfo = tinfo
        tDB["aa"] = utxo
        tinfo = newTinfo(1, 1, 1, 1)
        utxo.tinfo = tinfo
        tDB["ab"] = utxo
        tinfo = newTinfo(0, 1, 0, 0)
        utxo.tinfo = tinfo
        tDB["ac"] = utxo

        stakebases, poolFees, txFees = acct.calcTicketProfits()

        s, p, t = 2, 1, 5

        assert stakebases == s
        assert poolFees == p
        assert txFees == t
Ejemplo n.º 2
0
    def test_updateSpentTickets(self):
        class Dummy:
            pass

        class FakeAccount(account.Account):
            def __init__(self):
                self.signals = Dummy()
                pool = Dummy()
                pool.purchaseInfo = Dummy()
                pool.purchaseInfo.ticketAddress = "ticketaddress"
                self.stakePools = [pool]
                self.blockchain = Dummy()
                self.mempool = {}
                self.txs = {}
                self.utxos = {}
                self.netParams = nets.testnet

        db = KeyValueDatabase(":memory:").child("tmp")

        acct = FakeAccount()
        tDB = acct.ticketDB = db.child("tickets",
                                       datatypes=("TEXT", "BLOB"),
                                       blobber=account.UTXO)

        def fail():
            assert False

        acct.signals.spentTickets = fail

        # tickets and ticketDB empty, noop
        acct.updateSpentTickets()

        stakeSubmission = ByteArray(opcode.OP_SSTX)
        stakeSubmission += opcode.OP_HASH160
        stakeSubmission += opcode.OP_DATA_20
        stakeSubmission += 1 << (8 * 19)
        stakeSubmission += opcode.OP_EQUAL

        def newTinfo(status="live"):
            return account.TicketInfo(
                status=status,
                purchaseBlock=account.TinyBlock(ByteArray(0), 42),
                maturityHeight=0,
                expirationHeight=0,
                lotteryBlock=None,
                vote=None,
                revocation=None,
                poolFee=0,
                purchaseTxFee=1,
                spendTxFee=0,
                stakebase=0,
            )

        txid = "aa"

        def utxoWithTxid(txid):
            return account.UTXO(
                address="ticketaddress",
                txHash=reversed(ByteArray(txid)),
                vout=0,
                scriptPubKey=stakeSubmission,
                satoshis=2,
                tinfo=newTinfo(),
            )

        utxo = utxoWithTxid(txid)

        def txWithTxid(txid):
            txInOne = msgtx.TxIn(msgtx.OutPoint(ByteArray("ff"), 0, 0),
                                 valueIn=1)
            txInTwo = msgtx.TxIn(None, valueIn=3)
            txOutOne = msgtx.TxOut(pkScript=stakeSubmission, value=3)
            txOutTwo = msgtx.TxOut()
            txOutThree = msgtx.TxOut()
            txOutFour = msgtx.TxOut()
            txOutFive = msgtx.TxOut()
            txsIn = [txInOne, txInTwo]
            txsOut = [txOutOne, txOutTwo, txOutThree, txOutFour, txOutFive]
            return msgtx.MsgTx(reversed(ByteArray(txid)), None, None, txsIn,
                               txsOut, None, None)

        tx = txWithTxid(txid)
        utxo.tinfo.status = "mempool"
        tDB[txid] = utxo
        acct.mempool[txid] = tx

        # mempool and ticketDB have the same txid and status, noop
        acct.updateSpentTickets()

        called = False

        def ok():
            nonlocal called
            called = True

        acct.signals.spentTickets = ok

        tinfos = {
            "aa": utxo.tinfo,
            "ab": newTinfo(),
            "ac": newTinfo(),
            "ad": newTinfo(),
            "ae": newTinfo(status="unconfirmed"),
        }

        txs = {k: txWithTxid(k) for k in tinfos.keys() if k != "ab"}

        blockheader = Dummy()
        blockheader.height = 0
        blockheader.timestamp = 0

        vote = "ff"
        revocation = "fe"

        def setVoteOrRevoke(txid):
            if txid == vote:
                ticket = tDB["ac"]
                ticket.tinfo.vote = reversed(ByteArray(vote))
            if txid == revocation:
                ticket = tDB["ad"]
                ticket.tinfo.revocation = reversed(ByteArray(revocation))

        acct.spendTicket = lambda tx: setVoteOrRevoke(
            reversed(tx.cachedH).hex())
        acct.blockchain.tx = lambda txid: txs[txid]
        acct.blockchain.ticketInfo = lambda txid: tinfos[txid]
        acct.blockchain.blockForTx = lambda *args: blockheader
        acct.utxos = {k + "#0": utxoWithTxid(k) for k in txs.keys()}
        acct.mempool = {"ab": txWithTxid("ab")}

        txs[vote] = txWithTxid(vote)
        txs[revocation] = txWithTxid(revocation)

        # Live tickets are now different than database.
        acct.updateSpentTickets()
        assert called

        # The tickets are now stored in the database.
        assert "ab" in tDB and "ac" in tDB and "ad" in tDB
        # They are unspent tickets.
        ut = acct.unspentTickets()
        assert "ab" in ut and "ac" in ut and "ad" in ut

        called = False
        txid = "ac"
        tinfos["ac"].vote = reversed(ByteArray(vote))
        del acct.utxos[txid + "#0"]

        # A ticket has been voted.
        acct.updateSpentTickets()
        assert called
        # It is an voted ticket.
        assert txid in acct.votedTickets() and txid not in acct.unspentTickets(
        )

        called = False
        txid = "ad"
        tinfos["ad"].revocation = reversed(ByteArray(revocation))
        del acct.utxos[txid + "#0"]

        # A ticket has been revoked.
        acct.updateSpentTickets()
        assert called
        # It is a revoked ticket.
        assert txid in acct.revokedTickets(
        ) and txid not in acct.unspentTickets()

        txid = "af"
        called = False
        tDB[txid] = utxo

        # A txid is in the ticketDB but not in utxos or mempool or the
        # blockchain.
        acct.updateSpentTickets()
        assert called
        # It was removed.
        assert txid not in tDB
Ejemplo n.º 3
0
    def test_spendTicket(self, monkeypatch):
        """
        Test updating spent tickets.
        """
        vote = ("010000000200000000000000000000000000000000000000000000000000"
                "00000000000000ffffffff00ffffffff571ce9fb0c52ae22c3a6480cbf6d"
                "30ff76bdffbf54b6e081eb218aa3a0ca2bc40000000001ffffffff040000"
                "0000000000000000266a2432c0c546b332f7abf51f3fc73f4482185f4c09"
                "61625763a766774237280000007f75050000000000000000000000086a06"
                "050008000000900102000000000000001abb76a914781f472a926da0bb7c"
                "e9eec7f4d434de21015cae88acd9b293890100000000001abb76a9149087"
                "5ba5fee5f35352e9171d40190e796b1b152788ac000000000000000002a6"
                "3895010000000000000000ffffffff020000c47b00880100000049700500"
                "0600000091483045022100c1ec49cb687fa2421e76b534ced49563b3de1e"
                "c6407b1bbfda26752fbdedc88302204988390ea3be77324909781322a46b"
                "463d00dd14718f0964b9536b5eef4e35570147512103af3c24d005ca8b75"
                "5e7167617f3a5b4c60a65f8318a7fcd1b0cacb1abd2a97fc21027b81bc16"
                "954e28adb832248140eb58bedb6078ae5f4dabf21fde5a8ab7135cb652ae")

        revocation = ("010000000139cb023fdcf6fcd18273c7395f51084721d0e4baf0ca"
                      "3ee9590ad63a411a4cb30000000001ffffffff02f0ff0100000000"
                      "0000001abc76a914781f472a926da0bb7ce9eec7f4d434de21015c"
                      "ae88ac8fcc07ba0100000000001abc76a914f90abbb67dc9257efa"
                      "6ab24eb88e2755f34b1f7f88ac0000000000000000018ad609ba01"
                      "000000296505001000000091483045022100bc9a694d0864df030e"
                      "6edea181a2e2385dfbf93396b5792899a65f19c9afd67a02206052"
                      "192b5631e062f4497706276ec514a14cdfa8035ef6c7dca0df7120"
                      "84618e0147512103af3c24d005ca8b755e7167617f3a5b4c60a65f"
                      "8318a7fcd1b0cacb1abd2a97fc21027b81bc16954e28adb8322481"
                      "40eb58bedb6078ae5f4dabf21fde5a8ab7135cb652ae")

        # Tickets can be found on testnet3.
        ticketVotedTxid = (
            "c42bcaa0a38a21eb81e0b654bfffbd76ff306dbf0c48a6c322ae520cfbe91c57")
        ticketRevokedTxid = (
            "b34c1a413ad60a59e93ecaf0bae4d0214708515f39c77382d1fcf6dc3f02cb39")

        voteTxid = "aa19094e404a1ee056760bdb1b7ed1b6c8e5f1d97752335eddbfdfa19e76c262"
        revocationTxid = (
            "d85694ba7aae060667b393558cd96c2df2926426f80db16a18bf4fc9102b0953")

        class Dummy:
            pass

        class FakeAccount(account.Account):
            def __init__(self):
                self.signals = Dummy()
                self.blockchain = Dummy()
                self.netParams = nets.testnet

        db = KeyValueDatabase(":memory:").child("tmp")

        acct = FakeAccount()
        tDB = acct.ticketDB = db.child("tickets",
                                       datatypes=("TEXT", "BLOB"),
                                       blobber=account.UTXO)

        def newTinfo(status):
            return account.TicketInfo(
                status=status,
                purchaseBlock=account.TinyBlock(ByteArray(0), 0),
                maturityHeight=0,
                expirationHeight=0,
            )

        utxo = account.UTXO(
            address="",
            txHash=reversed(ByteArray("aa")),
            vout=0,
            scriptPubKey=None,
            satoshis=5,
        )

        txidToTinfo = {
            voteTxid: newTinfo("vote"),
            revocationTxid: newTinfo("revocation"),
        }
        acct.blockchain.ticketInfoForSpendingTx = lambda txid, netParams: txidToTinfo[
            txid]

        tDB[ticketVotedTxid] = utxo
        tDB[ticketRevokedTxid] = utxo

        v = msgtx.MsgTx.deserialize(ByteArray(vote))

        acct.spendTicket(v)
        tinfo = tDB[ticketVotedTxid].tinfo
        assert tinfo.status == "vote"

        rev = msgtx.MsgTx.deserialize(ByteArray(revocation))

        acct.spendTicket(rev)
        tinfo = tDB[ticketRevokedTxid].tinfo
        assert tinfo.status == "revocation"

        # Test the error case.
        def mock_isSSGen(tx):
            return False

        monkeypatch.setattr(txscript, "isSSGen", mock_isSSGen)
        with pytest.raises(DecredError):
            acct.spendTicket(v)