-
Notifications
You must be signed in to change notification settings - Fork 32
use dns resolver to get account key #12
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
2841baa
4337d74
cbb2433
97400e7
fa59956
f1672b6
0845e71
63fd2f2
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 |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import ast | ||
import dns.resolver | ||
from loguru import logger | ||
from typing import Optional | ||
|
||
DEFAULT_VERSION = "v2" | ||
|
||
|
||
# Retrieving keys via DNS TXT records should not be considered secure and is provided as a convenience only. | ||
# Accounts should be stored locally and verified before being used for production. | ||
def get_key(network: str, type: str, version: str = DEFAULT_VERSION) -> Optional[str]: | ||
""" | ||
Get the program or mapping keys from dns TXT records. | ||
|
||
Example dns records: | ||
|
||
devnet-program-v2.pyth.network | ||
mainnet-program-v2.pyth.network | ||
testnet-mapping-v2.pyth.network | ||
""" | ||
url = f"{network}-{type}-{version}.pyth.network" | ||
try: | ||
answer = dns.resolver.resolve(url, "TXT") | ||
except dns.resolver.NXDOMAIN: | ||
logger.error("TXT record for {} not found", url) | ||
return "" | ||
if len(answer) != 1: | ||
logger.error("Invalid number of records returned for {}!", url) | ||
return "" | ||
# Example of the raw_key: | ||
# "program=FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH" | ||
raw_key = ast.literal_eval(list(answer)[0].to_text()) | ||
# program=FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH" | ||
_, key = raw_key.split("=", 1) | ||
return key | ||
Comment on lines
+12
to
+35
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. I wrote this code for pyth-observer as a quick and dirty hack and don't consider it very pretty python. Would you mind adding some tests for it? I'd be willing to help you do so if necessary. 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. will write tests for it 🙂 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
_ACCOUNT_HEADER_BYTES, _VERSION_2, PythMappingAccount, PythPriceType, PythProductAccount, PythPriceAccount | ||
) | ||
|
||
from pythclient.pythclient import PythClient, V2_FIRST_MAPPING_ACCOUNT_KEY, V2_PROGRAM_KEY, WatchSession | ||
from pythclient.pythclient import PythClient, WatchSession | ||
from pythclient.solana import ( | ||
SolanaClient, | ||
SolanaCommitment, | ||
|
@@ -24,6 +24,9 @@ | |
# 2) these values are used in get_account_info_resp() and get_program_accounts_resp() | ||
# and so if they are passed in as fixtures, the functions will complain for the args | ||
# while mocking the respective functions | ||
V2_FIRST_MAPPING_ACCOUNT_KEY = 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2' | ||
V2_PROGRAM_KEY = 'gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s' | ||
Comment on lines
+27
to
+28
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. declared as constants for the same reason as the constants below -- doesn't seems like there's a need to mock this? |
||
|
||
BCH_PRODUCT_ACCOUNT_KEY = '89GseEmvNkzAMMEXcW9oTYzqRPXTsJ3BmNerXmgA1osV' | ||
BCH_PRICE_ACCOUNT_KEY = '4EQrNZYk5KR1RnjyzbaaRbHsv8VqZWzSUtvx58wLsZbj' | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from _pytest.logging import LogCaptureFixture | ||
import pytest | ||
|
||
from pytest_mock import MockerFixture | ||
|
||
from mock import Mock | ||
|
||
import dns.resolver | ||
import dns.rdatatype | ||
import dns.rdataclass | ||
import dns.message | ||
import dns.rrset | ||
import dns.flags | ||
|
||
from pythclient.utils import get_key | ||
|
||
|
||
@pytest.fixture() | ||
def answer_program() -> dns.resolver.Answer: | ||
qname = dns.name.Name(labels=(b'devnet-program-v2', b'pyth', b'network', b'')) | ||
rdtype = dns.rdatatype.TXT | ||
rdclass = dns.rdataclass.IN | ||
response = dns.message.QueryMessage(id=0) | ||
response.flags = dns.flags.QR | ||
rrset_qn = dns.rrset.from_text(qname, 100, rdclass, rdtype) | ||
rrset_ans = dns.rrset.from_text(qname, 100, rdclass, rdtype, '"program=gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"') | ||
response.question = [rrset_qn] | ||
response.answer = [rrset_ans] | ||
answer = dns.resolver.Answer( | ||
qname=qname, rdtype=rdtype, rdclass=rdclass, response=response) | ||
answer.rrset = rrset_ans | ||
return answer | ||
|
||
|
||
@pytest.fixture() | ||
def answer_mapping() -> dns.resolver.Answer: | ||
qname = dns.name.Name(labels=(b'devnet-mapping-v2', b'pyth', b'network', b'')) | ||
rdtype = dns.rdatatype.TXT | ||
rdclass = dns.rdataclass.IN | ||
response = dns.message.QueryMessage(id=0) | ||
response.flags = dns.flags.QR | ||
rrset_qn = dns.rrset.from_text(qname, 100, rdclass, rdtype) | ||
rrset_ans = dns.rrset.from_text(qname, 100, rdclass, rdtype, '"mapping=BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2"') | ||
response.question = [rrset_qn] | ||
response.answer = [rrset_ans] | ||
answer = dns.resolver.Answer( | ||
qname=qname, rdtype=rdtype, rdclass=rdclass, response=response) | ||
answer.rrset = rrset_ans | ||
return answer | ||
|
||
|
||
@pytest.fixture() | ||
def mock_dns_resolver_resolve(mocker: MockerFixture) -> Mock: | ||
mock = Mock() | ||
mocker.patch('dns.resolver.resolve', side_effect=mock) | ||
return mock | ||
|
||
|
||
def test_utils_get_program_key(mock_dns_resolver_resolve: Mock, answer_program: dns.resolver.Answer) -> None: | ||
mock_dns_resolver_resolve.return_value = answer_program | ||
program_key = get_key("devnet", "program") | ||
assert program_key == "gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s" | ||
|
||
|
||
def test_utils_get_mapping_key(mock_dns_resolver_resolve: Mock, answer_mapping: dns.resolver.Answer) -> None: | ||
mock_dns_resolver_resolve.return_value = answer_mapping | ||
mapping_key = get_key("devnet", "mapping") | ||
assert mapping_key == "BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2" | ||
|
||
|
||
def test_utils_get_mapping_key_not_found(mock_dns_resolver_resolve: Mock, | ||
answer_mapping: dns.resolver.Answer, | ||
caplog: LogCaptureFixture) -> None: | ||
mock_dns_resolver_resolve.side_effect = dns.resolver.NXDOMAIN | ||
exc_message = f'TXT record for {str(answer_mapping.response.canonical_name())[:-1]} not found' | ||
key = get_key("devnet", "mapping") | ||
assert exc_message in caplog.text | ||
assert key == "" | ||
|
||
|
||
def test_utils_get_mapping_key_invalid_number(mock_dns_resolver_resolve: Mock, | ||
answer_mapping: dns.resolver.Answer, | ||
caplog: LogCaptureFixture) -> None: | ||
answer_mapping.rrset = None | ||
mock_dns_resolver_resolve.return_value = answer_mapping | ||
exc_message = f'Invalid number of records returned for {str(answer_mapping.response.canonical_name())[:-1]}!' | ||
key = get_key("devnet", "mapping") | ||
assert exc_message in caplog.text | ||
assert key == "" |
Uh oh!
There was an error while loading. Please reload this page.