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

[WIP] Constantine bindings #184

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Conversation

NickSneo
Copy link

No description provided.

Signed-off-by: Nischal Sharma <nischal@web3labs.com>
@NickSneo NickSneo marked this pull request as draft June 27, 2024 04:19
@garyschulte
Copy link
Contributor

👀

just a tracking comment

Copy link

@mratsim mratsim left a comment

Choose a reason for hiding this comment

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

There shouldn't be a need for indirection and allocation, the function can be called and write directly in the relevant buffer, see C, Rust and Go code as referemce.

Also is optimizing for ARM desirable?

On non-x86 Constantine compiles through uint128 as it's the most portable and best perf across all compilers. Both GCC and Clang support an add with overflow called __builtin__addcll but the perf is abysmal on GCC: https://gcc.godbolt.org/z/jdecvffaP see overall issue mratsim/constantine#357

I do have a plan for optimizing for ARM targets (see mratsim/constantine#200 ) but so far no time to tackle it.

cc @Vindaar as well as he is currently maintaining the external languages dependencies.

var inputSeq: seq[byte] = cast[seq[byte]](inputs[0 ..< inputsLen])
let status = ethereum_evm_precompiles.eth_evm_bn254_g1add(result, inputSeq)
copyMem(r, addr result[0], rLen)
return status
Copy link

Choose a reason for hiding this comment

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

There shouldn't be a need for this indirection and allocation.

If from Nim (ptr, length) pair can be zero-copied transformed to openarrays with https://nim-lang.org/docs/system.html#toOpenArray%2Cptr.UncheckedArray%5BT%5D%2Cint%2Cint

If from C, Go, Rust, you can directly call eth_evm_bn254_g1add
because it's compiled to (ptr, len):

see https://github.com/mratsim/constantine/blob/8db2639/include/constantine/protocols/ethereum_evm_precompiles.h#L98-L128

/**
  *  Elliptic Curve addition on BN254_Snarks
  *  (also called alt_bn128 in Ethereum specs
  *   and bn256 in Ethereum tests)
  *
  *  Name: ECADD
  *
  *  Inputs:
  *  - A G1 point P with coordinates (Px, Py)
  *  - A G1 point Q with coordinates (Qx, Qy)
  *
  *  Each coordinate is a 32-byte bigEndian integer
  *  They are serialized concatenated in a byte array [Px, Py, Qx, Qy]
  *  If the length is less than 128 bytes, input is virtually padded with zeros.
  *  If the length is greater than 128 bytes, input is truncated to 128 bytes.
  *
  *  Output
  *  - Output buffer MUST be of length 64 bytes
  *  - A G1 point R = P+Q with coordinates (Rx, Ry)
  *  - Status code:
  *    cttEVM_Success
  *    cttEVM_InvalidOutputSize
  *    cttEVM_IntLargerThanModulus
  *    cttEVM_PointNotOnCurve
  *
  *  Spec https://eips.ethereum.org/EIPS/eip-196
  */
ctt_evm_status ctt_eth_evm_bn254_g1add(
    byte* r, ptrdiff_t r_len,
    const byte* inputs, ptrdiff_t inputs_len
) __attribute__((warn_unused_result));

See how to call SHA256 from C (same interface):
https://github.com/mratsim/constantine/blob/8db2639/examples-c/ethereum_evm_precompiles.c#L47-L51

    byte result[32] = {0};
    const char txt[] = "Foo, Bar and Baz are all friends.";

    evm_status = ctt_eth_evm_sha256(result, 32, (const byte*)txt, sizeof(txt));

Rust slices to Nim with zero-copy or alloc:
https://github.com/mratsim/constantine/blob/8db2639/constantine-rust/constantine-ethereum-evm-precompiles/src/lib.rs#L53-L69

pub fn evm_bn254_g1add(
    result: &mut [u8],
    inputs: &[u8]
) -> Result<bool, ctt_evm_status> {
    unsafe {
	let status = ctt_eth_evm_bn254_g1add(
            result.as_mut_ptr() as *mut byte,
            result.len() as isize,
            inputs.as_ptr() as *const byte,
            inputs.len() as isize,
	);
        match status {
            ctt_evm_status::cttEVM_Success => Ok(true),
            _ => Err(status)
        }
    }
}

Go slices to Nim with zero-copy or alloc:
https://github.com/mratsim/constantine/blob/8db2639/constantine-go/constantine.go#L705-L718

func EvmBn254G1Mul(result []byte, inputs []byte) (bool, error) {
	status := C.ctt_eth_evm_bn254_g1mul((*C.byte)(getAddr(result)),
		(C.ptrdiff_t)(len(result)),
		(*C.byte)(getAddr(inputs)),
		(C.ptrdiff_t)(len(inputs)),
	)
	if status != C.cttEVM_Success {
		err := errors.New(
			C.GoString(C.ctt_evm_status_to_string(status)),
		)
		return false, err
	}
	return true, nil
}

One thing is that in an excess zeal moment I set the type to ptrdiff_t but I'll change it to size_t.

In general Constantine was written to minimize allocations and for precompiles it's only needed for MSMs and KZG point precompiles.

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.

None yet

3 participants