Ejemplo n.º 1
0
            def utxosource(amt, filter):
                nextVal = 10
                total = 0
                utxos = []

                while total < amt:
                    atoms = int(nextVal * 1e8)
                    privKey = Curve.generateKey()
                    pkHash = crypto.hash160(
                        privKey.pub.serializeCompressed().b)
                    addr = addrlib.AddressPubKeyHash(pkHash, testnet)
                    addrs.append(addr)
                    addrString = addr.string()
                    keys[addrString] = privKey
                    pkScript = txscript.makePayToAddrScript(
                        addrString, testnet)
                    txHash = rando.newHash()
                    txid = reversed(txHash).hex()
                    utxos.append(
                        account.UTXO(
                            address=addrString,
                            txHash=txHash,
                            vout=0,
                            ts=int(time.time()),
                            scriptPubKey=pkScript,
                            satoshis=atoms,
                        ))
                    tx = msgtx.MsgTx.new()
                    tx.addTxOut(msgtx.TxOut(value=atoms, pkScript=pkScript))
                    txs[txid] = tx
                    total += atoms
                    nextVal *= 2
                return utxos, True
Ejemplo n.º 2
0
 def utxoWithTxid(txid):
     return account.UTXO(
         address="ticketaddress",
         txHash=reversed(ByteArray(txid)),
         vout=0,
         scriptPubKey=stakeSubmission,
         satoshis=2,
         tinfo=newTinfo(),
     )
