AztecProtocol / aztec-packages

Apache License 2.0
195 stars 206 forks source link

Lack of trait implementation for the type `[u8; 32]` #8380

Open NikolayKostadinov21 opened 1 month ago

NikolayKostadinov21 commented 1 month ago

Overview

I am trying to implement the Serialize trait for the struct struct BytesBatch { bytes: [u8; 32], } . My attempts so far are unfruitful because of the No method named 'serialize' found for type '[u8; 32]' error. Since the orphan rule in Rust states that you can only implement a trait for a type if either the trait or the type is defined in your crate. This means that you cannot implement a foreign trait for a foreign type directly in your crate. Thus I am unable to serialize my BytesBatch struct.

main.nr

contract Main {
    use dep::aztec::prelude::{Map, PublicMutable};
    use dep::aztec::protocol_types::traits::{Serialize, Deserialize};

    global ONE = 1;
    global THIRTY_TWO = 32;

    impl Serialize<ONE> for BytesBatch {
        fn serialize(self) -> [Field; ONE] {
            [bytes32_to_field(self)]
        }
    }

    impl Deserialize<ONE> for BytesBatch {
        fn deserialize(input: [Field; ONE]) -> Self {
            Self {
                bytes: input[0].to_le_bytes(THIRTY_TWO).as_array()
            }
        }
    }

    // Convert a 32 byte array to a field element by modding
    fn bytes32_to_field(feed: BytesBatch) -> pub Field {
        // Convert it to a field element
        let mut v = 1;
        let mut high = 0 as Field;
        let mut low = 0 as Field;

        for i in 0..16 {
            high = high + (feed.bytes[15 - i] as Field) * v;
            low = low + (feed.bytes[16 + 15 - i] as Field) * v;
            v = v * 256;
        }
        // Abuse that a % p + b % p = (a + b) % p and that low < p
        low + high * v
    }

    struct BytesBatch {
        bytes: [u8; THIRTY_TWO],
    }

    #[aztec(storage)]
    struct Storage {
        fields: Map<u64, PublicMutable<Field>>,
    }

    #[aztec(public)]
    fn setFields(key: [u64; THIRTY_TWO], input: [[u8; THIRTY_TWO]; THIRTY_TWO], length: u64) {
        for i in 0..length {
            let current_bytes = bytes32_to_field(BytesBatch { bytes: input[i] });
            storage.fields.at(key[i]).write(current_bytes);
        }
    }

    unconstrained fn getFields(key: u64) -> pub Field {
        storage.fields.at(key).read()
    }
}

The problem

I cannot compile, nor test my smart contract program due to the aforementioned issues. The error is as follows:

error: No method named 'serialize' found for type '[u8; 32]'
   ┌─ src/main.nr:24:15
   │
24 │         // Convert it to a field element
   │               -------------
   │

And as already mentioned, obviously if I attempt to implement the serialize trait for [u8; 32], I get the error below:

`error: Orphaned trait implementation
  ┌─ src/main.nr:8:28
  │
8 │     impl Serialize<32> for [u8; 32] {
  │                            -------- Either the type or the trait must be from the same crate as the trait implementation
  │

Aborting due to 1 previous error
NikolayKostadinov21 commented 1 month ago

Tools versions

Noir compiler version: >=0.33.0 aztec version: 0.52.0, currently the latest aztec.js and the other js package: ^0.52.0, currently the latest

Steps to reproduce

  1. Initialize a new Aztec contract project and replace its content with the one above. (main.nr)
  2. Create new package.json and tsconfig.json.

package.json:

{
  "scripts": {
    "test": "yarn vitest",
    "compile": "${AZTEC_NARGO:-aztec-nargo} compile --silence-warnings",
    "codegen": "aztec codegen target --outdir src/artifacts"
  },
  "type": "module",
  "module": "esnext",
  "dependencies": {
    "@aztec/aztec.js": "^0.52.0",
    "vite": "^5.4.2"
  },
  "devDependencies": {
    "@aztec/accounts": "^0.52.0",
    "@aztec/builder": "^0.52.0",
    "@aztec/noir-contracts.js": "^0.52.0",
    "@babel/preset-typescript": "^7.24.7",
    "@types/node": "^22.5.1",
    "typescript": "^5.5.4",
    "vitest": "^2.0.5"
  }
}

As you can see, I am using the Vitest testing framework, but feel free to use whichever you prefer.

tsconfig.json

{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["esnext", "dom", "es2017.object"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "skipLibCheck": true
  },
  "include": ["tests", "src", "src/**/*.json", "src/artifacts/*"]
}
  1. Go to root directory of the project (you already have to be there if you follow the steps and run yarn install to install the dependencies.
  2. Run yarn compile to compile the Noir smart contract program.
    • Check the script commands of the package.json for reference.
  3. Run yarn codegen to generate the ABI artifacts for communicating with the contract.
  4. Run your sandbox with aztec start --sandbox
    • Here is a guide for installing and running the sandbox
  5. Run yarn test

[!IMPORTANT] To reproduce the error: Orphaned trait implementation issue, paste this serialization code below the global constants:

    impl Serialize<32> for [u8; 32] {
       fn serialize(self) -> [Field; 32] {
           let mut output = [0; 32];
           for i in 0..THIRTY_TWO {
                output[i] = self[i] as Field;
           }
         output
       }
   }
rahul-kothari commented 1 month ago

@Thunkar

Thunkar commented 4 weeks ago

I'm gonna check whether it's sensible to implement Serialize<N> for every primitive type in protocol_circuits, since it has recently been removed for[Field; N] while implementing public_dispatch (CC @fcarreiro).

In the meantime @NikolayKostadinov21 I think you can reimplement Serialize in your crate (just make sure the function it declares has the same name!) to get around the orphan rule. That way you can use arrays as arguments for your aztec functions.

fcarreiro commented 4 weeks ago

@Thunkar shouldn't [u8; 32] be covered by

Or is this a problem with the generics because THIRTY_TWO is not 32?

Thunkar commented 3 weeks ago

Oh, I forgot this got generalized, thanks @fcarreiro!

@NikolayKostadinov21 I think updating to the latest release (v0.56) should take care of this