diff --git a/.changeset/stale-beers-tickle.md b/.changeset/stale-beers-tickle.md new file mode 100644 index 0000000000..e27fa34070 --- /dev/null +++ b/.changeset/stale-beers-tickle.md @@ -0,0 +1,5 @@ +--- +'@api3/airnode-node': patch +--- + +Remove airnode address from templateId verification diff --git a/packages/airnode-node/src/api/index.ts b/packages/airnode-node/src/api/index.ts index d7e137d461..dedb745d55 100644 --- a/packages/airnode-node/src/api/index.ts +++ b/packages/airnode-node/src/api/index.ts @@ -6,7 +6,7 @@ import { getMasterHDNode } from '../evm'; import { getReservedParameters } from '../adapters/http/parameters'; import { API_CALL_TIMEOUT, API_CALL_TOTAL_TIMEOUT } from '../constants'; import { isValidSponsorWallet, isValidRequestId } from '../evm/verification'; -import { getExpectedTemplateId } from '../evm/templates'; +import { getExpectedTemplateIdV0, getExpectedTemplateIdV1 } from '../evm/templates'; import { AggregatedApiCall, ApiCallResponse, LogsData, RequestErrorMessage, ApiCallErrorResponse } from '../types'; import { Config } from '../config/types'; @@ -126,7 +126,7 @@ function verifyRequestId(payload: CallApiPayload): LogsData | null { const { aggregatedApiCall } = payload; - // TODO: check if beacon needs to verify templates + if (aggregatedApiCall.type === 'http-gateway') return null; const { templateId, template, id } = aggregatedApiCall; @@ -146,7 +146,11 @@ export function verifyTemplateId(payload: CallApiPayload): LogsData { const TEMPLATE_ID = '0xb2f063157fcc3c986daf4c2cf1b8ac8b8843f2b1a54c5de5e1ebdf12fb85a927'; + it('derives templateId for V0', () => { + const expectedTemplateIdV0 = verification.getExpectedTemplateIdV0(validTemplateFields); + + expect(expectedTemplateIdV0).toEqual( + utils.keccak256( + utils.solidityPack( + ['address', 'bytes32', 'bytes'], + [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x2f3a3adf6daf5a3bb00ab83aa82262a6a84b59b0a89222386135330a1819ab48', + '0x6466726f6d63455448', + ] + ) + ) + ); + }); + it('derives templateId for V1', () => { + const expectedTemplateIdV1 = verification.getExpectedTemplateIdV1(validTemplateFields); + + expect(expectedTemplateIdV1).toEqual( + utils.keccak256( + utils.solidityPack( + ['bytes32', 'bytes'], + ['0x2f3a3adf6daf5a3bb00ab83aa82262a6a84b59b0a89222386135330a1819ab48', '0x6466726f6d63455448'] + ) + ) + ); + }); + it('returns API calls not linked to templates', () => { const aggregatedApiCall = fixtures.buildAggregatedRegularApiCall({ templateId: null }); const config = fixtures.buildConfig(); @@ -79,7 +109,7 @@ describe('verify', () => { templateId: TEMPLATE_ID, template: invalidTemplate, }); - const expectedTemplateId = verification.getExpectedTemplateId(invalidTemplate); + const expectedTemplateId = verification.getExpectedTemplateIdV0(invalidTemplate); const response = verifyTemplateId({ aggregatedApiCall, config }); expect(response).toEqual([ [ diff --git a/packages/airnode-node/src/evm/templates/template-verification.ts b/packages/airnode-node/src/evm/templates/template-verification.ts index d3889cebc2..fd25715975 100644 --- a/packages/airnode-node/src/evm/templates/template-verification.ts +++ b/packages/airnode-node/src/evm/templates/template-verification.ts @@ -6,7 +6,7 @@ interface ValidatedField { readonly value: any; } -function getTemplateIdValidationFields(template: ApiCallTemplateWithoutId): ValidatedField[] { +function getTemplateIdValidationFieldsV0(template: ApiCallTemplateWithoutId): ValidatedField[] { return [ { type: 'address', value: template.airnodeAddress }, { type: 'bytes32', value: template.endpointId }, @@ -14,8 +14,26 @@ function getTemplateIdValidationFields(template: ApiCallTemplateWithoutId): Vali ]; } -export function getExpectedTemplateId(template: ApiCallTemplateWithoutId): string { - const validatedFields = getTemplateIdValidationFields(template); +export function getExpectedTemplateIdV0(template: ApiCallTemplateWithoutId): string { + const validatedFields = getTemplateIdValidationFieldsV0(template); + const types = validatedFields.map((v) => v.type); + const values = validatedFields.map((v) => v.value); + const { + utils: { keccak256, solidityPack }, + } = ethers; + + return keccak256(solidityPack(types, values)); +} + +function getTemplateIdValidationFieldsV1(template: ApiCallTemplateWithoutId): ValidatedField[] { + return [ + { type: 'bytes32', value: template.endpointId }, + { type: 'bytes', value: template.encodedParameters }, + ]; +} + +export function getExpectedTemplateIdV1(template: ApiCallTemplateWithoutId): string { + const validatedFields = getTemplateIdValidationFieldsV1(template); const types = validatedFields.map((v) => v.type); const values = validatedFields.map((v) => v.value); const { diff --git a/packages/airnode-node/src/handlers/process-http-signed-data-request.test.ts b/packages/airnode-node/src/handlers/process-http-signed-data-request.test.ts index f5ee940f80..ab92f713b9 100644 --- a/packages/airnode-node/src/handlers/process-http-signed-data-request.test.ts +++ b/packages/airnode-node/src/handlers/process-http-signed-data-request.test.ts @@ -5,7 +5,7 @@ import * as fixtures from '../../test/fixtures'; import { HttpSignedDataApiCallSuccessResponse } from '../types'; const ENDPOINT_ID = '0x13dea3311fe0d6b84f4daeab831befbc49e19e6494c41e9e065a09c3c68f43b6'; -const TEMPLATE_ID = '0x600975681b98422eee1146d4b835a8103689ae4cddb76069925a929caf0eb79f'; +const TEMPLATE_ID = '0xaa1525fe964092a826934ff09c75e1db395b947543a4ca3eb4a19628bad6c6d5'; function buildConfigWithEndpoint(endpoint?: Endpoint) { const endpoints = endpoint ? [endpoint] : []; diff --git a/packages/airnode-node/src/handlers/process-http-signed-data-request.ts b/packages/airnode-node/src/handlers/process-http-signed-data-request.ts index a14a782365..f3bc2b5270 100644 --- a/packages/airnode-node/src/handlers/process-http-signed-data-request.ts +++ b/packages/airnode-node/src/handlers/process-http-signed-data-request.ts @@ -5,7 +5,7 @@ import * as evm from '../evm'; import { AggregatedApiCall, HttpSignedDataApiCallSuccessResponse, ApiCallTemplateWithoutId } from '../types'; import { callApi } from '../api'; import { Config } from '../config/types'; -import { getExpectedTemplateId } from '../evm/templates'; +import { getExpectedTemplateIdV1 } from '../evm/templates'; export async function processHttpSignedDataRequest( config: Config, @@ -39,7 +39,7 @@ export async function processHttpSignedDataRequest( endpointId, encodedParameters, }; - const templateId = getExpectedTemplateId(template); + const templateId = getExpectedTemplateIdV1(template); const aggregatedApiCall: AggregatedApiCall = { type: 'http-signed-data-gateway',