Skip to content

Commit

Permalink
chore: Switch to ffi_interface ipa-multipoint (#139)
Browse files Browse the repository at this point in the history
Signed-off-by: Kevaundray Wedderburn <[email protected]>
  • Loading branch information
kevaundray authored Jan 29, 2024
1 parent abd453a commit 9b2e7bb
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 134 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
export PATH=$GOROOT/bin:$PATH
# rust dependencies
export CARGO_HOME="$HOME/.cargo"
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.68.2
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.75.0
- name: Checkout Repo
uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:
brew install [email protected] || true
# rust dependencies
export CARGO_HOME="$HOME/.cargo"
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.68.2
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.75.0
# install both x86 and arm64 toolchains
export PATH="$HOME/.cargo/bin:$PATH"
rustup target add x86_64-apple-darwin
Expand Down Expand Up @@ -189,7 +189,7 @@ jobs:
brew install [email protected] || true
# rust dependencies
export CARGO_HOME="$HOME/.cargo"
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.68.2
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.75.0
# install both x86 and arm64 toolchains
export PATH="$HOME/.cargo/bin:$PATH"
rustup target add x86_64-apple-darwin
Expand Down
13 changes: 3 additions & 10 deletions ipa-multipoint/ipa_multipoint_jni/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,14 @@ repository = "https://github.com/hyperledger/besu-native"
edition = "2018"

[dependencies]
verkle-spec = { git = "https://github.com/crate-crypto/rust-verkle", rev = "67d47d9e78f873da7c9e72687158468041059b5d" }
verkle-trie = { git = "https://github.com/crate-crypto/rust-verkle", rev = "67d47d9e78f873da7c9e72687158468041059b5d" }
ipa-multipoint = { git = "https://github.com/crate-crypto/ipa_multipoint", branch = "banderwagon_migration" }
banderwagon = { git = "https://github.com/crate-crypto/banderwagon" }
bandersnatch = "0.1.1"
ark-ff = { version = "^0.3.0", default-features = false }
ark-ec = { version = "^0.3.0", default-features = false }
ark-serialize = { version = "^0.3.0", default-features = false }
ark-std = { version = "^0.3.0", default-features = false }
ffi_interface = { git = "https://github.com/crate-crypto/rust-verkle", rev = "13dd7e9b4cb4491230fb0bda1759ef9c62938f28" }
ipa-multipoint = { git = "https://github.com/crate-crypto/rust-verkle", rev = "13dd7e9b4cb4491230fb0bda1759ef9c62938f28" }
jni = { version = "0.19.0", features = [
"invocation",
] } # We use invocation in tests.
hex = "0.4.3"
num-bigint = "0.4.4"

once_cell = "1.19.0"

[lib]
name = "ipa_multipoint_jni"
Expand Down
191 changes: 70 additions & 121 deletions ipa-multipoint/ipa_multipoint_jni/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
use ark_ff::PrimeField;
use banderwagon::{Fr, multi_scalar_mul};
use ipa_multipoint::crs::CRS;
use verkle_spec::*;
// use crate::{vergroup_to_field};
use ark_serialize::CanonicalSerialize;
use verkle_trie::*;

// use group_to_field;
use std::convert::TryInto;

use jni::JNIEnv;
use ipa_multipoint::committer::DefaultCommitter;
use ipa_multipoint::crs::CRS;
use jni::objects::JClass;
use jni::sys::jbyteArray;
use jni::JNIEnv;
use once_cell::sync::Lazy;

pub struct Config {
committer: DefaultCommitter,
}

// Copied from rust-verkle: https://github.com/crate-crypto/rust-verkle/blob/581200474327f5d12629ac2e1691eff91f944cec/verkle-trie/src/constants.rs#L12
const PEDERSEN_SEED: &'static [u8] = b"eth_verkle_oct_2021";
// TODO: Use a pointer here instead. This is only being used so that the interface does not get changed.
// TODO: and bindings do not need to be modified.
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
let crs = CRS::default();
let committer = DefaultCommitter::new(&crs.G);
Config { committer }
});

/// Pedersen hash receives an address and a trie index and returns a hash calculated this way:
/// H(constant || address_low || address_high || trie_index_low || trie_index_high)
Expand All @@ -45,130 +49,75 @@ pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaM
_class: JClass,
input: jbyteArray,
) -> jbyteArray {

let input = env.convert_byte_array(input).unwrap();

let mut address32 = [0u8; 32];

address32.copy_from_slice(&input[0..32]);

let mut trie_index= [0u8; 32];

trie_index.copy_from_slice(&input[32..64]);
trie_index.reverse(); // reverse for little endian per specs

let base_hash = hash_addr_int(&address32, &trie_index);

let result = base_hash.as_fixed_bytes();
let output = env.byte_array_from_slice(result).unwrap();
output
}

