From 02966b4a6f1778fb3c1cd7f136fb6e3d72dfb0d9 Mon Sep 17 00:00:00 2001 From: Jacques Vella Critien Date: Thu, 5 Jan 2023 16:07:36 +0100 Subject: [PATCH] added features to priceAggregatorChainlink --- .../src/priceAggregatorChainlink.js | 139 +++++++++++------- 1 file changed, 89 insertions(+), 50 deletions(-) diff --git a/packages/inter-protocol/src/priceAggregatorChainlink.js b/packages/inter-protocol/src/priceAggregatorChainlink.js index a2197a053c0..f5ee787646a 100644 --- a/packages/inter-protocol/src/priceAggregatorChainlink.js +++ b/packages/inter-protocol/src/priceAggregatorChainlink.js @@ -177,9 +177,12 @@ const start = async (zcf, privateArgs) => { /** * @typedef {object} RoundDetails * @property {bigint[]} submissions + * @property {String[]} submissionsBy * @property {number} maxSubmissions * @property {number} minSubmissions * @property {number} roundTimeout + * @property {Timestamp} startedAt + * @property {string} startedBy */ /** @type {MapStore} */ const details = makeScalarBigMapStore('details', { durable: true }); @@ -343,6 +346,7 @@ const start = async (zcf, privateArgs) => { const roundTimedOut = timedOut(roundId, blockTimestamp); if (!roundTimedOut) return; + console.log("round " + roundId + " timed out") const prevRound = rounds.get(subtract(roundId, 1)); @@ -360,14 +364,17 @@ const start = async (zcf, privateArgs) => { * @param {bigint} roundId */ const newRound = roundId => { - return roundId === add(reportingRoundId, 1); + let createRound = roundId === add(reportingRoundId, 1) + console.log("creating new round", roundId, createRound) + return createRound; }; /** * @param {bigint} roundId * @param {Timestamp} blockTimestamp + * @param {string} oracleAddr */ - const initializeNewRound = (roundId, blockTimestamp) => { + const initializeNewRound = (roundId, blockTimestamp, oracleAddr) => { newRound(roundId) || Fail`Round ${roundId} already started`; updateTimedOutRoundInfo(subtract(roundId, 1), blockTimestamp); @@ -376,15 +383,20 @@ const start = async (zcf, privateArgs) => { latestRoundPublisher.publish({ roundId: reportingRoundId, startedAt: blockTimestamp, + startedBy: oracleAddr, + submissionsBy: [] }); details.init( roundId, harden({ submissions: [], + submissionsBy: [], maxSubmissions: maxSubmissionCount, minSubmissions: minSubmissionCount, roundTimeout: timeout, + startedAt: blockTimestamp, + startedBy: oracleAddr }), ); @@ -397,21 +409,30 @@ const start = async (zcf, privateArgs) => { answeredInRound: 0n, }), ); + + rounds.set(roundId, { + ...rounds.get(roundId), + startedAt: blockTimestamp, + }); }; /** * @param {bigint} roundId - * @param {string} oracleId + * @param {string} oracleAddr * @param {Timestamp} blockTimestamp */ - const proposeNewRound = (roundId, oracleId, blockTimestamp) => { + const proposeNewRound = (roundId, oracleAddr, blockTimestamp) => { if (!newRound(roundId)) return; - const lastStarted = oracleStatuses.get(oracleId).lastStartedRound; // cache storage reads - if (roundId <= add(lastStarted, restartDelay) && lastStarted !== 0n) return; - initializeNewRound(roundId, blockTimestamp); + const lastStarted = oracleStatuses.get(oracleAddr).lastStartedRound; // cache storage reads + if (roundId <= add(lastStarted, restartDelay) && lastStarted !== 0n) { + console.log("Cannot propose round.") + console.log("Restart delay -", restartDelay, "Last started -", lastStarted) + return; + } + initializeNewRound(roundId, blockTimestamp, oracleAddr); - oracleStatuses.set(oracleId, { - ...oracleStatuses.get(oracleId), + oracleStatuses.set(oracleAddr, { + ...oracleStatuses.get(oracleAddr), lastStartedRound: roundId, }); }; @@ -426,22 +447,33 @@ const start = async (zcf, privateArgs) => { /** * @param {bigint} submission * @param {bigint} roundId - * @param {string} oracleId + * @param {string} oracleAddr */ - const recordSubmission = (submission, roundId, oracleId) => { + const recordSubmission = (submission, roundId, oracleAddr) => { acceptingSubmissions(roundId) || Fail`round not accepting submissions`; + console.error("Value to be submitted", submission); const lastRoundDetails = details.get(roundId); details.set(roundId, { ...lastRoundDetails, submissions: [...lastRoundDetails.submissions, submission], + submissionsBy: [...lastRoundDetails.submissionsBy, oracleAddr] }); - oracleStatuses.set(oracleId, { - ...oracleStatuses.get(oracleId), + oracleStatuses.set(oracleAddr, { + ...oracleStatuses.get(oracleAddr), lastReportedRound: roundId, latestSubmission: submission, + }) + + //update vstorage + latestRoundPublisher.publish({ + roundId: roundId, + startedAt: details.get(roundId).startedAt, + startedBy: details.get(roundId).startedBy, + submissionsBy: details.get(roundId).submissionsBy }); + return true; }; /** @@ -473,6 +505,7 @@ const start = async (zcf, privateArgs) => { answeredInRound: roundId, }); + console.error("newAnswer", newAnswer); lastValueOutForUnitIn = newAnswer; answerUpdator.updateState(undefined); @@ -525,14 +558,14 @@ const start = async (zcf, privateArgs) => { }; /** - * @param {string} oracleId + * @param {string} oracleAddr * @param {bigint} roundId * @param {Timestamp} blockTimestamp * @returns {string?} error message, if there is one */ - const validateOracleRound = (oracleId, roundId, blockTimestamp) => { + const validateOracleRound = (oracleAddr, roundId, blockTimestamp) => { // cache storage reads - const startingRound = oracleStatuses.get(oracleId).startingRound; + const startingRound = oracleStatuses.get(oracleAddr).startingRound; const rrId = reportingRoundId; let canSupersede = true; @@ -542,9 +575,9 @@ const start = async (zcf, privateArgs) => { if (startingRound === 0n) return 'not enabled oracle'; if (startingRound > roundId) return 'not yet enabled oracle'; - if (oracleStatuses.get(oracleId).endingRound < roundId) + if (oracleStatuses.get(oracleAddr).endingRound < roundId) return 'no longer allowed oracle'; - if (oracleStatuses.get(oracleId).lastReportedRound >= roundId) + if (oracleStatuses.get(oracleAddr).lastReportedRound >= roundId) return 'cannot report on previous rounds'; if ( roundId !== rrId && @@ -558,11 +591,11 @@ const start = async (zcf, privateArgs) => { }; /** - * @param {string} oracleId + * @param {string} oracleAddr * @param {bigint} roundId */ - const delayed = (oracleId, roundId) => { - const lastStarted = oracleStatuses.get(oracleId).lastStartedRound; + const delayed = (oracleAddr, roundId) => { + const lastStarted = oracleStatuses.get(oracleAddr).lastStartedRound; return roundId > add(lastStarted, restartDelay) || lastStarted === 0n; }; @@ -570,11 +603,11 @@ const start = async (zcf, privateArgs) => { * a method to provide all current info oracleStatuses need. Intended only * only to be callable by oracleStatuses. Not for use by contracts to read state. * - * @param {string} oracleId + * @param {string} oracleAddr * @param {Timestamp} blockTimestamp */ - const oracleRoundStateSuggestRound = (oracleId, blockTimestamp) => { - const oracle = oracleStatuses.get(oracleId); + const oracleRoundStateSuggestRound = (oracleAddr, blockTimestamp) => { + const oracle = oracleStatuses.get(oracleAddr); const shouldSupersede = oracle.lastReportedRound === reportingRoundId || @@ -588,7 +621,7 @@ const start = async (zcf, privateArgs) => { let eligibleToSubmit; if (canSupersede && shouldSupersede) { roundId = add(reportingRoundId, 1); - eligibleToSubmit = delayed(oracleId, roundId); + eligibleToSubmit = delayed(oracleAddr, roundId); } else { roundId = reportingRoundId; eligibleToSubmit = acceptingSubmissions(roundId); @@ -606,7 +639,7 @@ const start = async (zcf, privateArgs) => { roundTimeout = 0; } - const error = validateOracleRound(oracleId, roundId, blockTimestamp); + const error = validateOracleRound(oracleAddr, roundId, blockTimestamp); if (error !== null) { eligibleToSubmit = false; } @@ -622,31 +655,35 @@ const start = async (zcf, privateArgs) => { }; /** - * @param {string} oracleId + * @param {string} oracleAddr * @param {bigint} queriedRoundId * @param {Timestamp} blockTimestamp */ const eligibleForSpecificRound = ( - oracleId, + oracleAddr, queriedRoundId, blockTimestamp, ) => { - const error = validateOracleRound(oracleId, queriedRoundId, blockTimestamp); + const error = validateOracleRound( + oracleAddr, + queriedRoundId, + blockTimestamp, + ); if (TimeMath.absValue(rounds.get(queriedRoundId).startedAt) > 0n) { return acceptingSubmissions(queriedRoundId) && error === null; } else { - return delayed(oracleId, queriedRoundId) && error === null; + return delayed(oracleAddr, queriedRoundId) && error === null; } }; /** - * @param {string} oracleId + * @param {string} oracleAddr */ - const getStartingRound = oracleId => { + const getStartingRound = oracleAddr => { const currentRound = reportingRoundId; if ( currentRound !== 0n && - currentRound === oracleStatuses.get(oracleId).endingRound + currentRound === oracleStatuses.get(oracleAddr).endingRound ) { return currentRound; } @@ -662,9 +699,9 @@ const start = async (zcf, privateArgs) => { * directly to manage the price submissions as well as to terminate the * relationship. * - * @param {string} oracleId unique per contract instance + * @param {string} oracleAddr Bech32 of oracle operator smart wallet */ - makeOracleInvitation: async oracleId => { + makeOracleInvitation: async oracleAddr => { /** * If custom arguments are supplied to the `zoe.offer` call, they can * indicate an OraclePriceSubmission notifier and a corresponding @@ -672,9 +709,10 @@ const start = async (zcf, privateArgs) => { * reported data. * * @param {ZCFSeat} seat + * @returns {Promise<{admin: OracleAdmin, invitationMakers: {makePushPriceInvitation: (result: PriceRound) => Promise>} }>} */ const offerHandler = async seat => { - const admin = await creatorFacet.initOracle(oracleId); + const admin = await creatorFacet.initOracle(oracleAddr); const invitationMakers = Far('invitation makers', { /** @param {PriceRound} result */ PushPrice(result) { @@ -700,16 +738,17 @@ const start = async (zcf, privateArgs) => { oracleStatuses.delete(oracleId); }, + // unlike the median case, no query argument is passed, since polling behavior is undesired /** - * @param {string} oracleId unique per contract instance + * @param {string} oracleAddr uBech32 of oracle operator smart wallet */ - async initOracle(oracleId) { - assert.typeof(oracleId, 'string'); + async initOracle(oracleAddr) { + assert.typeof(oracleAddr, 'string'); oracleStatuses.init( - oracleId, + oracleAddr, harden({ - startingRound: getStartingRound(oracleId), + startingRound: getStartingRound(oracleAddr), endingRound: ROUND_MAX, lastReportedRound: 0n, lastStartedRound: 0n, @@ -740,7 +779,7 @@ const start = async (zcf, privateArgs) => { let roundId; if (roundIdRaw === undefined) { const suggestedRound = oracleRoundStateSuggestRound( - oracleId, + oracleAddr, blockTimestamp, ); roundId = suggestedRound.eligibleForSpecificRound @@ -751,7 +790,7 @@ const start = async (zcf, privateArgs) => { } const errorMsg = validateOracleRound( - oracleId, + oracleAddr, roundId, blockTimestamp, ); @@ -760,8 +799,8 @@ const start = async (zcf, privateArgs) => { assert.fail(errorMsg); } - proposeNewRound(roundId, oracleId, blockTimestamp); - recordSubmission(value, roundId, oracleId); + proposeNewRound(roundId, oracleAddr, blockTimestamp); + recordSubmission(value, roundId, oracleAddr) updateRoundAnswer(roundId, blockTimestamp); deleteRoundDetails(roundId); @@ -801,28 +840,28 @@ const start = async (zcf, privateArgs) => { * a method to provide all current info oracleStatuses need. Intended only * only to be callable by oracleStatuses. Not for use by contracts to read state. * - * @param {string} oracleId unique per contract instance + * @param {string} oracleAddr Bech32 of oracle operator smart wallet * @param {bigint} queriedRoundId */ - async oracleRoundState(oracleId, queriedRoundId) { + async oracleRoundState(oracleAddr, queriedRoundId) { const blockTimestamp = await E(timer).getCurrentTimestamp(); if (queriedRoundId > 0) { const round = rounds.get(queriedRoundId); const detail = details.get(queriedRoundId); return { eligibleForSpecificRound: eligibleForSpecificRound( - oracleId, + oracleAddr, queriedRoundId, blockTimestamp, ), queriedRoundId, - oracleStatus: oracleStatuses.get(oracleId).latestSubmission, + oracleStatus: oracleStatuses.get(oracleAddr).latestSubmission, startedAt: round.startedAt, roundTimeout: detail.roundTimeout, oracleCount: oracleCount(), }; } else { - return oracleRoundStateSuggestRound(oracleId, blockTimestamp); + return oracleRoundStateSuggestRound(oracleAddr, blockTimestamp); } }, });