Ejemplo n.º 3
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.º 4
0
def test_Client(dcrdConfig):
    if dcrdConfig is None:
        pytest.skip("did not locate a dcrd config file")
    rpcClient = rpc.Client(
        urlunsplit(("https", dcrdConfig["rpclisten"], "/", "", "")),
        dcrdConfig["rpcuser"],
        dcrdConfig["rpcpass"],
        dcrdConfig["rpccert"],
    )

    stringify = rpc.stringify
    assert stringify(ByteArray("face")) == "cefa"
    assert stringify([ByteArray("face"),
                      ByteArray("baadf00d")]) == ["cefa", "0df0adba"]
    assert stringify("face") == "face"
    assert stringify(["face", "baadf00d"]) == ["face", "baadf00d"]
    alist = [ByteArray("face"), "baadf00d", ByteArray("deadbeef")]
    alistReversed = ["cefa", "baadf00d", "efbeadde"]
    assert set(stringify((b for b in alist))) == set(
        (s for s in alistReversed))
    assert stringify([
        ByteArray("face"),
        (ByteArray("badd"), "d00d", ByteArray("babe")),
        [alist, [alist], [alist, alist, [alist]]],
        [ByteArray("feed"), ByteArray("1100")],
        set({
            "hey": ByteArray("1234"),
            "there": ByteArray("4321")
        }.keys()),
        ByteArray("baadf00d"),
    ]) == [
        "cefa",
        ("ddba", "d00d", "beba"),
        [
            ["cefa", "baadf00d", "efbeadde"],
            [["cefa", "baadf00d", "efbeadde"]],
            [
                ["cefa", "baadf00d", "efbeadde"],
                ["cefa", "baadf00d", "efbeadde"],
                [["cefa", "baadf00d", "efbeadde"]],
            ],
        ],
        ["edfe", "0011"],
        {"there", "hey"},
        "0df0adba",
    ]

    with pytest.raises(DecredError):
        rpcClient.call("no_such_method")

    assert rpcClient.addNode("127.0.0.1", "onetry") is None

    debugLevel = rpcClient.debugLevel("show")
    assert isinstance(debugLevel, str)

    debugLevel = rpcClient.debugLevel("info")
    assert debugLevel == "Done."

    estimateFee = rpcClient.estimateFee()
    assert isinstance(estimateFee, int)

    estimateSmartFee = rpcClient.estimateSmartFee(32)
    assert isinstance(estimateSmartFee, int)

    estimateStakeDiff = rpcClient.estimateStakeDiff(0)
    assert isinstance(estimateStakeDiff, rpc.EstimateStakeDiffResult)

    existsAddress = rpcClient.existsAddress(mainnetAddress)
    assert existsAddress

    existsAddress = rpcClient.existsAddress(cookedAddress1)
    assert not existsAddress

    existsAddresses = rpcClient.existsAddresses([
        cookedAddress1,
        cookedAddress2,
        mainnetAddress,
        cookedAddress2,
        cookedAddress1,
        cookedAddress2,
        mainnetAddress,
    ])
    assert existsAddresses == [False, False, True, False, False, False, True]

    liveTickets = rpcClient.liveTickets()
    assert isinstance(liveTickets, list)

    aTicket = liveTickets[0]

    for ticket in liveTickets:
        getRawTransaction = rpcClient.getRawTransaction(ticket)

        script = getRawTransaction.txOut[0].pkScript

        if txscript.extractStakeScriptHash(script, opcode.OP_SSTX):
            decodeScript = rpcClient.decodeScript(script)
            assert isinstance(decodeScript, rpc.DecodeScriptResult)
            break

    else:
        raise RuntimeError("did not find a suitable script to decode")

    existsExpiredTickets = rpcClient.existsExpiredTickets([aTicket, aTicket])
    assert existsExpiredTickets == [False, False]

    bestBlock = rpcClient.getBestBlock()
    assert isinstance(bestBlock, rpc.GetBestBlockResult)

    blockchainInfo = rpcClient.getBlockchainInfo()
    assert isinstance(blockchainInfo, rpc.GetBlockChainInfoResult)

    getAddedNodeInfo = rpcClient.getAddedNodeInfo(True)
    assert isinstance(getAddedNodeInfo, list)

    getAddedNodeInfo = rpcClient.getAddedNodeInfo(False)
    assert isinstance(getAddedNodeInfo, list)

    getBestBlockHash = rpcClient.getBestBlockHash()
    assert isinstance(getBestBlockHash, ByteArray)

    getBlock = rpcClient.getBlock(blkHash414000, False)
    assert getBlock == blkHex414000

    getBlock = rpcClient.getBlock(blkHash414000)
    assert isinstance(getBlock, rpc.GetBlockVerboseResult)

    getBlock = rpcClient.getBlock(blkHash414000, True, True)
    assert isinstance(getBlock, rpc.GetBlockVerboseResult)

    getBlockCount = rpcClient.getBlockCount()
    assert isinstance(getBlockCount, int)

    getBlockHash = rpcClient.getBlockHash(0)
    assert getBlockHash == reversed(ByteArray(genesisHash))

    getBlockHeader = rpcClient.getBlockHeader(blkHash414000)
    assert isinstance(getBlockHeader, rpc.GetBlockHeaderVerboseResult)

    getBlockHeader = rpcClient.getBlockHeader(blkHash414000, False)
    assert isinstance(getBlockHeader, BlockHeader)

    getBlockSubsidy = rpcClient.getBlockSubsidy(414500, 5)
    assert isinstance(getBlockSubsidy, rpc.GetBlockSubsidyResult)

    getCFilter = rpcClient.getCFilter(blkHash414000, "extended")
    assert getCFilter == cFilter414000

    getCFilterHeader = rpcClient.getCFilterHeader(blkHash414000, "extended")
    assert getCFilterHeader == cFilterHeader414000

    getCFilterV2 = rpcClient.getCFilterV2(blkHash414000)
    assert isinstance(getCFilterV2, rpc.GetCFilterV2Result)

    getChainTips = rpcClient.getChainTips()
    assert isinstance(getChainTips[0], rpc.GetChainTipsResult)

    getCoinSupply = rpcClient.getCoinSupply()
    assert isinstance(getCoinSupply, int)

    getConnectionCount = rpcClient.getConnectionCount()
    assert isinstance(getConnectionCount, int)

    getCurrentNet = rpcClient.getCurrentNet()
    assert isinstance(getCurrentNet, int)

    getDifficulty = rpcClient.getDifficulty()
    assert isinstance(getDifficulty, float)

    getGenerate = rpcClient.getGenerate()
    assert isinstance(getGenerate, bool)

    getHashesPerSec = rpcClient.getHashesPerSec()
    assert isinstance(getHashesPerSec, int)

    getInfo = rpcClient.getInfo()
    assert isinstance(getInfo, rpc.InfoChainResult)

    getMempoolInfo = rpcClient.getMempoolInfo()
    assert isinstance(getMempoolInfo, rpc.GetMempoolInfoResult)

    getMiningInfo = rpcClient.getMiningInfo()
    assert isinstance(getMiningInfo, rpc.GetMiningInfoResult)

    getNetTotals = rpcClient.getNetTotals()
    assert isinstance(getNetTotals, rpc.GetNetTotalsResult)

    getNetworkHashPS = rpcClient.getNetworkHashPS()
    assert isinstance(getNetworkHashPS, int)

    getNetworkInfo = rpcClient.getNetworkInfo()
    assert isinstance(getNetworkInfo, rpc.GetNetworkInfoResult)

    getPeerInfo = rpcClient.getPeerInfo()
    assert isinstance(getPeerInfo[0], rpc.GetPeerInfoResult)

    getRawMempool = rpcClient.getRawMempool()
    assert isinstance(getRawMempool, list)
    mempoolTx = getRawMempool[0]

    existsMempoolTxs = rpcClient.existsMempoolTxs(getRawMempool[:3] +
                                                  [aTicket])
    assert existsMempoolTxs == [True, True, True, False]

    getRawMempool = rpcClient.getRawMempool(True)
    assert isinstance(getRawMempool[reversed(mempoolTx).hex()],
                      rpc.GetRawMempoolVerboseResult)

    getHeaders = rpcClient.getHeaders([blkHash414000], blkHash414005)
    assert blkHeader414002 in [header.serialize() for header in getHeaders]

    # This test will fail if --addrindex is not enabled in dcrd.
    getRawTransaction = rpcClient.getRawTransaction(aTicket)
    assert isinstance(getRawTransaction, MsgTx)

    decodeRawTransaction = rpcClient.decodeRawTransaction(getRawTransaction)
    assert isinstance(decodeRawTransaction, rpc.RawTransactionResult)

    rawaddr = txscript.extractStakeScriptHash(
        getRawTransaction.txOut[0].pkScript, opcode.OP_SSTX)
    if rawaddr:
        addressWithTickets = addrlib.AddressScriptHash(rawaddr,
                                                       mainnet).string()
    else:
        rawaddr = txscript.extractStakePubKeyHash(
            getRawTransaction.txOut[0].pkScript, opcode.OP_SSTX)
        addressWithTickets = addrlib.AddressPubKeyHash(rawaddr,
                                                       mainnet).string()

    getRawTransaction = rpcClient.getRawTransaction(aTicket, 1)
    assert isinstance(getRawTransaction, rpc.RawTransactionResult)

    getStakeDifficulty = rpcClient.getStakeDifficulty()
    assert isinstance(getStakeDifficulty, rpc.GetStakeDifficultyResult)

    getStakeVersionInfo = rpcClient.getStakeVersionInfo()
    assert isinstance(getStakeVersionInfo, rpc.GetStakeVersionInfoResult)

    getStakeVersions = rpcClient.getStakeVersions(blkHash414000, 3)
    assert isinstance(getStakeVersions[0], rpc.GetStakeVersionsResult)

    getTicketPoolValue = rpcClient.getTicketPoolValue()
    assert isinstance(getTicketPoolValue, float)

    getVoteInfo = rpcClient.getVoteInfo(7)
    assert isinstance(getVoteInfo, rpc.GetVoteInfoResult)

    # getWork will fail if --mininaddr is not set when starting dcrd
    with pytest.raises(DecredError):
        rpcClient.getWork()
    # getWork = rpcClient.getWork()
    # assert isinstance(getWork, rpc.GetWorkResult)

    dcrdHelp = rpcClient.help()
    assert isinstance(dcrdHelp, str)

    dcrdHelp = rpcClient.help("getinfo")
    assert isinstance(dcrdHelp, str)

    missedTickets = rpcClient.missedTickets()
    assert isinstance(missedTickets, list)

    assert rpcClient.node("connect", "127.0.0.1", "temp") is None

    revocableTicket = rpcClient.getRawTransaction(missedTickets[0])

    # create a ticket revoking transaction using txscript
    revocation = txscript.makeRevocation(revocableTicket, 3000)
    createRawSSRTx = rpcClient.createRawSSRTx(revocableTicket, 3000)

    # ours is just missing the block index
    revocation.txIn[0].blockIndex = createRawSSRTx.txIn[0].blockIndex
    assert createRawSSRTx.txHex() == revocation.txHex()

    # Using the revocation as an unspent output
    amt = revocableTicket.txOut[0].value
    script = revocableTicket.txOut[0].pkScript

    utxo = account.UTXO(
        address="",
        txHash=revocableTicket.hash(),
        vout=0,
        ts=None,
        scriptPubKey=script,
        satoshis=1,
        maturity=0,
        tinfo=None,
    )
    utxo2 = account.UTXO(
        address="",
        txHash=revocableTicket.hash(),
        vout=0,
        ts=None,
        scriptPubKey=script,
        satoshis=amt,
        maturity=0,
        tinfo=None,
    )
    amount = {cookedAddress2: amt + 1}

    zeroed = ByteArray(b"", length=20)
    changeAddr = addrlib.AddressPubKeyHash(zeroed, mainnet,
                                           crypto.STEcdsaSecp256k1).string()
    # only the first argument for couts is a non-zero value
    cout = rpc.COut(
        addr=mainnetAddress,
        commitAmt=0,
        changeAddr=changeAddr,
        changeAmt=0,
    )

    op = OutPoint(txHash=revocableTicket.hash(), idx=0, tree=wire.TxTreeStake)
    inputPool = txscript.ExtendedOutPoint(
        op=op,
        amt=1,
        pkScript=script,
    )
    inputMain = txscript.ExtendedOutPoint(
        op=op,
        amt=amt,
        pkScript=script,
    )
    ticketAddr = addrlib.AddressScriptHash(
        ByteArray(b58decode(cookedAddress2)[2:-4]), mainnet)
    mainAddr = addrlib.AddressScriptHash(
        ByteArray(b58decode(mainnetAddress)[2:-4]), mainnet)

    # create a ticket purchasing transaction using txscript
    ticketPurchase = txscript.makeTicket(mainnet, inputPool, inputMain,
                                         ticketAddr, mainAddr, amt + 1,
                                         mainAddr, 0)
    createRawSSTx = rpcClient.createRawSSTx([utxo, utxo2], amount,
                                            [cout, cout])

    # ours is just missing the block index
    ticketPurchase.txIn[0].blockIndex = createRawSSTx.txIn[0].blockIndex
    ticketPurchase.txIn[1].blockIndex = createRawSSTx.txIn[1].blockIndex
    assert createRawSSTx.txHex() == ticketPurchase.txHex()

    amount = {mainnetAddress: amt}
    txIn = TxIn(previousOutPoint=op, valueIn=amt)
    txOut = TxOut(
        value=amt,
        version=0,
        pkScript=txscript.payToAddrScript(mainAddr),
    )
    rawTx = MsgTx(
        serType=wire.TxSerializeFull,
        version=1,
        txIn=[txIn],
        txOut=[txOut],
        lockTime=0,
        expiry=0,
        cachedHash=None,
    )

    createRawTransaction = rpcClient.createRawTransaction([utxo2], amount)

    rawTx.txIn[0].blockIndex = createRawTransaction.txIn[0].blockIndex
    assert createRawTransaction.txHex() == rawTx.txHex()

    getTxOut = rpcClient.getTxOut(missedTickets[0], 0)
    assert isinstance(getTxOut, rpc.GetTxOutResult)

    existsLiveTicket = rpcClient.existsLiveTicket(liveTickets[0])
    assert existsLiveTicket

    existsLiveTicket = rpcClient.existsLiveTicket(missedTickets[0])
    assert not existsLiveTicket

    existsLiveTickets = rpcClient.existsLiveTickets(liveTickets[:5] +
                                                    missedTickets[:1] +
                                                    liveTickets[:2])
    assert existsLiveTickets == [
        True, True, True, True, True, False, True, True
    ]

    existsMissedTickets = rpcClient.existsMissedTickets(missedTickets[:8])
    assert existsMissedTickets == [True for _ in range(8)]

    with pytest.raises(DecredError):
        rpcClient.generate(2)

    rpcClient.ping()

    searchRawTransactions = rpcClient.searchRawTransactions(mainnetAddress)
    assert isinstance(searchRawTransactions[0], rpc.RawTransactionResult)

    searchRawTransactions = rpcClient.searchRawTransactions(
        mainnetAddress, False)
    msgtx = searchRawTransactions[0]
    assert isinstance(msgtx, MsgTx)

    with pytest.raises(DecredError):
        rpcClient.sendRawTransaction(msgtx)

    assert rpcClient.setGenerate(False) is None

    with pytest.raises(DecredError):
        rpcClient.submitBlock(ByteArray(b""))

    ticketFeeInfo = rpcClient.ticketFeeInfo()
    assert isinstance(ticketFeeInfo, rpc.TicketFeeInfoResult)

    ticketFeeInfo = rpcClient.ticketFeeInfo(5, 5)
    assert isinstance(ticketFeeInfo, rpc.TicketFeeInfoResult)

    ticketsForAddress = rpcClient.ticketsForAddress(addressWithTickets)
    assert aTicket in ticketsForAddress

    ticketVWAP = rpcClient.ticketVWAP()
    assert isinstance(ticketVWAP, float)

    ticketVWAP = rpcClient.ticketVWAP(414500)
    assert isinstance(ticketVWAP, float)

    ticketVWAP = rpcClient.ticketVWAP(414500, 414510)
    assert isinstance(ticketVWAP, float)

    txFeeInfo = rpcClient.txFeeInfo()
    assert isinstance(txFeeInfo, rpc.TxFeeInfoResult)

    txFeeInfo = rpcClient.txFeeInfo(5)
    assert isinstance(txFeeInfo, rpc.TxFeeInfoResult)
    tip = txFeeInfo.feeInfoBlocks[0].height

    txFeeInfo = rpcClient.txFeeInfo(5, tip - 5, tip)
    assert isinstance(txFeeInfo, rpc.TxFeeInfoResult)

    validateAddress = rpcClient.validateAddress(mainnetAddress)
    assert isinstance(validateAddress, rpc.ValidateAddressChainResult)
    assert validateAddress.isValid

    # Address for wrong network.
    validateAddress = rpcClient.validateAddress(testnetAddress)
    assert isinstance(validateAddress, rpc.ValidateAddressChainResult)
    assert not validateAddress.isValid

    # Address is bogus.
    validateAddress = rpcClient.validateAddress(nonsense)
    assert isinstance(validateAddress, rpc.ValidateAddressChainResult)
    assert not validateAddress.isValid

    verifyChain = rpcClient.verifyChain()
    assert verifyChain

    verifyMessage = rpcClient.verifyMessage(ownedAddress, signedMessage,
                                            message)
    assert verifyMessage

    # Signature is bogus.
    verifyMessage = rpcClient.verifyMessage(
        ownedAddress,
        nonsense,
        message,
    )
    assert not verifyMessage

    version = rpcClient.version()
    assert isinstance(version["dcrd"], rpc.VersionResult)
    assert isinstance(version["dcrdjsonrpcapi"], rpc.VersionResult)
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    def test_main(self, monkeypatch):
        """
        Test account functionality.
        """
        # Set up globals for test.
        monkeypatch.setattr(account, "DefaultGapLimit", 2)

        db = KeyValueDatabase(":memory:").child("tmp")
        acct = self.newAccount(db)
        acct.unlock(self.cryptoKey)
        acct.generateGapAddresses()
        for _ in range(20):
            acct.nextExternalAddress()
        satoshis = int(round(5 * 1e8))
        txHash = rando.newHash()
        txid = reversed(txHash).hex()
        vout = 2
        address = acct.nextExternalAddress()

        utxo = account.UTXO(
            address=address,
            txHash=txHash,
            vout=vout,
            scriptPubKey=ByteArray(0),
            satoshis=satoshis,
            maturity=1,
        )

        # A helper function to get the current utxo count.
        utxocount = lambda: len(list(acct.utxoscan()))

        # Add the utxo
        acct.addUTXO(utxo)
        # Check the count and balance categories.
        assert utxocount() == 1
        assert acct.calcBalance(1).total == satoshis
        assert acct.calcBalance(1).available == satoshis
        assert acct.calcBalance(0).available == 0
        # Helper functions.
        assert acct.caresAboutTxid(txid)
        assert not acct.caresAboutTxid("")
        acct.addUTXO(utxo)
        assert utxocount() == 1
        acct.spendUTXO(utxo)
        assert utxocount() == 0

        b = acct.serialize()
        reAcct = account.Account.unblob(b.b)

        assert acct.pubKeyEncrypted == reAcct.pubKeyEncrypted
        assert acct.privKeyEncrypted == reAcct.privKeyEncrypted
        assert acct.name == reAcct.name
        assert acct.coinID == reAcct.coinID
        assert acct.netID == reAcct.netID
        assert acct.gapLimit == reAcct.gapLimit
        assert acct.cursorExt == reAcct.cursorExt
        assert acct.cursorInt == reAcct.cursorInt
        assert acct.gapLimit == reAcct.gapLimit

        # Create a faux blockchain for the account.
        class Blockchain:
            txidsForAddr = lambda addr: []
            UTXOs = lambda addrs: []
            tipHeight = 5

        acct.blockchain = Blockchain

        class Signals:
            b = None

            @classmethod
            def balance(cls, b):
                cls.b = b

        acct.signals = Signals

        # Add a txid for this first address
        txid = rando.newHash().hex()
        zerothAddr = acct.externalAddresses[0]
        acct.addTxid(zerothAddr, txid)
        # Add a voting service provider
        vspKey = rando.newKey().hex()
        ticketAddr = "ticketAddr"
        pi = PurchaseInfo("addr", 1.0, ByteArray(b"scripthashscript"),
                          ticketAddr, 1, 0, 2)
        vsp = VotingServiceProvider("https://myvsp.com", vspKey,
                                    nets.mainnet.Name, pi)
        with pytest.raises(DecredError):
            acct.setPool(None)
        acct.setPool(vsp)
        acct.setNewPool(vsp)
        # Add UTXOs
        utxos = [account.UTXO.parse(u) for u in self.dcrdataUTXOs]
        acct.resolveUTXOs(utxos)
        ticketStats = acct.ticketStats()
        assert ticketStats.count == 0
        assert ticketStats.value == 0

        # Check that the addresses txid and the vsp load from the database.
        acct.txs.clear()
        acct.stakePools.clear()
        acct.utxos.clear()
        assert acct.stakePool() is None
        acct.load(db, Blockchain, Signals)
        assert zerothAddr in acct.txs
        assert len(acct.txs[zerothAddr]) == 1
        assert acct.txs[zerothAddr][0] == txid
        assert len(acct.stakePools) == 1
        assert acct.stakePool().apiKey == vspKey
        assert acct.calcBalance().available == self.utxoTotal
        assert len(acct.getUTXOs(self.utxoTotal)[0]) == 2
        assert len(acct.getUTXOs(self.utxoTotal, lambda u: False)[0]) == 0
        assert acct.lastSeen(acct.externalAddresses, -1) == 0
        branch, idx = acct.branchAndIndex(zerothAddr)
        assert branch == account.EXTERNAL_BRANCH
        assert idx == 0
        branch, idx = acct.branchAndIndex(acct.internalAddresses[0])
        assert branch == account.INTERNAL_BRANCH
        assert idx == 0
        checkKey = acct.privKey.child(account.EXTERNAL_BRANCH).child(0).key
        with pytest.raises(DecredError):
            acct.privKeyForAddress("")
        assert acct.privKeyForAddress(zerothAddr).key == checkKey
        ticketAddrs = acct.addTicketAddresses([])
        assert len(ticketAddrs) == 1
        assert ticketAddrs[0] == ticketAddr
        assert acct.hasPool()

        # Exercise setNode
        acct.setNode(1)
        assert acct.node == 1

        # Add a coinbase transaction output to the account.
        coinbase = newCoinbaseTx()
        cbUTXO = account.UTXO("addr",
                              ByteArray(b"id"),
                              1,
                              height=5,
                              satoshis=int(1e8))
        coinbase.addTxOut(msgtx.TxOut(int(1e8)))
        coinbaseID = coinbase.id()
        cbUTXO.txid = coinbaseID
        acct.addUTXO(cbUTXO)
        acct.addMempoolTx(coinbase)
        assert coinbaseID in acct.mempool
        # Send a block signal and have the transaction confirmed.
        sig = {"message": {"block": {"Tx": [{"TxID": coinbaseID}]}}}
        acct.blockchain.tipHeight = 5
        acct.blockchain.tx = lambda *a: coinbase
        acct.blockSignal(sig)
        assert coinbaseID not in acct.mempool
        assert cbUTXO.key() in acct.utxos
        maturity = 5 + nets.mainnet.CoinbaseMaturity
        assert acct.utxos[cbUTXO.key()].maturity == maturity
        assert acct.calcBalance(
            maturity).available == self.utxoTotal + cbUTXO.satoshis
        assert acct.calcBalance(0).available == self.utxoTotal
        acct.spendUTXOs([cbUTXO])
        assert acct.calcBalance(maturity).available == self.utxoTotal

        # make a ticket and a vote
        ticket = account.UTXO.parse(dcrdataUTXO)
        utxo.setTicketInfo(dcrdataTinfo)
        utxo.scriptPubKey = ticketScript
        utxo.parseScriptClass()
        acct.addUTXO(ticket)
        expVal = acct.calcBalance().available - ticket.satoshis
        voteTx = msgtx.MsgTx.new()
        voteTx.addTxIn(
            msgtx.TxIn(
                msgtx.OutPoint(reversed(ByteArray(ticket.txid)), ticket.vout,
                               0)))
        acct.blockchain.tx = lambda *a: voteTx
        acct.addressSignal(ticketAddr, "somehash")
        assert Signals.b.available == expVal

        # Detect a new utxo for the account.
        newVal = int(5e8)
        expVal = acct.calcBalance().available + newVal
        addr = acct.externalAddresses[1]
        a = addrlib.decodeAddress(addr, nets.mainnet)
        script = txscript.payToAddrScript(a)
        newTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(newVal, script)
        newTx.addTxOut(op)
        acct.blockchain.tx = lambda *a: newTx
        utxo = account.UTXO(addr, ByteArray(b"txid"), 0, satoshis=newVal)
        acct.blockchain.txVout = lambda *a: utxo
        acct.addressSignal(addr, "somehash")
        assert Signals.b.available == expVal

        # test syncing. add a single output for external address #2
        acct.stakePool().getPurchaseInfo = lambda: None
        acct.stakePool().authorize = lambda a: True
        newVal = int(3e8)
        addr = acct.externalAddresses[2]
        a = addrlib.decodeAddress(addr, nets.mainnet)
        script = txscript.payToAddrScript(a)
        newTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(newVal, script)
        newTx.addTxOut(op)
        utxo = account.UTXO(addr, ByteArray(b"txid"), 0, satoshis=newVal)

        def t4a(*a):
            acct.blockchain.txidsForAddr = lambda *a: []
            return [newTx.id()]

        acct.blockchain.txidsForAddr = t4a

        def utxos4a(*a):
            acct.blockchain.UTXOs = lambda *a: []
            return [utxo]

        acct.blockchain.UTXOs = utxos4a
        acct.blockchain.subscribeAddresses = lambda addrs, receiver: None
        acct.blockchain.subscribeBlocks = lambda a: None
        acct.sync()
        assert Signals.b.available == newVal

        # spend the utxo by sending it. Reusing newTx for convenience.
        changeVal = int(5e7)
        addr = acct.internalAddresses[0]
        change = account.UTXO(addr,
                              ByteArray(b"newtxid"),
                              0,
                              satoshis=changeVal)
        acct.blockchain.sendToAddress = lambda *a: (newTx, [utxo], [change])
        acct.sendToAddress(1, "recipient")
        assert Signals.b.available == changeVal

        # purchase some tickets. Reusing newTx for convenience again.
        ticket = account.UTXO.parse(dcrdataUTXO)
        ticket.setTicketInfo(dcrdataTinfo)
        ticket.scriptPubKey = ticketScript
        ticket.parseScriptClass()
        acct.blockchain.purchaseTickets = lambda *a: ([newTx, []], [],
                                                      [ticket])
        acct.signals.spentTickets = lambda: True
        acct.purchaseTickets(1, 1)
        assert Signals.b.total == changeVal + ticket.satoshis

        # revoke the ticket
        ticketTx = msgtx.MsgTx.new()
        op = msgtx.TxOut(ticket.satoshis, ticket.scriptPubKey)
        ticketTx.addTxOut(op)
        ticket.tinfo.status = "missed"
        redeemHash = addrlib.AddressScriptHash(
            txscript.extractStakeScriptHash(ticketScript, opcode.OP_SSTX),
            nets.mainnet,
        )
        acct.stakePool().purchaseInfo.ticketAddress = redeemHash.string()
        revoked = False

        def rev(*a):
            nonlocal revoked
            revoked = True

        acct.blockchain.tx = lambda *a: ticketTx
        acct.blockchain.revokeTicket = rev
        acct.revokeTickets()
        assert revoked