Skip to content
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

OEV gateway #1663

Merged
merged 7 commits into from
Mar 8, 2023
Merged

OEV gateway #1663

merged 7 commits into from
Mar 8, 2023

Conversation

amarthadan
Copy link
Contributor

@amarthadan amarthadan commented Mar 3, 2023

Close #1579

I need to test the AWS and GCP gateways to see whether the OpenAPI templates are correct but other than that the code should be ready for a review.

@amarthadan amarthadan requested review from bbenligiray, Siegrift and a team March 3, 2023 15:31
@amarthadan amarthadan self-assigned this Mar 3, 2023
res.status(200).send(result!.data);
};

app.post(oevGatewayPath, signOevDataRequestHandler);

Check failure

Code scanning / CodeQL

Missing rate limiting

This route handler performs [authorization](1), but is not rate-limited. This route handler performs [authorization](2), but is not rate-limited.
};

app.post(oevGatewayPath, signOevDataRequestHandler);
app.options(oevGatewayPath, signOevDataRequestHandler);

Check failure

Code scanning / CodeQL

Missing rate limiting

This route handler performs [authorization](1), but is not rate-limited. This route handler performs [authorization](2), but is not rate-limited.
packages/airnode-deployer/terraform/aws/main.tf Outdated Show resolved Hide resolved
packages/airnode-node/src/handlers/sign-oev-data.test.ts Outdated Show resolved Hide resolved

export type ProcessSignOevDataRequestBody = z.infer<typeof signOevDataBodySchema>;

export function dropInvalidBeacons(beacons: Beacon[]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the verification logic should be separated into separate function:

  1. decodeBeacon - decodes encoded data and performs verifications, returns null in case something is wrong

Then you can do:

return beacons.map(decodeBeacon).filter(Boolean)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call it maybeDecodeBeacon or something, but I think the above is good enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by verification logic? Checking the signature? Why? I actually tried to put as much "processing" of individual beacons into this function as possible so we don't need to iterate over them again and again.

Copy link

@patriksimurka patriksimurka Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't mind, but would rename the function, as it's currently a bit misleading - it also modifies the returned (not dropped) beacons.

return 0;
}

export function dropInconsistentBeacons(beacons: BeaconDecoded[]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export function dropInconsistentBeacons(beacons: BeaconDecoded[]) {
// TODO: Nit: rename to "chooseMaximumConsistentSet" or something
export function dropInconsistentBeacons(beacons: BeaconDecoded[]) {
const sortedBeacons = [...beacons].sort(beaconCompare);
let maxConsistent: BeaconDecoded[] = [];
// Check every beacon interval. The maximum consistent set of beacons for aggregation must have every element
// deviating from the average by less than 1%.
for (let start = 0; start < sortedBeacons.length; start++) {
const tmp = [sortedBeacons[start]];
for (let end = start + 1; end < sortedBeacons.length; end++) {
tmp.push(sortedBeacons[end]);
// TODO: Extract to helper fn
const avg = tmp.reduce((sum, beacon) => sum.add(beacon.decodedValue), ethers.BigNumber.from(0)).div(tmp.length);
const maxDeviation = avg.div(100).mul(1);
if (
// TODO: Can be extracted as well
avg.sub(tmp.at(0)!.decodedValue).abs().lte(maxDeviation) &&
avg.sub(tmp.at(-1)!.decodedValue).abs().lte(maxDeviation)
) {
if (tmp.length > maxConsistent.length) maxConsistent = tmp;
}
}
}
return maxConsistent;
}

Copy link
Contributor

@Siegrift Siegrift Mar 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on https://api3workspace.slack.com/archives/C048B0DFMQR/p1677939910239879?thread_ts=1677744170.618349&cid=C048B0DFMQR

(btw. I didn't run/test the code in the suggestion so double check it).

Copy link
Contributor

@Siegrift Siegrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also discussed about validator failing if there is OEV gateway without signed data one. But I don't remember the resolution.

EDIT: Found the discussion https://api3workspace.slack.com/archives/C02AYRX8D89/p1676288357122369?thread_ts=1676024035.659029&cid=C02AYRX8D89. We don't want validator to validate this.

const currentTime = new Date();

return beacons.reduce((result, beacon) => {
const goProcessing = goSync(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
const goProcessing = goSync(() => {
const goParseBeacon = goSync(() => {

I dislike the name, but my suggestion is not great either... I like that all the verification is encapsulated in this single goSync though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about goBeaconValidation? That sounds better to me.

@amarthadan amarthadan requested a review from Siegrift March 6, 2023 14:46
export async function signOevData(
requestBody: ProcessSignOevDataRequestBody,
validUpdateValues: ethers.BigNumber[]
): Promise<[Error, null] | [null, SignOevDataResponse]> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) I've noticed it's by convention in this repo, but it's weird to me that the successful response has success property, but the error response doesn't.


export type ProcessSignOevDataRequestBody = z.infer<typeof signOevDataBodySchema>;

export function dropInvalidBeacons(beacons: Beacon[]) {
Copy link

@patriksimurka patriksimurka Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't mind, but would rename the function, as it's currently a bit misleading - it also modifies the returned (not dropped) beacons.

@@ -0,0 +1,191 @@
openapi: "3.0.2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How and where is this generated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not generated, it was written manually.

@amarthadan amarthadan requested a review from Siegrift March 7, 2023 09:57
Copy link
Contributor

@Siegrift Siegrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 LGTM, but haven't tested on serverless.

Leaving that to @patriksimurka while implementing the related functionality for OEV relay.

@patriksimurka patriksimurka self-requested a review March 7, 2023 13:19
Copy link

@patriksimurka patriksimurka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@amarthadan
Copy link
Contributor Author

After running the gateway on GCP I needed to change two more things:

  • I needed to remove the request description from the OpenAPI spec for GCP as it complained about the usage of the array type. Not a big deal, GCP doesn't really use it for anything anyway.
  • I needed to bump the memory allocation for the cloud function handling the request from 128MB to 256MB as it was failing on the lower one. It was only needed for GCP but I did the same for AWS as well as I presume that their functions are not that much more efficient and we would run into this limitation later anyway. I guess that's also not that of a big deal.

@amarthadan amarthadan merged commit a85a586 into master Mar 8, 2023
@amarthadan amarthadan deleted the oev-gateway branch March 8, 2023 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement the HTTP gateway to facilitate OEV auctions
3 participants