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

refactor: Update function selector computation #2001

Merged
merged 5 commits into from
Sep 5, 2023

Conversation

LHerskind
Copy link
Contributor

Fixed #1990. Also adds an oracle to more easily compute selectors from inside the contracts for improved devex.

Checklist:

Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge.

  • If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag.
  • I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code.
  • Every change is related to the PR description.
  • I have linked this pull request to relevant issues (if any exist).

@@ -29,6 +29,7 @@ export const ONE_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES - 1)}
* The supported oracle names.
*/
type ORACLE_NAMES =
| 'computeSelector'
Copy link
Member

Choose a reason for hiding this comment

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

despite the convinience, computing the selector is something that should be done at compile time, and not at runtime. A malicious app could alter the source code / calls being made without changing the verification key.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the sandbox a malicious actor can do whatever they want. Calling other functions are also done through oracles where it could just return success independent of what happens. This is essentially just a helper to compute an input to another function that is completely controlled by a possibly malicious app so not sure it changes much there 🤷

Copy link
Collaborator

Choose a reason for hiding this comment

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

I may be missing something, but I agree with @Maddiaa0 on this one. Calling other functions as you say can also return anything, but we then have a kernel circuit that ensures that the oracle behaved properly. But it's true it doesn't affect the security of sandbox as it is today.

I'm fine merging this, but we should add an issue to replace it with a Noir-only implementation in the future (ie before testnet).

Copy link
Contributor

@iAmMichaelConnor iAmMichaelConnor Sep 5, 2023

Choose a reason for hiding this comment

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

I suppose if the sought-after pattern is to have the selector be a parameter to the function, then an oracle is ok. It would be akin to this Solidity pseudocode (because I've forgotten how to write solidity):

function do_an_arbitrary_call(bytes4 selector) {
    address contract_address = 0x1234...;
    call(contract_address, selector);
}

But if a user actually wants to always call a specific selector, then an oracle call is dangerous, because it isn't constrained:

function do_a_specific_call() {
    address contract_address = 0x1234...;
    bytes4 selector = 0x12345678; // this cannot be achieved with an oracle call
    call(contract_address, selector);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Allowing users to take over control flow is almost always a death-sin in solidity. I agree with both @spalladino and @Maddiaa0 that having an oracle is horrible, but since you can take over whenever through some of the other execution it seemed like a solution that was usable for this problem. #1550 should get rid of it, it is just used temporarily because it is too shit to compute selectors by hand when you want self-calls etc.

@LHerskind LHerskind force-pushed the lh/1990-function-selector branch from 330ae13 to d391150 Compare September 5, 2023 12:12
@@ -29,6 +29,7 @@ export const ONE_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES - 1)}
* The supported oracle names.
*/
type ORACLE_NAMES =
| 'computeSelector'
Copy link
Collaborator

Choose a reason for hiding this comment

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

I may be missing something, but I agree with @Maddiaa0 on this one. Calling other functions as you say can also return anything, but we then have a kernel circuit that ensures that the oracle behaved properly. But it's true it doesn't affect the security of sandbox as it is today.

I'm fine merging this, but we should add an issue to replace it with a Noir-only implementation in the future (ie before testnet).

let abi: FunctionAbiWithDebugMetadata | undefined = undefined;

// Brute force
for (let i = 1; i < 5; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe we got a MAX_NOTE_FIELDS_LENGTH constant we could use here instead of the 5

Comment on lines 3 to 5
export const computeNoteHashAndNullifierSignature = 'compute_note_hash_and_nullifier(Field,Field,Field,[Field;3])';

export const computeNoteHashAndNullifierSelector = FunctionSelector.fromSignature(computeNoteHashAndNullifierSignature);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we remove these, given they are replaced by the loop?

@@ -88,6 +88,7 @@ describe('e2e_lending_contract', () => {
stableCoin: NativeTokenContract,
account: Account,
) => {
await sleep(5000);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this needed? If so, let's add a comment on why.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, no was when debugging and thinking it was due to race-conditions, will remove it.

Comment on lines 102 to 103
// If you are debugging, can be useful to uncomment the following line.
// console.log(`Function selector for ${signature} is ${selector}`);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not use a debug logger, so it can be turned on with an env var? Even better, we can have our users turn that on without having to change the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure why. But if I use the debug logger in here. It breaks jest toEqual and typing in the server_world_state_synchroniser.

This Is Fine GIF

Copy link
Collaborator

Choose a reason for hiding this comment

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

It probably breaks if you add it as an instance variable, but you can always create a new one as a local variable for this function and then ditch it.

Comment on lines +93 to +94
export class FunctionSignatureDecoder {
constructor(private name: string, private parameters: ABIParameter[]) {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know this is a matter of personal preference, but I prefer to export plain functions rather than classes with a "do" method. A user of this API just wants to decodeFunctionSignature(name, parameters), and doesn't care about the FunctionSignatureDecoder that's doing the job.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added decodeFunctionSignature to handle it

@@ -355,7 +337,7 @@ contract NativeToken {
// Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes.
unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {
let note_header = NoteHeader { contract_address, nonce, storage_slot };
if (storage_slot == 2) {
if (storage_slot == 3) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Curious how you found this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had failures because of missing compute_note_hash_and_nullifier so debugged from there and saw that the slot did not match what was in storage. The actual issue was then the different selectors 😬 leading to the loop.

* @returns The function signature.
*/
public decode(): string {
const hmm = this.parameters.map(param => this.decodeParameter(param.type));
Copy link
Member

Choose a reason for hiding this comment

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

naming

Copy link
Contributor Author

Choose a reason for hiding this comment

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

guy skibbidy

@@ -815,7 +819,7 @@ describe('Private Execution test suite', () => {
);

expect(result.enqueuedPublicFunctionCalls).toHaveLength(1);
expect(result.enqueuedPublicFunctionCalls[0]).toEqual(publicCallRequest);
expect(result.enqueuedPublicFunctionCalls[0].toBuffer()).toEqual(publicCallRequest.toBuffer());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For some reason, the toEqual was failing with the objects directly, even though they serialise to the same values 🤷

@LHerskind LHerskind force-pushed the lh/1990-function-selector branch from ffe1078 to 92c0b18 Compare September 5, 2023 20:27
@LHerskind LHerskind enabled auto-merge (squash) September 5, 2023 20:38
@LHerskind LHerskind merged commit e07ea1a into master Sep 5, 2023
@LHerskind LHerskind deleted the lh/1990-function-selector branch September 5, 2023 20:45
spypsy pushed a commit that referenced this pull request Sep 6, 2023
🤖 I have created a new Aztec Packages release
---


##
[0.1.0-alpha60](v0.1.0-alpha59...v0.1.0-alpha60)
(2023-09-06)


### Features

* Goblin recursive verifier
([#1822](#1822))
([f962cb6](f962cb6))
* initial `is_valid` eip1271 style wallet + minimal test changes
([#1935](#1935))
([f264c54](f264c54))


### Bug Fixes

* benchmark git repo
([#2041](#2041))
([3c696bb](3c696bb))
* cli canary & deployment
([#2053](#2053))
([1ddd24a](1ddd24a))
* **rpc:** Fixes getNodeInfo serialisation
([#1991](#1991))
([0a29fa8](0a29fa8))


### Miscellaneous

* **circuits:** - use msgpack for cbind routines of native private
kernel circuits
([#1938](#1938))
([3dc5c07](3dc5c07))
* **docs:** API docs stucture
([#2014](#2014))
([9aab9dd](9aab9dd))
* Update function selector computation
([#2001](#2001))
([e07ea1a](e07ea1a))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Update selector computation
4 participants