// Helper function to hash an address and an integer taken from rust-verkle/verkle-specs.
pub(crate) fn hash_addr_int(addr: &[u8; 32], integer: &[u8; 32]) -> H256 {

let address_bytes = addr;

let integer_bytes = integer;
let mut hash_input = [0u8; 64];
let (first_half, second_half) = hash_input.split_at_mut(32);

// Copy address and index into slice, then hash it
first_half.copy_from_slice(address_bytes);
second_half.copy_from_slice(integer_bytes);
let committer = &CONFIG.committer;

let mut input: [u8; 64] = match input.try_into() {
Ok(input) => input,
Err(_) => {
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid input length. Should be 64-bytes.",
)
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}
};

// The tree_index is interpreted as a little endian integer
// But its given in big endian format.
// The tree_index is the last 32 bytes of the input,
// so we use this method to reverse its endian
fn reverse_last_32_bytes(arr: &mut [u8; 64]) {
let last_32 = &mut arr[32..];
last_32.reverse();
}
reverse_last_32_bytes(&mut input);

hash64(hash_input)
let hash = ffi_interface::get_tree_key_hash(committer, input);
env.byte_array_from_slice(&hash).unwrap()
}

/// Commit receives a list of 32 byte scalars and returns a 32 byte scalar
/// Scalar is actually the map_to_field(commitment) because we want to reuse the commitment in parent node.
/// This is ported from rust-verkle.
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commit(env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray)
-> jbyteArray {
// Input should be a multiple of 32-be-bytes.
let inp = env.convert_byte_array(input).expect("Cannot convert jbyteArray to rust array");
let len = inp.len();
if len % 32 != 0 {
env.throw_new("java/lang/IllegalArgumentException", "Invalid input length. Should be a multiple of 32-bytes.")
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}
let n_scalars = len / 32;
if n_scalars > 256 {
env.throw_new("java/lang/IllegalArgumentException", "Invalid input length. Should be at most 256 elements of 32-bytes.")
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}

// Each 32-be-bytes are interpreted as field elements.
let mut scalars: Vec<Fr> = Vec::with_capacity(n_scalars);
for b in inp.chunks(32) {
scalars.push(Fr::from_be_bytes_mod_order(b));
}

// Committing all values at once.
let bases = CRS::new(n_scalars, PEDERSEN_SEED);
let commit = multi_scalar_mul(&bases.G, &scalars);

// Serializing via x/y in projective coordinates, to int and to scalars.
let scalar = group_to_field(&commit);
let mut scalar_bytes = [0u8; 32];
scalar.serialize(&mut scalar_bytes[..]).expect("could not serialise Fr into a 32 byte array");
scalar_bytes.reverse();

return env.byte_array_from_slice(&scalar_bytes).expect("Couldn't convert to byte array");
}
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commit(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
) -> jbyteArray {
let input = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

let committer = &CONFIG.committer;

let commitment = ffi_interface::commit_to_scalars(committer, &input).unwrap();
let hash = ffi_interface::hash_commitment(commitment);

env.byte_array_from_slice(&hash)
.expect("Couldn't convert to byte array")
}

/// Commit_root receives a list of 32 byte scalars and returns a 32 byte commitment.to_bytes()
/// This is ported from rust-verkle.
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commitRoot(env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray)
-> jbyteArray {
// Input should be a multiple of 32-be-bytes.
let inp = env.convert_byte_array(input).expect("Cannot convert jbyteArray to rust array");
let len = inp.len();
if len % 32 != 0 {
env.throw_new("java/lang/IllegalArgumentException", "Invalid input length. Should be a multiple of 32-bytes.")
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}
let n_scalars = len / 32;
if n_scalars > 256 {
env.throw_new("java/lang/IllegalArgumentException", "Invalid input length. Should be at most 256 elements of 32-bytes.")
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}

// Each 32-be-bytes are interpreted as field elements.
let mut scalars: Vec<Fr> = Vec::with_capacity(n_scalars);
for b in inp.chunks(32) {
scalars.push(Fr::from_be_bytes_mod_order(b));
}

// Committing all values at once.
let bases = CRS::new(n_scalars, PEDERSEN_SEED);
let commit = multi_scalar_mul(&bases.G, &scalars);

// Serializing using first affine coordinate
let commit_bytes = commit.to_bytes();
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commitRoot(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
) -> jbyteArray {
let input = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

return env.byte_array_from_slice(&commit_bytes).expect("Couldn't convert to byte array");
}
let committer = &CONFIG.committer;

let commitment = ffi_interface::commit_to_scalars(committer, &input).unwrap();
let hash = ffi_interface::deprecated_serialize_commitment(commitment);

// Note: This is a 2 to 1 map, but the two preimages are identified to be the same
// TODO: Create a document showing that this poses no problems
pub(crate)fn group_to_field(point: &Element) -> Fr {
let base_field = point.map_to_field();
let mut bytes = [0u8; 32];
base_field
.serialize(&mut bytes[..])
.expect("could not serialise point into a 32 byte array");
Fr::from_le_bytes_mod_order(&bytes)
}
env.byte_array_from_slice(&hash)
.expect("Couldn't convert to byte array")
}

0 comments on commit 9b2e7bb

Please sign in to comment.