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

chore: Validate RPC inputs #9672

Merged
merged 27 commits into from
Nov 10, 2024
Merged

chore: Validate RPC inputs #9672

merged 27 commits into from
Nov 10, 2024

Conversation

spalladino
Copy link
Collaborator

@spalladino spalladino commented Nov 1, 2024

Adds schemas for every API. Every API exposed via JSON RPC now requires a zod schema (see #9656 for more context on the rationale for this change). All schemas are in circuit-types/interfaces, and look like:

/** Schemas for prover node API functions. */
export const ProverNodeApiSchema: ApiSchemaFor<ProverNodeApi> = {
getJobs: z
.function()
.args()
.returns(z.array(z.object({ uuid: z.string(), status: z.enum(EpochProvingJobState) }))),
startProof: z.function().args(schemas.Integer).returns(z.void()),
prove: z.function().args(schemas.Integer).returns(z.void()),
sendEpochProofQuote: z.function().args(EpochProofQuote.schema).returns(z.void()),
};

These schemas are type-checked against the interface via the ApiSchemaFor utility type, so if the interface changes, schemas are required by the compiler to change as well. Schemas are now used in the JSON RPC server to 1) identify which methods are exposed (so we no longer need the method disallowlist) and 2) parse their arguments. The JSON RPC server, once it has identified the method to be called, grabs the arguments schema and funnels the result of a vanilla JSON parse through it.

Every type or struct that is exposed via an interface now has an associated schema, which is referenced in the API for parsing. Schemas both validate input and hydrate instances. This means that we no longer set a type property to identify how to hydrate each object in a request during deserialization, which was a security risk.

static get schema() {
return z
.object({
archive: AppendOnlyTreeSnapshot.schema,
header: Header.schema,
body: Body.schema,
})
.transform(({ archive, header, body }) => new L2Block(archive, header, body));
}

Schemas are also used in the JSON RPC client for deserializing the result types. Again, this lets us remove the type parameter from all serialized entities, though this is still present in since it is required by the TypeRegistry (still to be removed) which is only used in the snapshot manager.

All schemas are tested via mini integration tests. These tests define a mock implementation for each service, use it for setting up a JSON RPC server, starting it in a free port, and test calling every method through JSON RPC.

