evm unit tests in tilt (#186)

* Enable and fix Rust unit tests in Tilt for p2w-sdk and p2w-attest

commit-id:cae6ee05

* p2w-sdk/rust/src/lib.rs: comment about hex payload generation

commit-id:f9f3a249

* p2w-sdk/rust/src/lib.rs: Comment about hex payload generation

commit-id:352e74f9

* Fix and enable EVM unit tests in Tilt

commit-id:11bcb841

* EVM p2w tests: ensure true negatives in "should cache price updates"

commit-id:3aaa0527

* pyth.js: typo

commit-id:f76c5465

* EVM: Remove unrelated setup scripts, build contracts in Dockerfile

commit-id:b09c1d83

* ethereum/dockerfile: Remove node_modules caching

commit-id:c66c79c1

* EVM: revert dockerfile

commit-id:2b9e0b7a

* trigger build

commit-id:c7689ec4

* EVM: change stale timestamp test to the exact edge case

commit-id:d2709f47
This commit is contained in:
Stanisław Drozd 2022-04-15 12:39:02 +01:00 committed by GitHub
parent 035021905a
commit ded0fb37bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 36895 additions and 252 deletions

View File

@ -54,7 +54,11 @@ spec:
- ganache-cli
- -e 10000
- --deterministic
- --time="1970-01-01T00:00:00+00:00"
# NOTE(2022-04-13): Some unit tests need block timestamp
# to be sufficiently far above UNIX epoch (Pyth EVM tests
# check feed staleness logic against 0 as lowest possible
# timestamp)
- --time="1970-01-02T00:00:00+00:00"
- --host=0.0.0.0
ports:
- containerPort: 8545
@ -69,7 +73,9 @@ spec:
command:
- /bin/sh
- -c
- "npm run migrate && npx truffle exec scripts/deploy_test_token.js && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_bsc_chain.js && nc -lkp 2000 0.0.0.0"
- "npm run migrate &&
npx truffle test test/pyth.js 2>&1 &&
nc -lkp 2000 0.0.0.0"
readinessProbe:
periodSeconds: 1
failureThreshold: 300

View File

@ -173,7 +173,7 @@ contract Pyth is PythGetters, PythSetters, AbstractPyth {
/// This includes attestation delay which currently might up to a minute.
uint private constant VALID_TIME_PERIOD_SECS = 180;
function queryPriceFeed(bytes32 id) public override returns (PythStructs.PriceFeed memory priceFeed){
function queryPriceFeed(bytes32 id) public view override returns (PythStructs.PriceFeed memory priceFeed){
// Look up the latest price info for the given ID
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);

36826
ethereum/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@
"license": "ISC",
"dependencies": {
"@openzeppelin/contracts-upgradeable": "^4.5.2",
"@pythnetwork/pyth-sdk-solidity": "0.0.2",
"@pythnetwork/pyth-sdk-solidity": "0.0.3",
"dotenv": "^10.0.0",
"elliptic": "^6.5.2",
"ganache-cli": "^6.12.1",

View File

@ -44,7 +44,7 @@ contract("Pyth", function () {
})
it("should allow upgrades from the owner", async function(){
// Check that the owner is the default account Truffle
// Check that the owner is the default account Truffle
// has configured for the network. upgradeProxy will send
// transactions from the default account.
const accounts = await web3.eth.getAccounts();
@ -62,7 +62,7 @@ contract("Pyth", function () {
})
it("should allow ownership transfer", async function(){
// Check that the owner is the default account Truffle
// Check that the owner is the default account Truffle
// has configured for the network.
const accounts = await web3.eth.getAccounts();
const defaultAccount = accounts[0];
@ -88,10 +88,10 @@ contract("Pyth", function () {
// we cannot specify which account upgradeProxy send transactions from:
// it will always use the default account.
//
// Therefore, we transfer the ownership to another account first,
// Therefore, we transfer the ownership to another account first,
// and then attempt an upgrade using the default account.
// Check that the owner is the default account Truffle
// Check that the owner is the default account Truffle
// has configured for the network.
const accounts = await web3.eth.getAccounts();
const defaultAccount = accounts[0];
@ -107,22 +107,47 @@ contract("Pyth", function () {
await expectRevert(upgradeProxy(this.pythProxy.address, MockPythUpgrade), notOwnerError);
})
const rawBatchPriceAttestation = "0x"+"503257480002020004009650325748000201c0e11df4c58a4e53f2bc059ba57a7c8f30ddada70b5bdc3753f90b824b64dd73c1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e01000000000000071dfffffffb00000000000005f70000000132959bbd00000000c8bfed5f00000000000000030000000041c7b65b00000000c8bfed5f0000000000000003010000000000TTTTTTTT503257480002017090c4ecf0309718d04c5a162c08aa4b78f533f688fa2f3ccd7be74c2a253a54fd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620010000000000000440fffffffb00000000000005fb000000015cfe8c9d00000000e3dbaa7f00000000000000020000000041c7c5bb00000000e3dbaa7f0000000000000007010000000000TTTTTTTT503257480002012f064374f55cb2efbbef29329de3b652013a76261876c55a1caf3a489c721ccd8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd010000000000000609fffffffb00000000000005cd00000001492c19bd00000000dd92071f00000000000000020000000041c7d3fb00000000dd92071f0000000000000001010000000000TTTTTTTT5032574800020171ddabd1a2c1fb6d6c4707b245b7c0ab6af0ae7b96b2ff866954a0b71124aee517fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb70100000000000007bcfffffffb00000000000005e2000000014db2995d00000000dd8f775f00000000000000020000000041c7df9b00000000dd8f775f0000000000000003010000000000TTTTTTTT";
// NOTE(2022-04-11): Raw hex payload obtained from format serialization unit tests in `p2w-sdk/rust`
// Latest known addition: num_publishers, max_num_publishers
//
// Tests rely on a p2w-sdk mock price/prod ID generation rule:
// nthProdByte(n) = n % 256, starting with n=1
// nthPriceByte(n) = 255 - (n % 256), starting with n=1
//
// Examples:
// 1st prod = "0x010101[...]"
// 1st price = "0xFEFEFE[...]"
// 2nd prod = "0x020202[...]"
// 2nd price = "0xFDFDFD[...]"
// 3rd prod = "0x030303[...]"
// 3rd price = "0xFCFCFC[...]"
const RAW_BATCH_TIMESTAMP_REGEX = /DEADBEEFFADEDEED/g;
const RAW_BATCH_PRICE_REGEX = /0000002BAD2FEED7/g;
const RAW_BATCH_ATTESTATION_COUNT = 10;
const rawBatchPriceAttestation = "0x" + "50325748000202000A009E503257480002010101010101010101010101010101010101010101010101010101010101010101FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010202020202020202020202020202020202020202020202020202020202020202FDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFD010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010303030303030303030303030303030303030303030303030303030303030303FCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFC010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010404040404040404040404040404040404040404040404040404040404040404FBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFB010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010505050505050505050505050505050505050505050505050505050505050505FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFA010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010606060606060606060606060606060606060606060606060606060606060606F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9F9010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010707070707070707070707070707070707070707070707070707070707070707F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010808080808080808080808080808080808080808080808080808080808080808F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010909090909090909090909090909090909090909090909090909090909090909F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6F6010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0503257480002010A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0AF5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5F5010000002BAD2FEED7FFFFFFFDFFFFFFFFFFFFFFD6000000000000000F0000000000000025000000000000002A000000000000045700000000000008AE00000000000000650100DEADBEEFFADEDEED0001E14C0004E6D0";
function encodeTimestamp(timestamp) {
return timestamp.toString(16).padStart(8, "0");
// Takes an unsigned 64-bit integer, converts it to hex with 0-padding
function u64ToHex(timestamp) {
// u64 -> 8 bytes -> 16 hex bytes
return timestamp.toString(16).padStart(16, "0");
}
function generateRawBatchAttestation(timestamp) {
return rawBatchPriceAttestation.replace(/TTTTTTTT/g, encodeTimestamp(timestamp));
function generateRawBatchAttestation(timestamp, priceVal) {
const ts = u64ToHex(timestamp);
const price = u64ToHex(priceVal);
const replaced = rawBatchPriceAttestation
.replace(RAW_BATCH_TIMESTAMP_REGEX, ts)
.replace(RAW_BATCH_PRICE_REGEX, price);
return replaced;
}
it("should parse batch price attestation correctly", async function() {
const magic = 1345476424;
const magic = 0x50325748;
const version = 2;
let timestamp = 1647273460;
let rawBatch = generateRawBatchAttestation(timestamp);
let priceVal = 1337;
let rawBatch = generateRawBatchAttestation(timestamp, priceVal);
let parsed = await this.pythProxy.parseBatchPriceAttestation(rawBatch);
// Check the header
@ -130,90 +155,40 @@ contract("Pyth", function () {
assert.equal(parsed.header.version, version);
assert.equal(parsed.header.payloadId, 2);
assert.equal(parsed.nAttestations, 4);
assert.equal(parsed.attestationSize, 150);
assert.equal(parsed.nAttestations, RAW_BATCH_ATTESTATION_COUNT);
assert.equal(parsed.attestationSize, 158);
assert.equal(parsed.attestations.length, 4);
assert.equal(parsed.attestations.length, parsed.nAttestations);
// Attestation #1
assert.equal(parsed.attestations[0].header.magic, magic);
assert.equal(parsed.attestations[0].header.version, version);
assert.equal(parsed.attestations[0].header.payloadId, 1);
assert.equal(parsed.attestations[0].productId, "0xc0e11df4c58a4e53f2bc059ba57a7c8f30ddada70b5bdc3753f90b824b64dd73");
assert.equal(parsed.attestations[0].priceId, "0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e");
assert.equal(parsed.attestations[0].priceType, 1);
assert.equal(parsed.attestations[0].price, 1821);
assert.equal(parsed.attestations[0].exponent, -5);
assert.equal(parsed.attestations[0].emaPrice.value, 1527);
assert.equal(parsed.attestations[0].emaPrice.numerator, 5143632829);
assert.equal(parsed.attestations[0].emaPrice.denominator, 3368021343);
assert.equal(parsed.attestations[0].emaConf.value, 3);
assert.equal(parsed.attestations[0].emaConf.numerator, 1103607387);
assert.equal(parsed.attestations[0].emaConf.denominator, 3368021343);
assert.equal(parsed.attestations[0].confidenceInterval, 3);
assert.equal(parsed.attestations[0].status, 1);
assert.equal(parsed.attestations[0].corpAct, 0);
assert.equal(parsed.attestations[0].timestamp, timestamp);
for (var i = 0; i < parsed.attestations.length; ++i) {
const prodId = "0x" + (i+1).toString(16).padStart(2, "0").repeat(32);
const priceByte = 255 - ((i+1) % 256);
const priceId = "0x" + priceByte.toString(16).padStart(2, "0").repeat(32);
// Attestation #2
assert.equal(parsed.attestations[1].header.magic, magic);
assert.equal(parsed.attestations[1].header.version, version);
assert.equal(parsed.attestations[1].header.payloadId, 1);
assert.equal(parsed.attestations[1].productId, "0x7090c4ecf0309718d04c5a162c08aa4b78f533f688fa2f3ccd7be74c2a253a54");
assert.equal(parsed.attestations[1].priceId, "0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620");
assert.equal(parsed.attestations[1].priceType, 1);
assert.equal(parsed.attestations[1].price, 1088);
assert.equal(parsed.attestations[1].exponent, -5);
assert.equal(parsed.attestations[1].emaPrice.value, 1531);
assert.equal(parsed.attestations[1].emaPrice.numerator, 5855153309);
assert.equal(parsed.attestations[1].emaPrice.denominator, 3822824063);
assert.equal(parsed.attestations[1].emaConf.value, 2);
assert.equal(parsed.attestations[1].emaConf.numerator, 1103611323);
assert.equal(parsed.attestations[1].emaConf.denominator, 3822824063);
assert.equal(parsed.attestations[1].confidenceInterval, 7);
assert.equal(parsed.attestations[1].status, 1);
assert.equal(parsed.attestations[1].corpAct, 0);
assert.equal(parsed.attestations[1].timestamp, timestamp);
// Attestation #3
assert.equal(parsed.attestations[2].header.magic, magic);
assert.equal(parsed.attestations[2].header.version, version);
assert.equal(parsed.attestations[2].header.payloadId, 1);
assert.equal(parsed.attestations[2].productId, "0x2f064374f55cb2efbbef29329de3b652013a76261876c55a1caf3a489c721ccd");
assert.equal(parsed.attestations[2].priceId, "0x8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd");
assert.equal(parsed.attestations[2].priceType, 1);
assert.equal(parsed.attestations[2].price, 1545);
assert.equal(parsed.attestations[2].exponent, -5);
assert.equal(parsed.attestations[2].emaPrice.value, 1485);
assert.equal(parsed.attestations[2].emaPrice.numerator, 5522594237);
assert.equal(parsed.attestations[2].emaPrice.denominator, 3717334815);
assert.equal(parsed.attestations[2].emaConf.value, 2);
assert.equal(parsed.attestations[2].emaConf.numerator, 1103614971);
assert.equal(parsed.attestations[2].emaConf.denominator, 3717334815);
assert.equal(parsed.attestations[2].confidenceInterval, 1);
assert.equal(parsed.attestations[2].status, 1);
assert.equal(parsed.attestations[2].corpAct, 0);
assert.equal(parsed.attestations[2].timestamp, timestamp);
assert.equal(parsed.attestations[i].header.magic, magic);
assert.equal(parsed.attestations[i].header.version, version);
assert.equal(parsed.attestations[i].header.payloadId, 1);
assert.equal(parsed.attestations[i].productId, prodId);
assert.equal(parsed.attestations[i].priceId, priceId);
assert.equal(parsed.attestations[i].priceType, 1);
assert.equal(parsed.attestations[i].price, priceVal);
assert.equal(parsed.attestations[i].exponent, -3);
assert.equal(parsed.attestations[i].emaPrice.value, -42);
assert.equal(parsed.attestations[i].emaPrice.numerator, 15);
assert.equal(parsed.attestations[i].emaPrice.denominator, 37);
assert.equal(parsed.attestations[i].emaConf.value, 42);
assert.equal(parsed.attestations[i].emaConf.numerator, 1111);
assert.equal(parsed.attestations[i].emaConf.denominator, 2222);
assert.equal(parsed.attestations[i].confidenceInterval, 101);
assert.equal(parsed.attestations[i].status, 1);
assert.equal(parsed.attestations[i].corpAct, 0);
assert.equal(parsed.attestations[i].timestamp, timestamp);
assert.equal(parsed.attestations[i].num_publishers, 123212);
assert.equal(parsed.attestations[i].max_num_publishers, 321232);
// Attestation #4
assert.equal(parsed.attestations[3].header.magic, magic);
assert.equal(parsed.attestations[3].header.version, version);
assert.equal(parsed.attestations[3].header.payloadId, 1);
assert.equal(parsed.attestations[3].productId, "0x71ddabd1a2c1fb6d6c4707b245b7c0ab6af0ae7b96b2ff866954a0b71124aee5");
assert.equal(parsed.attestations[3].priceId, "0x17fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb7");
assert.equal(parsed.attestations[3].priceType, 1);
assert.equal(parsed.attestations[3].price, 1980);
assert.equal(parsed.attestations[3].exponent, -5);
assert.equal(parsed.attestations[3].emaPrice.value, 1506);
assert.equal(parsed.attestations[3].emaPrice.numerator, 5598517597);
assert.equal(parsed.attestations[3].emaPrice.denominator, 3717166943);
assert.equal(parsed.attestations[3].emaConf.value, 2);
assert.equal(parsed.attestations[3].emaConf.numerator, 1103617947);
assert.equal(parsed.attestations[3].emaConf.denominator, 3717166943);
assert.equal(parsed.attestations[3].confidenceInterval, 3);
assert.equal(parsed.attestations[3].status, 1);
assert.equal(parsed.attestations[3].corpAct, 0);
assert.equal(parsed.attestations[3].timestamp, timestamp);
console.debug(`attestation ${i + 1}/${parsed.attestations.length} parsed OK`);
}
})
async function attest(contract, data) {
@ -230,64 +205,71 @@ contract("Pyth", function () {
0,
0
);
await contract.updatePriceBatchFromVm("0x"+vm);
}
it("should attest price updates over wormhole", async function() {
let rawBatch = generateRawBatchAttestation(1647273460);
let rawBatch = generateRawBatchAttestation(1647273460, 1337);
await attest(this.pythProxy, rawBatch);
})
it("should cache price updates", async function() {
let currentTimestamp = (await web3.eth.getBlock("latest")).timestamp;
let rawBatch = generateRawBatchAttestation(currentTimestamp);
let priceVal = 521;
let rawBatch = generateRawBatchAttestation(currentTimestamp, priceVal);
await attest(this.pythProxy, rawBatch);
let first = await this.pythProxy.queryPriceFeed("0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e");
assert.equal(first.id, "0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e");
assert.equal(first.productId, "0xc0e11df4c58a4e53f2bc059ba57a7c8f30ddada70b5bdc3753f90b824b64dd73");
assert.equal(first.price, 1821);
assert.equal(first.conf, 3);
assert.equal(first.expo, -5);
assert.equal(first.status.toString(), PythStructs.PriceStatus.TRADING.toString());
assert.equal(first.numPublishers, 0);
assert.equal(first.maxNumPublishers, 0);
assert.equal(first.emaPrice, 1527);
assert.equal(first.emaConf, 3);
let first_prod_id = "0x" + "01".repeat(32)
let first_price_id = "0x" + "fe".repeat(32)
let second_prod_id = "0x" + "02".repeat(32)
let second_price_id = "0x" + "fd".repeat(32)
let second = await this.pythProxy.queryPriceFeed("0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620");
assert.equal(second.id, "0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620");
assert.equal(second.productId, "0x7090c4ecf0309718d04c5a162c08aa4b78f533f688fa2f3ccd7be74c2a253a54");
assert.equal(second.price, 1088);
assert.equal(second.conf, 7);
assert.equal(second.expo, -5);
assert.equal(second.status.toString(), PythStructs.PriceStatus.TRADING.toString());
assert.equal(second.numPublishers, 0);
assert.equal(second.maxNumPublishers, 0);
assert.equal(second.emaPrice, 1531);
assert.equal(second.emaConf, 2);
// Confirm that previously non-existent feeds are created
let first = await this.pythProxy.queryPriceFeed(first_price_id);
console.debug(`first is ${JSON.stringify(first)}`);
assert.equal(first.price, priceVal);
let second = await this.pythProxy.queryPriceFeed(second_price_id);
assert.equal(second.price, priceVal);
// Confirm the price is bumped after a new attestation updates each record
let nextTimestamp = currentTimestamp + 1;
let rawBatch2 = generateRawBatchAttestation(nextTimestamp, priceVal + 5);
await attest(this.pythProxy, rawBatch2);
first = await this.pythProxy.queryPriceFeed(first_price_id);
assert.equal(first.price, priceVal + 5);
second = await this.pythProxy.queryPriceFeed(second_price_id);
assert.equal(second.price, priceVal + 5);
// Confirm that only strictly larger timestamps trigger updates
let rawBatch3 = generateRawBatchAttestation(nextTimestamp, priceVal + 10);
await attest(this.pythProxy, rawBatch3);
first = await this.pythProxy.queryPriceFeed(first_price_id);
assert.equal(first.price, priceVal + 5);
assert.notEqual(first.price, priceVal + 10);
second = await this.pythProxy.queryPriceFeed(second_price_id);
assert.equal(second.price, priceVal + 5);
assert.notEqual(second.price, priceVal + 10);
})
it("should fail transaction if a price is not found", async function() {
await expectRevert(
this.pythProxy.queryPriceFeed(
"0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e"),
"0xdeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeed"),
"no price feed found for the given price id");
})
it("should show stale cached prices as unknown", async function() {
let smallestTimestamp = 1;
let rawBatch = generateRawBatchAttestation(smallestTimestamp);
let rawBatch = generateRawBatchAttestation(smallestTimestamp, 1337);
await attest(this.pythProxy, rawBatch);
let all_price_ids = ["0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e",
"0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620",
"0x8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd",
"0x17fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb7"
];
for (var i = 0; i < all_price_ids.length; i++) {
const price_id = all_price_ids[i];
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
const price_id = "0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
let priceFeedResult = await this.pythProxy.queryPriceFeed(price_id);
assert.equal(priceFeedResult.status.toString(), PythStructs.PriceStatus.UNKNOWN.toString());
}
@ -295,87 +277,16 @@ contract("Pyth", function () {
it("should show cached prices too far into the future as unknown", async function() {
let largestTimestamp = 4294967295;
let rawBatch = generateRawBatchAttestation(largestTimestamp);
let rawBatch = generateRawBatchAttestation(largestTimestamp, 1337);
await attest(this.pythProxy, rawBatch);
let all_price_ids = ["0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e",
"0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620",
"0x8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd",
"0x17fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb7"
];
for (var i = 0; i < all_price_ids.length; i++) {
const price_id = all_price_ids[i];
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
const price_id = "0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
let priceFeedResult = await this.pythProxy.queryPriceFeed(price_id);
assert.equal(priceFeedResult.status.toString(), PythStructs.PriceStatus.UNKNOWN.toString());
}
})
it("should only cache updates for new prices", async function() {
// This test sends two batches of updates, for the same Price IDs. The second batch contains
// different price values to the first batch, but only the first and last updates in
// the second batch have a newer timestamp than those in the first batch, and so these
// are the only two which should be cached.
let currentTimestamp = (await web3.eth.getBlock("latest")).timestamp;
let encodedCurrentTimestamp = encodeTimestamp(currentTimestamp);
let encodedNewerTimestamp = encodeTimestamp(currentTimestamp + 1);
const firstBatch = generateRawBatchAttestation(currentTimestamp);
let secondBatch = "0x"+"503257480002020004009650325748000201c0e11df4c58a4e53f2bc059ba57a7c8f30ddada70b5bdc3753f90b824b64dd73c1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e01000000000000073dfffffffb00000000000005470000000132959bbd00000000c8bfed5f00000000000000030000000041c7b65b00000000c8bfed5f0000000000000003010000000000"+encodedNewerTimestamp+"503257480002017090c4ecf0309718d04c5a162c08aa4b78f533f688fa2f3ccd7be74c2a253a54fd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620010000000000000450fffffffb00000000000005fb000000015cfe8c9d00000000e3dbaa7f00000000000000020000000041c7c5bb00000000e3dbaa7f0000000000000007010000000000"+encodedCurrentTimestamp+"503257480002012f064374f55cb2efbbef29329de3b652013a76261876c55a1caf3a489c721ccd8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd010000000000000659fffffffb00000000000005cd00000001492c19bd00000000dd92071f00000000000000020000000041c7d3fb00000000dd92071f0000000000000001010000000000"+encodedCurrentTimestamp+"5032574800020181ddabd1a2c1fb6d6c4707b245b7c0ab6af0ae7b96b2ff866954a0b71124aee517fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb70100000000000007bDfffffffb00000000000005e2000000014db2995d00000000dd8f775f00000000000000020000000041c7df9b00000000dd8f775f0000000000000003010000000000"+encodedNewerTimestamp;
let all_price_ids = ["0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e",
"0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620",
"0x8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd",
"0x17fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb7"
];
// Send the first batch
await attest(this.pythProxy, firstBatch);
let prices_after_first_update = {};
for (var i = 0; i < all_price_ids.length; i++) {
const price_id = all_price_ids[i];
prices_after_first_update[price_id] = await this.pythProxy.queryPriceFeed(price_id);
}
// Send the second batch
await attest(this.pythProxy, secondBatch);
let prices_after_second_update = {};
for (var i = 0; i < all_price_ids.length; i++) {
const price_id = all_price_ids[i];
prices_after_second_update[price_id] = await this.pythProxy.queryPriceFeed(price_id);
}
// Price IDs which have newer timestamps
let new_price_updates = [
"0xc1902e05cdf03bc089a943d921f87ccd0e3e1b774b5660d037b9f428c0d3305e",
"0x17fbe895e5416ddb4d5af9d83c599ee2c4f94cb25e8597f9e5978bd63a7cdcb7"
];
// Price IDs which have older timestamps
let old_price_updates = [
"0xfd4caca566fc44a9d6585420959d13897877c606477b3f0e7f247295b7275620",
"0x8c5dd422900917e8e26316fe598e8f062058d390644e0e36d42c187298420ccd"];
// Check that the new price updates have been updated
for (var i = 0; i < new_price_updates.length; i++) {
const price_id = new_price_updates[i];
assert.notEqual(prices_after_first_update[price_id].price, prices_after_second_update[price_id].price);
}
// Check that the old price updates have been discarded
for (var i = 0; i < old_price_updates.length; i++) {
const price_id = old_price_updates[i];
assert.equal(prices_after_first_update[price_id].price, prices_after_second_update[price_id].price);
assert.equal(prices_after_first_update[price_id].conf, prices_after_second_update[price_id].conf);
assert.equal(prices_after_first_update[price_id].expo, prices_after_second_update[price_id].expo);
assert.equal(prices_after_first_update[price_id].status.toString(), prices_after_second_update[price_id].status.toString());
assert.equal(prices_after_first_update[price_id].numPublishers, prices_after_second_update[price_id].numPublishers);
assert.equal(prices_after_first_update[price_id].maxNumPublishers, prices_after_second_update[price_id].maxNumPublishers);
assert.equal(prices_after_first_update[price_id].emaPrice, prices_after_second_update[price_id].emaPrice);
assert.equal(prices_after_first_update[price_id].emaConf, prices_after_second_update[price_id].emaConf);
}
})
});
const signAndEncodeVM = async function (