-
Notifications
You must be signed in to change notification settings - Fork 14
tests: more unit tests for DcrdataBlockchain #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
37008ca
8e543d3
ce7b2b6
28c8fc6
7f299c2
4c0fcc3
45b7b53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -519,7 +519,7 @@ def __init__(self, db, params, datapath, skipConnect=False): | |||||||
| self.heightMap = db.child("height", datatypes=("INTEGER", "BLOB")) | ||||||||
| self.headerDB = db.child("header", blobber=msgblock.BlockHeader) | ||||||||
| self.txBlockMap = db.child("blocklink") | ||||||||
| self.tip = None | ||||||||
| self.tipHeight = None | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only the height is ever used, so it makes sense to store just that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Used here tinydecred/decred/decred/dcr/account.py Lines 819 to 821 in 704e58c
So could change there I guess. That's the only place I could find, but may be more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks. Checked the codebase, no other uses. |
||||||||
| self.subsidyCache = calc.SubsidyCache(params) | ||||||||
| if not skipConnect: | ||||||||
| self.connect() | ||||||||
|
|
@@ -865,7 +865,7 @@ def updateTip(self): | |||||||
| this can be used sparingly. | ||||||||
| """ | ||||||||
| try: | ||||||||
| self.tip = self.bestBlock() | ||||||||
| self.tipHeight = self.bestBlock()["height"] | ||||||||
| except Exception as e: | ||||||||
| log.error("failed to retrieve tip from blockchain: %s" % formatTraceback(e)) | ||||||||
| raise DecredError("no tip data retrieved") | ||||||||
|
|
@@ -928,23 +928,22 @@ def broadcast(self, txHex): | |||||||
|
|
||||||||
| def pubsubSignal(self, sig): | ||||||||
| """ | ||||||||
| Process a notifictation from the block explorer. | ||||||||
| Process a notification from the block explorer. | ||||||||
|
|
||||||||
| Arg: | ||||||||
| sig (obj or string): The block explorer's notification, decoded. | ||||||||
| """ | ||||||||
| # log.debug("pubsub signal recieved: %s" % repr(sig)) | ||||||||
| # log.debug("pubsub signal received: %s" % repr(sig)) | ||||||||
| if "done" in sig: | ||||||||
| return | ||||||||
| sigType = sig["event"] | ||||||||
| try: | ||||||||
| if sigType == "address": | ||||||||
| msg = sig["message"] | ||||||||
| log.debug("signal received for %s" % msg["address"]) | ||||||||
| self.addressReceiver(msg["address"], msg["transaction"]) | ||||||||
| self.addressReceiver(sig) | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The receiver signature requires one obj argument. |
||||||||
| elif sigType == "newblock": | ||||||||
| self.tip = sig["message"]["block"] | ||||||||
| self.tipHeight = self.tip["height"] | ||||||||
| self.tipHeight = sig["message"]["block"]["height"] | ||||||||
| self.blockReceiver(sig) | ||||||||
| elif sigType == "subscribeResp": | ||||||||
| # should check for error. | ||||||||
|
|
@@ -966,7 +965,7 @@ def changeScript(self, changeAddress): | |||||||
|
|
||||||||
| def approveUTXO(self, utxo): | ||||||||
| # If the UTXO appears unconfirmed, see if it can be confirmed. | ||||||||
| if utxo.maturity and self.tip["height"] < utxo.maturity: | ||||||||
| if utxo.maturity and self.tipHeight < utxo.maturity: | ||||||||
| return False | ||||||||
| if utxo.isTicket(): | ||||||||
| # Temporary until revocations implemented. | ||||||||
|
|
@@ -1172,8 +1171,7 @@ def purchaseTickets(self, keysource, utxosource, req): | |||||||
| raise DecredError("negative expiry") | ||||||||
|
|
||||||||
| # Perform a sanity check on expiry. | ||||||||
| tipHeight = self.tip["height"] | ||||||||
| if req.expiry <= tipHeight + 1 and req.expiry > 0: | ||||||||
| if req.expiry <= self.tipHeight + 1 and req.expiry > 0: | ||||||||
| raise DecredError("expiry height must be above next block height") | ||||||||
|
|
||||||||
| # Fetch a new address for creating a split transaction. Then, | ||||||||
|
|
@@ -1266,7 +1264,7 @@ def purchaseTickets(self, keysource, utxosource, req): | |||||||
| poolFeeAmt = txscript.stakePoolTicketFee( | ||||||||
| ticketPrice, | ||||||||
| ticketFee, | ||||||||
| tipHeight, | ||||||||
| self.tipHeight, | ||||||||
| req.poolFees, | ||||||||
| self.subsidyCache, | ||||||||
| self.params, | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ | |
|
|
||
| from decred import DecredError | ||
| from decred.crypto import opcode | ||
| from decred.dcr import agenda, txscript | ||
| from decred.dcr import account, agenda, txscript | ||
| from decred.dcr.dcrdata import ( | ||
| DcrdataBlockchain, | ||
| DcrdataClient, | ||
|
|
@@ -184,42 +184,6 @@ def test_static(self): | |
|
|
||
|
|
||
| class TestDcrdataBlockchain: | ||
| def test_subscriptions(self, http_get_post, tmp_path): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving this below the test data. |
||
|
|
||
| # Exception in updateTip. | ||
| preload_api_list(http_get_post) | ||
| with pytest.raises(DecredError): | ||
| DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
|
|
||
| # Successful creation. | ||
| preload_api_list(http_get_post) | ||
| http_get_post(f"{BASE_URL}api/block/best", 1) | ||
| ddb = DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
| assert ddb.tip == 1 | ||
|
|
||
| # Set the mock WebsocketClient. | ||
| ddb.dcrdata.ps = MockWebsocketClient() | ||
|
|
||
| # Subscribes. | ||
| def receiver(obj): | ||
| print("msg: %s" % repr(obj)) | ||
|
|
||
| ddb.subscribeBlocks(receiver) | ||
| assert ddb.dcrdata.ps.sent[0]["message"]["message"] == "newblock" | ||
|
|
||
| # Exception in subscribeAddresses. | ||
| with pytest.raises(DecredError): | ||
| ddb.subscribeAddresses([]) | ||
|
|
||
| ddb.dcrdata.ps.sent = [] | ||
| ddb.subscribeAddresses(["new_one"], receiver) | ||
| assert ddb.dcrdata.ps.sent[0]["message"]["message"] == "address:new_one" | ||
|
|
||
| # getAgendasInfo. | ||
| http_get_post(f"{BASE_URL}api/stake/vote/info", AGENDAS_INFO_RAW) | ||
| agsinfo = ddb.getAgendasInfo() | ||
| assert isinstance(agsinfo, agenda.AgendasInfo) | ||
|
|
||
| # Test data from block #427282. | ||
| utxos = ( | ||
| # Coinbase. | ||
|
|
@@ -302,9 +266,70 @@ def receiver(obj): | |
| ) | ||
| # fmt: on | ||
|
|
||
| def test_misc(self, http_get_post, tmp_path): | ||
| preload_api_list(http_get_post) | ||
| http_get_post(f"{BASE_URL}api/block/best", dict(height=1)) | ||
| ddb = DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
| assert ddb.tipHeight == 1 | ||
|
|
||
| # getAgendasInfo | ||
| http_get_post(f"{BASE_URL}api/stake/vote/info", AGENDAS_INFO_RAW) | ||
| agsinfo = ddb.getAgendasInfo() | ||
| assert isinstance(agsinfo, agenda.AgendasInfo) | ||
|
|
||
| def test_subscriptions(self, http_get_post, tmp_path): | ||
| # Exception in updateTip. | ||
| preload_api_list(http_get_post) | ||
| with pytest.raises(DecredError): | ||
| DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
|
|
||
| # Successful creation. | ||
| preload_api_list(http_get_post) | ||
| http_get_post(f"{BASE_URL}api/block/best", dict(height=1)) | ||
| ddb = DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
|
|
||
| # Set the mock WebsocketClient. | ||
| ddb.dcrdata.ps = MockWebsocketClient() | ||
|
|
||
| # Receiver | ||
| receive_queue = [] | ||
|
|
||
| def receiver(obj): | ||
| receive_queue.append(obj) | ||
|
|
||
| # subscribeBlocks | ||
| ddb.subscribeBlocks(receiver) | ||
| assert ddb.dcrdata.ps.sent[0]["message"]["message"] == "newblock" | ||
| ddb.dcrdata.ps.sent = [] | ||
|
|
||
| # subscribeAddresses | ||
| with pytest.raises(DecredError): | ||
| ddb.subscribeAddresses([]) | ||
| ddb.subscribeAddresses(["new_one"], receiver) | ||
| assert ddb.dcrdata.ps.sent[0]["message"]["message"] == "address:new_one" | ||
|
|
||
| # pubsubSignal | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New tests begin here. |
||
| assert ddb.pubsubSignal("done") is None | ||
| assert ddb.pubsubSignal(dict(event="subscribeResp")) is None | ||
| assert ddb.pubsubSignal(dict(event="ping")) is None | ||
| assert ddb.pubsubSignal(dict(event="unknown")) is None | ||
| # pubsubSignal address | ||
| sig = dict( | ||
| event="address", | ||
| message=dict(address="the_address", transaction="transaction",), | ||
| ) | ||
| ddb.pubsubSignal(sig) | ||
| assert receive_queue[0] == sig | ||
| receive_queue.clear() | ||
| # pubsubSignal newblock | ||
| sig = dict(event="newblock", message=dict(block=dict(height=1))) | ||
| ddb.pubsubSignal(sig) | ||
| assert receive_queue[0] == sig | ||
| receive_queue.clear() | ||
|
|
||
| def test_utxos(self, http_get_post, tmp_path): | ||
| preload_api_list(http_get_post) | ||
| http_get_post(f"{BASE_URL}api/block/best", 1) | ||
| http_get_post(f"{BASE_URL}api/block/best", dict(height=1)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was incorrect. The best block call returns a whole block, not directly the height. |
||
| ddb = DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
|
|
||
| # txVout error | ||
|
|
@@ -366,9 +391,41 @@ def test_utxos(self, http_get_post, tmp_path): | |
| # txVout success | ||
| assert ddb.txVout(self.txs[2][0], 0).satoshis == 14773017964 | ||
|
|
||
| # approveUTXO | ||
| utxo = account.UTXO.parse(self.utxos[1]) | ||
| utxo.maturity = 2 | ||
| assert ddb.approveUTXO(utxo) is False | ||
| utxo.maturity = None | ||
| assert ddb.approveUTXO(utxo) is False | ||
| utxo = account.UTXO.parse(self.utxos[0]) | ||
| assert ddb.approveUTXO(utxo) is True | ||
|
|
||
| # confirmUTXO | ||
| # No confirmation. | ||
| utxo = account.UTXO.parse(self.utxos[2]) | ||
| assert ddb.confirmUTXO(utxo) is False | ||
| # Confirmation. | ||
| blockHash = "00000000000000002b197e4018b990efb85e6bd43ffb15f7ede97a78f806a3f8" | ||
| txURL = f"{BASE_URL}api/tx/{self.txs[2][0]}" | ||
| decodedTx = {"block": {"blockhash": blockHash}} | ||
| http_get_post(txURL, decodedTx) | ||
| headerURL = f"{BASE_URL}api/block/hash/{blockHash}/header/raw" | ||
| blockHeader = { | ||
| "hex": ( | ||
| "07000000e00b3a83dc60f961d8f516ece63e6d009eff4c2af50139150000" | ||
| "000000000000873684038a5d384cf123ee39d39bdf9f65cf4051ec4d420f" | ||
| "e909c16344329aaa35879931c8695d9be6f9259fa7467c51d0c7e601d95c" | ||
| "c78fdd458210503865af0100721e0a6d2bf90500040091a40000e62f3418" | ||
| "c8518a700300000012850600213300003de0575e6e8b9d13e691326a1fd4" | ||
| "3a0000000000000000000000000000000000000000000000000007000000" | ||
| ), | ||
| } | ||
| http_get_post(headerURL, blockHeader) | ||
| assert ddb.confirmUTXO(utxo) is True | ||
|
|
||
| def test_blocks(self, http_get_post, tmp_path): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are pre-existing tests. |
||
| preload_api_list(http_get_post) | ||
| http_get_post(f"{BASE_URL}api/block/best", 1) | ||
| http_get_post(f"{BASE_URL}api/block/best", dict(height=1)) | ||
| ddb = DcrdataBlockchain(str(tmp_path / "test.db"), testnet, BASE_URL) | ||
|
|
||
| # blockHeader | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it's not so important at this stage, but this was intended to update the database. Once we have users, we can't expect them to delete their wallet and restore from the seed phrase every time we update something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made an issue #102
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I'm sorry, I missed that. Definitely an important feature that deserves its own consideration, and a clear and explicit implementation. 🙂 Thanks for making an issue about it.