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
def utxoWithTxid(txid): return account.UTXO( address="ticketaddress", txHash=reversed(ByteArray(txid)), vout=0, scriptPubKey=stakeSubmission, satoshis=2, tinfo=newTinfo(), )
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
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)
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)
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