-
Notifications
You must be signed in to change notification settings - Fork 278
Integration test for attestations #413
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
fbb77aa
653a26c
9598b32
3468817
3a9d323
29e0409
184668b
9050a40
af07dcf
0766b4d
82ab435
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,41 @@ | ||
--- | ||
apiVersion: v1 | ||
kind: Service | ||
metadata: | ||
name: check-attestations | ||
labels: | ||
app: check-attestations | ||
spec: | ||
clusterIP: None | ||
selector: | ||
app: check-attestations | ||
--- | ||
apiVersion: apps/v1 | ||
kind: StatefulSet | ||
metadata: | ||
name: check-attestations | ||
spec: | ||
selector: | ||
matchLabels: | ||
app: check-attestations | ||
serviceName: check-attestations | ||
replicas: 1 | ||
template: | ||
metadata: | ||
labels: | ||
app: check-attestations | ||
spec: | ||
restartPolicy: Always | ||
terminationGracePeriodSeconds: 0 | ||
containers: | ||
- name: check-attestations | ||
image: check-attestations | ||
command: | ||
- python3 | ||
- /usr/src/pyth/check_attestations.py | ||
tty: true | ||
readinessProbe: | ||
tcpSocket: | ||
port: 2000 | ||
periodSeconds: 1 | ||
failureThreshold: 300 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#syntax=docker/dockerfile:1.2@sha256:e2a8561e419ab1ba6b2fe6cbdf49fd92b95912df1cf7d313c3e2230a333fdbcc | ||
FROM python:3.9-alpine | ||
|
||
RUN pip install base58 | ||
|
||
ADD third_party/pyth/pyth_utils.py /usr/src/pyth/pyth_utils.py | ||
ADD third_party/pyth/check_attestations.py /usr/src/pyth/check_attestations.py | ||
|
||
RUN chmod a+rx /usr/src/pyth/*.py | ||
|
||
ENV READINESS_PORT=2000 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# This script is a CI test in tilt that verifies that prices are flowing through the entire system properly. | ||
# It checks that all prices being published by the pyth publisher are showing up at the price service. | ||
import base58 | ||
import logging | ||
import time | ||
from pyth_utils import * | ||
|
||
logging.basicConfig( | ||
level=logging.DEBUG, format="%(asctime)s | %(module)s | %(levelname)s | %(message)s" | ||
) | ||
|
||
# Where to read the set of accounts from | ||
PYTH_TEST_ACCOUNTS_HOST = "pyth" | ||
PYTH_TEST_ACCOUNTS_PORT = 4242 | ||
|
||
PRICE_SERVICE_HOST = "pyth-price-service" | ||
PRICE_SERVICE_PORT = 4200 | ||
|
||
def base58_to_hex(base58_string): | ||
asc_string = base58.b58decode(base58_string) | ||
return asc_string.hex() | ||
|
||
all_prices_attested = False | ||
while not all_prices_attested: | ||
publisher_state_map = get_pyth_accounts(PYTH_TEST_ACCOUNTS_HOST, PYTH_TEST_ACCOUNTS_PORT) | ||
pyth_price_account_ids = sorted([base58_to_hex(x["price"]) for x in publisher_state_map["symbols"]]) | ||
price_ids = sorted(get_json(PRICE_SERVICE_HOST, PRICE_SERVICE_PORT, "/api/price_feed_ids")) | ||
|
||
if price_ids == pyth_price_account_ids: | ||
if publisher_state_map["all_symbols_added"]: | ||
logging.info("Price ids match and all symbols added. Enabling readiness probe") | ||
all_prices_attested = True | ||
else: | ||
logging.info("Price ids match but still waiting for more symbols to come online.") | ||
else: | ||
logging.info("Price ids do not match") | ||
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 think making 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. you can't fail here because there's a race condition between the attester and the HTTP endpoint which can cause this case to trigger (but the next loop iteration succeeds). Looping is more reliable. 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. that makes sense |
||
logging.info(f"published ids: {pyth_price_account_ids}") | ||
logging.info(f"attested ids: {price_ids}") | ||
|
||
time.sleep(10) | ||
|
||
# Let k8s know the service is up | ||
readiness() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -109,20 +109,7 @@ | |
# Retrieve available symbols from the test pyth publisher if not provided in envs | ||
if P2W_ATTESTATION_CFG is None: | ||
P2W_ATTESTATION_CFG = "./attestation_cfg_test.yaml" | ||
conn = HTTPConnection(PYTH_TEST_ACCOUNTS_HOST, PYTH_TEST_ACCOUNTS_PORT) | ||
|
||
conn.request("GET", "/") | ||
|
||
res = conn.getresponse() | ||
|
||
publisher_state_map = {} | ||
|
||
if res.getheader("Content-Type") == "application/json": | ||
publisher_state_map = json.load(res) | ||
else: | ||
logging.error("Bad Content type") | ||
sys.exit(1) | ||
|
||
publisher_state_map = get_pyth_accounts(PYTH_TEST_ACCOUNTS_HOST, PYTH_TEST_ACCOUNTS_PORT) | ||
pyth_accounts = publisher_state_map["symbols"] | ||
|
||
logging.info( | ||
|
@@ -167,7 +154,7 @@ | |
cfg_yaml += f""" | ||
- group_name: longer_interval_sensitive_changes | ||
conditions: | ||
min_interval_secs: 10 | ||
min_interval_secs: 3 | ||
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. attest faster so we don't have to wait as long for the checking to work |
||
price_changed_bps: 300 | ||
symbols: | ||
""" | ||
|
@@ -186,7 +173,7 @@ | |
cfg_yaml += f""" | ||
- group_name: mapping | ||
conditions: | ||
min_interval_secs: 30 | ||
min_interval_secs: 10 | ||
price_changed_bps: 500 | ||
symbols: [] | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,9 @@ def do_GET(self): | |
self.wfile.flush() | ||
|
||
# Test publisher state that gets served via the HTTP endpoint. Note: the schema of this dict is extended here and there | ||
HTTP_ENDPOINT_DATA = {"symbols": [], "mapping_address": None} | ||
# all_symbols_added is set to True once all dynamically-created symbols are added to the on-chain program. This | ||
# flag allows the integration test in check_attestations.py to determine that every on-chain symbol is being attested. | ||
HTTP_ENDPOINT_DATA = {"symbols": [], "mapping_address": None, "all_symbols_added": False} | ||
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. If I understand correctly, I think that's reasonable, but can you note here what this is for? |
||
|
||
|
||
def publisher_random_update(price_pubkey): | ||
|
@@ -154,8 +156,8 @@ def add_symbol(num: int): | |
|
||
# Add a symbol if new symbol interval configured. This will add a new symbol if PYTH_NEW_SYMBOL_INTERVAL_SECS | ||
# is passed since adding the previous symbol. The second constraint ensures that | ||
# at most PYTH_TEST_SYMBOL_COUNT new price symbols are created. | ||
if PYTH_NEW_SYMBOL_INTERVAL_SECS > 0 and dynamically_added_symbols < PYTH_TEST_SYMBOL_COUNT: | ||
# at most PYTH_DYNAMIC_SYMBOL_COUNT new price symbols are created. | ||
if PYTH_NEW_SYMBOL_INTERVAL_SECS > 0 and dynamically_added_symbols < PYTH_DYNAMIC_SYMBOL_COUNT: | ||
# Do it if enough time passed | ||
now = time.monotonic() | ||
if (now - last_new_sym_added_at) >= PYTH_NEW_SYMBOL_INTERVAL_SECS: | ||
|
@@ -164,6 +166,9 @@ def add_symbol(num: int): | |
next_new_symbol_id += 1 | ||
dynamically_added_symbols += 1 | ||
|
||
if dynamically_added_symbols >= PYTH_DYNAMIC_SYMBOL_COUNT: | ||
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 generally like the permissive comparison, but I think the |
||
HTTP_ENDPOINT_DATA["all_symbols_added"] = True | ||
|
||
time.sleep(PYTH_PUBLISHER_INTERVAL_SECS) | ||
sys.stdout.flush() | ||
|
||
|
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 think this should live in
pyth_utils.py
and be used by both this andpyth_publisher.py
, but it's probably fine