diff --git a/packages/data-pusher/src/api-requests/data-provider.ts b/packages/data-pusher/src/api-requests/data-provider.ts index 7c5e1a62..adac40aa 100644 --- a/packages/data-pusher/src/api-requests/data-provider.ts +++ b/packages/data-pusher/src/api-requests/data-provider.ts @@ -2,10 +2,9 @@ import * as node from '@api3/airnode-node'; import { isNil, pick } from 'lodash'; import { getState } from '../state'; import { preProcessApiSpecifications } from '../unexported-airnode-features/api-specification-processing'; -import { SignedApiUpdate, TemplateId } from '../validation/schema'; +import { SignedApiUpdate } from '../validation/schema'; import { logger } from '../logger'; - -export type TemplateResponse = [TemplateId, node.HttpGatewayApiCallSuccessResponse]; +import { TemplateResponse } from '../sign-template-data'; export const callApi = async (payload: node.ApiCallPayload) => { logger.debug('Preprocessing API call payload', pick(payload.aggregatedApiCall, ['endpointName', 'oisTitle'])); diff --git a/packages/data-pusher/src/api-requests/signed-api.test.ts b/packages/data-pusher/src/api-requests/signed-api.test.ts index 2c30ce0a..0d2ce268 100644 --- a/packages/data-pusher/src/api-requests/signed-api.test.ts +++ b/packages/data-pusher/src/api-requests/signed-api.test.ts @@ -1,26 +1,10 @@ import axios from 'axios'; import { ZodError } from 'zod'; -import { postSignedApiData, signTemplateResponses } from './signed-api'; -import { config, signedApiResponse, nodarySignedTemplateResponses, nodaryTemplateResponses } from '../../test/fixtures'; +import { postSignedApiData } from './signed-api'; +import { config, signedApiResponse, nodarySignedTemplateResponses } from '../../test/fixtures'; import { logger } from '../logger'; import * as stateModule from '../state'; -describe(signTemplateResponses.name, () => { - it('signs template responses', async () => { - const state = stateModule.getInitialState(config); - jest.spyOn(stateModule, 'getState').mockReturnValue(state); - jest.useFakeTimers().setSystemTime(new Date('2023-01-20')); - - const signedTemplateResponses = await signTemplateResponses(nodaryTemplateResponses); - - expect(signedTemplateResponses).toEqual(nodarySignedTemplateResponses); - }); - - afterEach(() => { - jest.useRealTimers(); - }); -}); - describe(postSignedApiData.name, () => { it('posts data to central api', async () => { const state = stateModule.getInitialState(config); diff --git a/packages/data-pusher/src/api-requests/signed-api.ts b/packages/data-pusher/src/api-requests/signed-api.ts index 01007b60..c910926f 100644 --- a/packages/data-pusher/src/api-requests/signed-api.ts +++ b/packages/data-pusher/src/api-requests/signed-api.ts @@ -3,14 +3,10 @@ import axios, { AxiosError } from 'axios'; import { isEmpty, isNil } from 'lodash'; import { ethers } from 'ethers'; import { deriveBeaconId } from '@api3/airnode-node'; -import { TemplateResponse } from './data-provider'; import { logger } from '../logger'; import { getState } from '../state'; import { SignedApiNameUpdateDelayGroup } from '../update-signed-api'; -import { SignedApiPayload, SignedData, TemplateId, signedApiResponseSchema } from '../validation/schema'; -import { signWithTemplateId } from '../utils'; - -export type SignedResponse = [TemplateId, SignedData]; +import { SignedApiPayload, signedApiResponseSchema } from '../validation/schema'; export const postSignedApiData = async (group: SignedApiNameUpdateDelayGroup) => { const { @@ -70,34 +66,3 @@ export const postSignedApiData = async (group: SignedApiNameUpdateDelayGroup) => logger.info(`Pushed signed data updates to the signed API.`, { ...logContext, count }); return { success: true, count }; }; - -// TODO: This function could be moved elsewhere -export const signTemplateResponses = async (templateResponses: TemplateResponse[]) => { - logger.debug('Signing template responses', { templateResponses }); - - const signPromises = templateResponses.map(async ([templateId, response]) => { - const encodedValue = response.data.encodedValue; - const timestamp = Math.floor(Date.now() / 1000).toString(); - - const wallet = ethers.Wallet.fromMnemonic(getState().config.airnodeWalletMnemonic); - const goSignWithTemplateId = await go(() => signWithTemplateId(wallet, templateId, timestamp, encodedValue)); - if (!goSignWithTemplateId.success) { - const message = `Failed to sign response. Error: "${goSignWithTemplateId.error}"`; - logger.warn(message, { templateId }); - return null; - } - - return [ - templateId, - { - timestamp: timestamp, - encodedValue: encodedValue, - signature: goSignWithTemplateId.data, - }, - ]; - }); - const signedResponsesOrNull = await Promise.all(signPromises); - const signedResponses = signedResponsesOrNull.filter((response): response is SignedResponse => !isNil(response)); - - return signedResponses; -}; diff --git a/packages/data-pusher/src/fetch-beacon-data.ts b/packages/data-pusher/src/fetch-beacon-data.ts index 7eecc8d9..d1b5fe37 100644 --- a/packages/data-pusher/src/fetch-beacon-data.ts +++ b/packages/data-pusher/src/fetch-beacon-data.ts @@ -5,7 +5,7 @@ import { sleep } from './utils'; import { SignedApiUpdate } from './validation/schema'; import { NO_FETCH_EXIT_CODE } from './constants'; import { makeTemplateRequests } from './api-requests/data-provider'; -import { signTemplateResponses } from './api-requests/signed-api'; +import { signTemplateResponses } from './sign-template-data'; export const initiateFetchingBeaconData = () => { logger.debug('Initiating fetching all beacon data'); diff --git a/packages/data-pusher/src/sign-template-data.test.ts b/packages/data-pusher/src/sign-template-data.test.ts new file mode 100644 index 00000000..437023ac --- /dev/null +++ b/packages/data-pusher/src/sign-template-data.test.ts @@ -0,0 +1,19 @@ +import { signTemplateResponses } from './sign-template-data'; +import * as stateModule from './state'; +import { config, nodarySignedTemplateResponses, nodaryTemplateResponses } from '../test/fixtures'; + +describe(signTemplateResponses.name, () => { + it('signs template responses', async () => { + const state = stateModule.getInitialState(config); + jest.spyOn(stateModule, 'getState').mockReturnValue(state); + jest.useFakeTimers().setSystemTime(new Date('2023-01-20')); + + const signedTemplateResponses = await signTemplateResponses(nodaryTemplateResponses); + + expect(signedTemplateResponses).toEqual(nodarySignedTemplateResponses); + }); + + afterEach(() => { + jest.useRealTimers(); + }); +}); diff --git a/packages/data-pusher/src/sign-template-data.ts b/packages/data-pusher/src/sign-template-data.ts new file mode 100644 index 00000000..0ae0f9ff --- /dev/null +++ b/packages/data-pusher/src/sign-template-data.ts @@ -0,0 +1,44 @@ +import { ethers } from 'ethers'; +import { go } from '@api3/promise-utils'; +import * as node from '@api3/airnode-node'; +import { isNil } from 'lodash'; +import { logger } from './logger'; +import { getState } from './state'; +import { signWithTemplateId } from './utils'; +import { SignedData, TemplateId } from './validation/schema'; + +export type SignedResponse = [TemplateId, SignedData]; + +export type TemplateResponse = [TemplateId, node.HttpGatewayApiCallSuccessResponse]; + +export const signTemplateResponses = async (templateResponses: TemplateResponse[]) => { + logger.debug('Signing template responses', { templateResponses }); + + const signPromises = templateResponses.map(async ([templateId, response]) => { + const encodedValue = response.data.encodedValue; + const timestamp = Math.floor(Date.now() / 1000).toString(); + + const wallet = ethers.Wallet.fromMnemonic(getState().config.airnodeWalletMnemonic); + const goSignWithTemplateId = await go(() => signWithTemplateId(wallet, templateId, timestamp, encodedValue)); + if (!goSignWithTemplateId.success) { + const message = `Failed to sign response. Error: "${goSignWithTemplateId.error}"`; + logger.warn(message, { + templateId, + }); + return null; + } + + return [ + templateId, + { + timestamp: timestamp, + encodedValue: encodedValue, + signature: goSignWithTemplateId.data, + }, + ]; + }); + const signedResponsesOrNull = await Promise.all(signPromises); + const signedResponses = signedResponsesOrNull.filter((response): response is SignedResponse => !isNil(response)); + + return signedResponses; +}; diff --git a/packages/data-pusher/test/fixtures.ts b/packages/data-pusher/test/fixtures.ts index d028348e..00d3b888 100644 --- a/packages/data-pusher/test/fixtures.ts +++ b/packages/data-pusher/test/fixtures.ts @@ -2,8 +2,7 @@ import { PerformApiCallSuccess } from '@api3/airnode-node/dist/src/api'; import { ApiCallErrorResponse } from '@api3/airnode-node'; import { AxiosResponse } from 'axios'; import { Config } from '../src/validation/schema'; -import { SignedResponse } from '../src/api-requests/signed-api'; -import { TemplateResponse } from '../src/api-requests/data-provider'; +import { SignedResponse, TemplateResponse } from '../src/sign-template-data'; export const config: Config = { airnodeWalletMnemonic: 'diamond result history offer forest diagram crop armed stumble orchard stage glance',