beforeEach(async () => {
handler = new MockProverNode();
context = await createJsonRpcTestSetup<ProverNodeApi>(handler, ProverNodeApiSchema);
});
afterEach(() => {
tested.add(/^ProvingNodeApiSchema\s+([^(]+)/.exec(expect.getState().currentTestName!)![1]);
context.httpServer.close();
});
afterAll(() => {
const all = Object.keys(ProverNodeApiSchema);
expect([...tested].sort()).toEqual(all.sort());
});
it('getJobs', async () => {
const jobs = await context.client.getJobs();
const expected = await handler.getJobs();
expect(jobs).toEqual(expected);
});

These changes prompted other changes. For instance, we introduced the following changes to APIs:

  • ProvingJobSource.rejectProvingJob now accepts a reason string instead of an Error type
  • PXE.getEvents(type) is removed in favor of PXE.getEncryptedEvents and PXE.getUnencryptedEvents since both methods required different arguments

We also removed service-management methods (ie stop) from interfaces. We were inadvertently calling stop on remote instances over http when we shouldn't have. We also typed some previously untyped interfaces, such as the TXE's.

Fixes #9455

@spalladino spalladino force-pushed the palla/validate-rpc-inputs branch 2 times, most recently from 7260917 to 8b713f0 Compare November 5, 2024 15:26
@spalladino spalladino changed the title wip: Validate RPC inputs chore: Validate RPC inputs Nov 5, 2024
@spalladino spalladino marked this pull request as ready for review November 5, 2024 15:50
@spalladino spalladino force-pushed the palla/validate-rpc-inputs branch 4 times, most recently from 072ad42 to 90c592c Compare November 7, 2024 20:01
@spalladino spalladino self-assigned this Nov 8, 2024
@spalladino spalladino force-pushed the palla/validate-rpc-inputs branch 3 times, most recently from 3e78ec7 to d9d94e3 Compare November 8, 2024 23:18
@spalladino spalladino force-pushed the palla/validate-rpc-inputs branch from 16b649c to 369fb71 Compare November 9, 2024 02:47
Some processes may prefer IPv6 for localhost. Like the nargo external oracle resolver when run in earthly.
@spalladino spalladino enabled auto-merge (squash) November 9, 2024 19:52
@@ -65,7 +65,7 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod {
caller: this.paymentContract,
action: {
name: 'setup_refund',
args: [this.feeRecipient, this.wallet.getAddress(), maxFee, nonce],
args: [this.feeRecipient.toField(), this.wallet.getAddress().toField(), maxFee, nonce],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very happy to get rid of the inheritance here

import { TxEffect } from '../tx_effect.js';
import { type ArchiverApi, ArchiverApiSchema } from './archiver.js';

describe('ArchiverApiSchema', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice

* @returns Whether the message is synced and ready to be included in a block.
*/
isL1ToL2MessageSynced(l1ToL2Message: Fr, startL2BlockNumber: number): Promise<boolean>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Reason this isn't needed anymore?

@@ -121,6 +124,21 @@ export class Tx extends Gossipable {
]);
}

static get schema() {
// TODO(palla/schemas): Use the nested objects schemas as opposed to the toBuffers
Copy link
Collaborator

Choose a reason for hiding this comment

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

noting no issue number

@@ -16,7 +16,7 @@ describe('ArtifactHash', () => {
notes: {},
};
expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot(
`"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting, why the change?

@@ -53,11 +53,11 @@ export const ProtocolContractLeaf = {
AuthRegistry: Fr.fromString('0x295f52c40413b660d817ceea60d07154322a035d28d18927b2ca84e8ec3b2115'),
ContractInstanceDeployer: Fr.fromString('0x00113ad28d270a493266484d733f73a56b98c74b4e2cdf9fc040b5d3a6560f2d'),
ContractClassRegisterer: Fr.fromString('0x1d591819cccc4031cc18a7865321c54f6344ae42a205782874d1f72648df2034'),
MultiCallEntrypoint: Fr.fromString('0x20a2e7e882045d27b3aa9e36188b8e45483b3c11652d4a46406699e5eb4efa9b'),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Curious why these change?

@@ -1,337 +0,0 @@
import cors from '@koa/cors';
import http from 'http';
Copy link
Collaborator

Choose a reason for hiding this comment

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

nice

@ludamad
Copy link
Collaborator

ludamad commented Nov 10, 2024

LGTM.

Review methodology:

  • look at methodology, agree with the approach
  • every line of diff needs to hit my eyeballs
  • consider large parts of it as low risk because of 1. type-checked schema 2. has tests 3. written by Palla
  • look closer at parts that stand out like hash changes, package.json changes etc
  • say LGTM

@spalladino spalladino merged commit 6554122 into master Nov 10, 2024
66 checks passed
@spalladino spalladino deleted the palla/validate-rpc-inputs branch November 10, 2024 04:49
ludamad added a commit that referenced this pull request Nov 11, 2024
ludamad added a commit that referenced this pull request Nov 11, 2024
Reverts #9672 as it broke some e2e tests and
boxes
TomAFrench added a commit that referenced this pull request Nov 11, 2024
* master: (182 commits)
  feat(avm): mem specific range check (#9828)
  refactor: remove public kernel inner (#9865)
  chore: Revert "chore: Validate RPC inputs" (#9875)
  Revert "fix: deploy l2 contracts fails on 48 validator" (#9871)
  fix: deploy l2 contracts fails on 48 validator (#9860)
  chore: Validate RPC inputs (#9672)
  fix: fixing devcontainers to use the sandbox docker-compose file (#9782)
  fix: Revert changes to ci.yml (#9863)
  chore: Move epoch and slot durations to config (#9861)
  git subrepo push --branch=master noir-projects/aztec-nr
  git_subrepo.sh: Fix parent in .gitrepo file. [skip ci]
  chore: replace relative paths to noir-protocol-circuits
  git subrepo push --branch=master barretenberg
  fix: tree heights that last past 3 days (#9760)
  fix(build): l1-contracts .rebuild_patterns did not cover test files (#9862)
  fix: bench prover test (#9856)
  fix: Fix mac build by calling `count` on durations (#9855)
  feat: zk shplemini (#9830)
  feat: domain separate block proposals and attestations (#9842)
  chore: bump runner cache disk size (#9849)
  ...
spalladino added a commit that referenced this pull request Nov 11, 2024
Second attempt at #9672 after it broke some e2e tests on master.
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.

Epic: Validate messages received over JSON RPC interfaces
2 participants