near / nearcore

Reference client for NEAR Protocol
https://near.org
GNU General Public License v3.0
2.31k stars 618 forks source link

alt_bn128_g1_multiexp range end index 65 out of range #7520

Open veigajoao opened 2 years ago

veigajoao commented 2 years ago

I'm currently developing a zero knowledge application on NEAR, which requires the use of some near-sys features such as alt_bn128_g1_multiexp.

Per the decumentation in nearcore, alt_bn128_g1_multiexp should be called with the following parameters:

 /// # Arguments
///
/// * `value` - sequence of (g1:G1, fr:Fr), where
///    G1 is point (x:Fq, y:Fq) on alt_bn128,
///   alt_bn128 is Y^2 = X^3 + 3 curve over Fq.
///
///   `value` is encoded as packed, little-endian
///   `[((u256, u256), u256)]` slice.
///
pub fn alt_bn128_g1_multiexp(
        &mut self,
        value_len: u64,
        value_ptr: u64,
        register_id: u64,
    ) -> Result<()>

Basically the input should be a total of 96 bytes long, comprising 3 different little endian 32 bytes numbers.

The listed errors are:

/// # Errors
///
/// If `value_len + value_ptr` points outside the memory or the registers
/// use more memory than the limit, the function returns
/// `MemoryAccessViolation`.
///
/// If point coordinates are not on curve, point is not in the subgroup,
/// scalar is not in the field or  `value.len()%96!=0`, the function returns
/// `AltBn128InvalidInput`.

However, in my implementation, I am receiving a completely different error. I am implementing a method on a G1Point struct to multiplicate it by a scalar:

#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize, Clone)]
#[serde(crate = "near_sdk::serde")]
pub struct G1Point {
    pub x: U256,
    pub y: U256,
}

pub fn scalar_mul(&self, s: U256) -> G1Point {
        let mut input_vec: Vec<u8> = vec![];
        self.x.to_le_bytes().map(|b| input_vec.push(b));
        self.y.to_le_bytes().map(|b| input_vec.push(b));
        s.to_le_bytes().map(|b| input_vec.push(b));
        let input_value: &[u8] = &input_vec;

        unsafe {
            alt_bn128_g1_multiexp(
                input_value.len() as _,
                input_value.as_ptr() as _,
                ATOMIC_OP_REGISTER,
            );
            read_register_g1(ATOMIC_OP_REGISTER)
        };
    }

However, I am getting the following error precisely at the alt_bn128_g1_multiexp function call:

ExecutionError("Smart contract panicked: panicked at 'range end index 65 out of range for slice of length 64', library/core/src/slice/index.rs:73:5")'

I have already verified the slice input being passed which is correct, it is exactly 96 bytes long and correctly serializes the numbers.

Since it has not been possible to compile unit tests for the code given that some libs only compile to wasm I am stuck running simulation tests on workspaces-rs and cannot get further down on the contract's stack trace.

Does anyone have an idea of what is causing this error and possible fixes/workarrounds?

Thanks in advance guys

austinabell commented 2 years ago

transferring the issue to nearcore. We are just exporting the host function and haven't built a higher-level API for it or tested it yet. I'll track this as it should influence how we build out API

matklad commented 2 years ago

@veigajoao is there perhaps some repository with reproduction? I'd like to poke at it. I think it's read_register_g1 which might be at fault here -- slice lenght 64 is what the result here is, so it looks like something tries to read 65 bytes out of that.

Also, sorry for the absence of a stacktrace, I'll see if we can fix that...

matklad commented 2 years ago

For the curious: the Rust bug that the panic message points to core, rather than user's code, is fixed here: https://github.com/rust-lang/rust/pull/100759