-
Notifications
You must be signed in to change notification settings - Fork 42
chore: change rust kzg nif to direct elixir bindings #1484
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| alias ChainSpec | ||
| alias Kzg | ||
|
|
||
| # BLS12-381 Fr modulus | ||
| modulus = | ||
| 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 | ||
|
|
||
| rand_field_element = fn -> | ||
| int = | ||
| :crypto.strong_rand_bytes(64) | ||
| |> :binary.decode_unsigned(:big) | ||
| |> rem(modulus) | ||
|
|
||
| <<int::unsigned-size(256)>> | ||
| end | ||
|
|
||
| ok! = fn | ||
| {:ok, value} -> value | ||
| other -> raise "expected {:ok, _}, got #{inspect(other)}" | ||
| end | ||
|
|
||
| fe_per_blob = ChainSpec.get("FIELD_ELEMENTS_PER_BLOB") | ||
|
|
||
| rand_blob = fn -> | ||
| for _ <- 1..fe_per_blob, into: <<>> do | ||
| rand_field_element.() | ||
| end | ||
| end | ||
|
|
||
| z = rand_field_element.() | ||
| blob = rand_blob.() | ||
|
|
||
| commitment = blob |> Kzg.blob_to_kzg_commitment() |> ok!.() | ||
| proof = blob |> Kzg.compute_blob_kzg_proof(commitment) |> ok!.() | ||
|
|
||
| # Only did 4 blobs, we don't have all day... | ||
| batch = | ||
| Enum.map(1..4, fn _ -> | ||
| b = rand_blob.() | ||
| c = b |> Kzg.blob_to_kzg_commitment() |> ok!.() | ||
| p = b |> Kzg.compute_blob_kzg_proof(c) |> ok!.() | ||
| {b, c, p} | ||
| end) | ||
|
|
||
| blobs = Enum.map(batch, fn {b, _, _} -> b end) | ||
| commitments = Enum.map(batch, fn {_, c, _} -> c end) | ||
| proofs = Enum.map(batch, fn {_, _, p} -> p end) | ||
|
|
||
| Benchee.run( | ||
| %{ | ||
| "blob_to_kzg_commitment" => fn -> Kzg.blob_to_kzg_commitment(blob) end, | ||
| "compute_blob_kzg_proof" => fn -> Kzg.compute_blob_kzg_proof(blob, commitment) end, | ||
| "verify_blob_kzg_proof" => fn -> Kzg.verify_blob_kzg_proof(blob, commitment, proof) end, | ||
| "verify_blob_kzg_proof_batch (4)" => fn -> | ||
| Kzg.verify_blob_kzg_proof_batch(blobs, commitments, proofs) | ||
| end, | ||
| "compute_kzg_proof (z)" => fn -> Kzg.compute_kzg_proof(blob, z) end | ||
| }, | ||
| warmup: 2, | ||
| time: 5 | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,7 +49,6 @@ config :lambda_ethereum_consensus, :logger, [ | |
| # Avoid compiling Rustler NIFs when `RUSTLER_SKIP_COMPILE` is set | ||
| if System.get_env("RUSTLER_SKIP_COMPILE") do | ||
| config :lambda_ethereum_consensus, Bls, skip_compilation?: true | ||
| config :lambda_ethereum_consensus, Kzg, skip_compilation?: true | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if there should be another flag to skip compiling the nif? |
||
| config :lambda_ethereum_consensus, Snappy, skip_compilation?: true | ||
| config :lambda_ethereum_consensus, Ssz, skip_compilation?: true | ||
| end | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,53 +2,86 @@ defmodule Kzg do | |
| @moduledoc """ | ||
| KZG functions | ||
| """ | ||
| use Rustler, otp_app: :lambda_ethereum_consensus, crate: "kzg_nif" | ||
|
|
||
| alias KZG, as: CKZG | ||
|
|
||
| require Logger | ||
|
|
||
| @bytes_per_blob 4096 * 32 | ||
| @bytes_per_commitment 48 | ||
| @bytes_per_proof 48 | ||
| @trusted_setup_path Path.expand("config/kzg/official_trusted_setup.txt", File.cwd!()) | ||
|
|
||
| @type commitment :: <<_::384>> | ||
| @type proof :: <<_::768>> | ||
| @type proof :: <<_::384>> | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am pretty sure proof size is 384 and not 768, but feel free to revert |
||
|
|
||
| @spec blob_to_kzg_commitment(Types.blob()) :: {:ok, commitment()} | {:error, binary()} | ||
| def blob_to_kzg_commitment(_blob) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| @spec blob_to_kzg_commitment(Types.blob()) :: {:ok, commitment()} | {:error, atom()} | ||
| def blob_to_kzg_commitment(blob) do | ||
| with :ok <- ensure_blob_size(blob) do | ||
| with_settings(fn s -> CKZG.blob_to_kzg_commitment(blob, s) end) | ||
| end | ||
| end | ||
|
|
||
| @spec compute_kzg_proof(Types.blob(), Types.bytes32()) :: | ||
| {:ok, {proof(), Types.bytes32()}} | {:error, binary()} | ||
| def compute_kzg_proof(_blob, _z) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| {:ok, {proof(), Types.bytes32()}} | {:error, atom()} | ||
| def compute_kzg_proof(blob, z) do | ||
| with :ok <- ensure_blob_size(blob), | ||
| :ok <- ensure_bytes32(z) do | ||
| with_settings(fn s -> CKZG.compute_kzg_proof(blob, z, s) end) | ||
| end | ||
| end | ||
|
|
||
| @spec verify_kzg_proof( | ||
| commitment(), | ||
| Types.bytes32(), | ||
| Types.bytes32(), | ||
| proof() | ||
| ) :: | ||
| {:ok, boolean} | {:error, binary()} | ||
| def verify_kzg_proof(_kzg_commitment, _z, _y, _kzg_proof) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| @spec verify_kzg_proof(commitment(), Types.bytes32(), Types.bytes32(), proof()) :: | ||
| {:ok, boolean} | {:error, atom()} | ||
| def verify_kzg_proof(commitment, z, y, proof) do | ||
| with :ok <- ensure_commitment_size(commitment), | ||
| :ok <- ensure_bytes32(z), | ||
| :ok <- ensure_bytes32(y), | ||
| :ok <- ensure_proof_size(proof) do | ||
| with_settings(fn s -> CKZG.verify_kzg_proof(commitment, z, y, proof, s) end) | ||
| end | ||
| end | ||
|
|
||
| @spec compute_blob_kzg_proof(Types.blob(), commitment()) :: | ||
| {:ok, proof()} | {:error, binary()} | ||
| def compute_blob_kzg_proof(_blob, _kzg_commitment) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| {:ok, proof()} | {:error, atom()} | ||
| def compute_blob_kzg_proof(blob, commitment) do | ||
| with :ok <- ensure_blob_size(blob), | ||
| :ok <- ensure_commitment_size(commitment) do | ||
| with_settings(fn s -> CKZG.compute_blob_kzg_proof(blob, commitment, s) end) | ||
| end | ||
| end | ||
|
|
||
| @spec verify_blob_kzg_proof(Types.blob(), commitment(), proof()) :: | ||
| {:ok, boolean} | {:error, binary()} | ||
| def verify_blob_kzg_proof(_blob, _kzg_commitment, _kzg_proof) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| {:ok, boolean} | {:error, atom()} | ||
| def verify_blob_kzg_proof(blob, commitment, proof) do | ||
| with :ok <- ensure_blob_size(blob), | ||
| :ok <- ensure_commitment_size(commitment), | ||
| :ok <- ensure_proof_size(proof) do | ||
| with_settings(fn s -> | ||
| CKZG.verify_blob_kzg_proof(blob, commitment, proof, s) | ||
| end) | ||
| end | ||
| end | ||
|
|
||
| @spec verify_blob_kzg_proof_batch( | ||
| list(Types.blob()), | ||
| list(commitment()), | ||
| list(proof()) | ||
| [Types.blob()], | ||
| [commitment()], | ||
| [proof()] | ||
| ) :: | ||
| {:ok, boolean} | {:error, binary()} | ||
| def verify_blob_kzg_proof_batch(_blobs, _kzg_commitments, _kzg_proofs) do | ||
| :erlang.nif_error(:nif_not_loaded) | ||
| {:ok, boolean} | {:error, atom()} | ||
| def verify_blob_kzg_proof_batch(blobs, kzg_commitments, kzg_proofs) do | ||
| with :ok <- ensure_all_blob_sizes(blobs), | ||
| :ok <- ensure_all_commitment_sizes(kzg_commitments), | ||
| :ok <- ensure_all_proof_sizes(kzg_proofs), | ||
| :ok <- ensure_batch_lengths_match(blobs, kzg_commitments, kzg_proofs) do | ||
| blobs_bin = IO.iodata_to_binary(blobs) | ||
| commitments_bin = IO.iodata_to_binary(kzg_commitments) | ||
| proofs_bin = IO.iodata_to_binary(kzg_proofs) | ||
|
|
||
| with_settings(fn settings -> | ||
| CKZG.verify_blob_kzg_proof_batch(blobs_bin, commitments_bin, proofs_bin, settings) | ||
| end) | ||
| end | ||
| end | ||
|
|
||
| ################ | ||
|
|
@@ -66,4 +99,64 @@ defmodule Kzg do | |
| {:error, _} -> false | ||
| end | ||
| end | ||
|
|
||
| defp settings() do | ||
| :persistent_term.get({__MODULE__, :settings}, fn -> load_settings() end) | ||
| end | ||
|
|
||
| defp load_settings() do | ||
| {:ok, settings} = CKZG.load_trusted_setup(@trusted_setup_path, 0) | ||
| :persistent_term.put({__MODULE__, :settings}, settings) | ||
| settings | ||
| end | ||
|
|
||
| defp reset_settings() do | ||
| Logger.warning("[KZG] Resetting settings resource") | ||
| :persistent_term.erase({__MODULE__, :settings}) | ||
| load_settings() | ||
| end | ||
|
|
||
| defp with_settings(fun) do | ||
| case fun.(settings()) do | ||
| {:error, :failed_get_settings_resource} -> fun.(reset_settings()) | ||
| other -> other | ||
| end | ||
| end | ||
|
|
||
| defp ensure_blob_size(<<_::binary-size(@bytes_per_blob)>>), do: :ok | ||
| defp ensure_blob_size(_), do: {:error, :invalid_blob_length} | ||
|
|
||
| defp ensure_bytes32(<<_::binary-size(32)>>), do: :ok | ||
| defp ensure_bytes32(_), do: {:error, :invalid_field_element_length} | ||
|
|
||
| defp ensure_commitment_size(<<_::binary-size(@bytes_per_commitment)>>), do: :ok | ||
| defp ensure_commitment_size(_), do: {:error, :invalid_commitment_length} | ||
|
|
||
| defp ensure_proof_size(<<_::binary-size(@bytes_per_proof)>>), do: :ok | ||
| defp ensure_proof_size(_), do: {:error, :invalid_proof_length} | ||
|
|
||
| defp ensure_all_blob_sizes(list) when is_list(list) do | ||
| if Enum.all?(list, &match?(<<_::binary-size(@bytes_per_blob)>>, &1)), | ||
| do: :ok, | ||
| else: {:error, :invalid_blob_length} | ||
| end | ||
|
|
||
| defp ensure_all_commitment_sizes(list) when is_list(list) do | ||
| if Enum.all?(list, &match?(<<_::binary-size(@bytes_per_commitment)>>, &1)), | ||
| do: :ok, | ||
| else: {:error, :invalid_commitment_length} | ||
| end | ||
|
|
||
| defp ensure_all_proof_sizes(list) when is_list(list) do | ||
| if Enum.all?(list, &match?(<<_::binary-size(@bytes_per_proof)>>, &1)), | ||
| do: :ok, | ||
| else: {:error, :invalid_proof_length} | ||
| end | ||
|
|
||
| defp ensure_batch_lengths_match(blobs, commitments, proofs) do | ||
| case {length(blobs), length(commitments), length(proofs)} do | ||
| {same, same, same} -> :ok | ||
| _ -> {:error, :invalid_batch_length} | ||
| end | ||
| end | ||
| end | ||
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure i should add precompiling the nif here instead?