diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a7eb71a..83f9e0e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,8 +1,30 @@ FROM python + +# Configure Poetry ENV PYTHONPATH=. +ENV POETRY_VERSION=1.3.2 +ENV POETRY_HOME=/opt/poetry +ENV POETRY_VENV=/opt/poetry-venv +ENV POETRY_CACHE_DIR=/opt/.cache + +# Install poetry separated from system interpreter +RUN python3 -m venv $POETRY_VENV && $POETRY_VENV/bin/pip install -U pip setuptools && $POETRY_VENV/bin/pip install poetry==${POETRY_VERSION} + +# Add `poetry` to PATH +ENV PATH="${PATH}:${POETRY_VENV}/bin" # install deps -RUN apt update -y && apt install curl zsh git -y -# poetry, ohMyZsh -RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - +RUN apt update -y && apt upgrade +RUN apt install -y curl zsh git build-essential gnupg && \ + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + +# Instalar o Yarn +RUN apt-get update && apt-get install -y yarn + +# Intall Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly +ENV PATH="/root/.cargo/bin:${PATH}" + +# ohMyZsh RUN sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 327cf00..09f7ff5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,16 +1,12 @@ { - "name": "Existing Dockerfile", - "context": "..", - "dockerFile": "./Dockerfile", - "postCreateCommand": "poetry install && pip install --upgrade pip", - "settings": { - "python.pythonPath": "." - }, - "extensions": [ - "bungcip.better-toml", - "mhutchie.git-graph", - "Tyriar.sort-lines", - "ms-python.python", - "eamodio.gitlens" - ] -} \ No newline at end of file + "name": "Existing Dockerfile", + "context": "..", + "dockerFile": "./Dockerfile", + "extensions": [ + "bungcip.better-toml", + "mhutchie.git-graph", + "Tyriar.sort-lines", + "ms-python.python", + "eamodio.gitlens" + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f38257..a66012d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,24 +23,35 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip pip install pytest pip install -e . if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Set up Node.js uses: actions/setup-node@v2 with: node-version: "18" + + - name: Set up Rust Minimal Nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + - name: Install Node.js dependencies run: | cd test/merkletreejs yarn install + - name: Test with pytest run: | - pytest -m "not benchmark" -vv \ No newline at end of file + pytest -m "not benchmark" -vv diff --git a/.gitignore b/.gitignore index 4581abf..ff34216 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules +target + .hypothesis .vscode *cache* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 338d44e..f18c80b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: hooks: - id: pytest name: Unit Tests (Pytest) - entry: pytest -m "not benchmark and not merkletreejs" + entry: pytest -m "not benchmark" language: system pass_filenames: false always_run: true diff --git a/merkly/accelerator/Cargo.toml b/merkly/accelerator/Cargo.toml new file mode 100644 index 0000000..677d461 --- /dev/null +++ b/merkly/accelerator/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "accelerator" +version = "0.1.0" +edition = "2021" + +[dependencies] +tiny-keccak = { version = "2.0.2", features = ["keccak"] } + + +[lib] +crate-type = ["cdylib"] + +[profile.release] +strip = true diff --git a/merkly/accelerator/build.sh b/merkly/accelerator/build.sh new file mode 100755 index 0000000..2f0eea5 --- /dev/null +++ b/merkly/accelerator/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cargo b -r --out-dir . -Z unstable-options \ No newline at end of file diff --git a/merkly/accelerator/libaccelerator.dylib b/merkly/accelerator/libaccelerator.dylib new file mode 100755 index 0000000..7e3ea1e Binary files /dev/null and b/merkly/accelerator/libaccelerator.dylib differ diff --git a/merkly/accelerator/libaccelerator.so b/merkly/accelerator/libaccelerator.so new file mode 100755 index 0000000..dba8041 Binary files /dev/null and b/merkly/accelerator/libaccelerator.so differ diff --git a/merkly/accelerator/mtreers.py b/merkly/accelerator/mtreers.py new file mode 100644 index 0000000..dc83618 --- /dev/null +++ b/merkly/accelerator/mtreers.py @@ -0,0 +1,93 @@ +from ctypes import CDLL, POINTER, Structure, c_size_t, c_ubyte +from merkly.node import Node +from typing import List + + +class MerkleProof(Structure): + _fields_ = [("ptr", POINTER(POINTER(c_ubyte))), ("len", c_size_t)] + + +class MTreers: + def __init__(self) -> None: + self.lib = CDLL("./merkly/accelerator/libaccelerator.dylib") + + self.lib.make_root.argtypes = [POINTER(POINTER(c_ubyte)), c_size_t] + self.lib.make_root.restype = POINTER(c_ubyte) + + self.lib.free_root.argtypes = [POINTER(c_ubyte)] + self.lib.free_root.restype = None + + self.lib.make_proof.argtypes = [ + POINTER(POINTER(c_ubyte)), + c_size_t, + POINTER(c_ubyte), + ] + self.lib.make_proof.restype = MerkleProof + + self.lib.free_proof.argtypes = [POINTER(POINTER(c_ubyte))] + self.lib.free_proof.restype = None + + # self.lib.verify.argtypes = [ + # POINTER(POINTER(c_ubyte)), + # c_size_t, + # POINTER(c_ubyte), + # ] + # self.lib.verify.restype = c_bool + + def make_root(self, leaves: List[bytes]) -> bytes: + len_leaves = len(leaves) + leaves_pointers = (POINTER(c_ubyte) * len_leaves)() + + for i, leaf in enumerate(leaves): + array_type = c_ubyte * 32 + leaves_pointers[i] = array_type(*leaf) + + root_ptr = self.lib.make_root(leaves_pointers, len_leaves) + root = bytes(root_ptr[:32]) + self.lib.free_root(root_ptr) + return root + + def make_proof(self, leaves: List[bytes], leaf: bytes) -> bytes: + leaf_pointer = (c_ubyte * 32)(*leaf) + len_leaves = len(leaves) + leaves_pointers = (POINTER(c_ubyte) * len_leaves)() + + for i, leaf in enumerate(leaves): + array_type = c_ubyte * 32 + leaves_pointers[i] = array_type(*leaf) + + result_struct: MerkleProof = self.lib.make_proof( + leaves_pointers, len_leaves, leaf_pointer + ) + + proof = [] + for i in range(result_struct.len): + node_ptr = result_struct.ptr[i] + node_data = bytes(node_ptr[:33]) + + flag = node_data[32] + data = node_data[:32] + + proof.append(Node(data=data, side=flag)) + + self.lib.free_proof(result_struct.ptr, result_struct.len) + return proof + + def verify(self, proof: List[Node], leaf: bytes) -> bytes: + proof_bytes = [] + + for node in proof: + flag = node.side.value + data = node.data + proof_bytes.append([*data, flag]) + + len_proof = len(proof_bytes) + proof_pointers = (POINTER(c_ubyte) * len_proof)() + + for i, node in enumerate(proof_bytes): + array_type = c_ubyte * 33 + proof_pointers[i] = array_type(*node) + + leaf_pointer = (c_ubyte * 32)(*leaf) + + return self.lib.verify(proof_pointers, len_proof, leaf_pointer) diff --git a/merkly/accelerator/src/lib.rs b/merkly/accelerator/src/lib.rs new file mode 100644 index 0000000..54fc739 --- /dev/null +++ b/merkly/accelerator/src/lib.rs @@ -0,0 +1,3 @@ +pub mod merkle_proof; +pub mod merkle_root; +pub mod utils; diff --git a/merkly/accelerator/src/merkle_proof.rs b/merkly/accelerator/src/merkle_proof.rs new file mode 100644 index 0000000..a8f1f50 --- /dev/null +++ b/merkly/accelerator/src/merkle_proof.rs @@ -0,0 +1,190 @@ +use crate::merkle_root::make_root; +use crate::utils::make_node; +use std::slice; + +#[repr(C)] +pub struct MerkleProof { + ptr: *mut *mut u8, + len: usize, +} + +/// Constructs a Merkle proof for the given leaf. +#[no_mangle] +pub unsafe extern "C" fn make_proof( + leaves_ptr: *const *const u8, + len_leaves: usize, + leaf_ptr: *const u8, +) -> MerkleProof { + let leaf = unsafe { slice::from_raw_parts(leaf_ptr, 32).to_vec() }; + let leaves = unsafe { slice::from_raw_parts(leaves_ptr, len_leaves) } + .iter() + .map(|leaf_ptr| unsafe { slice::from_raw_parts(*leaf_ptr, 32).to_vec() }) + .collect::>>(); + + let mut proof: Vec<[u8; 33]> = Vec::new(); + + let mut current_leaves = leaves.clone(); + while current_leaves.len() > 1 { + let index = match leaves.iter().position(|x| *x == leaf) { + Some(i) => i, + None => panic!("Leaf does not exist in the tree"), + }; + if current_leaves.len() == 2 { + if index == 1 { + // proof.push(Node(data = leaves[0], side = Side.LEFT)) + let left: [u8; 32] = current_leaves[0] + .as_slice() + .try_into() + .expect("Failed to convert LEFT to array"); + let node = make_node(left, 0); + proof.push(node); + break; + } else { + // proof.append(Node(data = leaves[1], side = Side.RIGHT)) + let right: [u8; 32] = current_leaves[1] + .as_slice() + .try_into() + .expect("Failed to convert RIGHT to array"); + let node = make_node(right, 1); + proof.push(node); + break; + } + } + + let half_size = current_leaves.len() / 2; + + // divide a lista em 2, left e right + let (left, right) = current_leaves.split_at(half_size); + + // se o index estiver em left + if index < half_size { + // faça a merkle root de right + let right_ptrs: Vec<*const u8> = right.iter().map(|vec| vec.as_ptr()).collect(); + let right_ptr: *const *const u8 = right_ptrs.as_ptr(); + + // TODO: make root must be a async (using other thread) + let right_root_ptr = make_root(right_ptr, right.len()); + let right_root_raw = slice::from_raw_parts(right_root_ptr, 32); + let right_root: [u8; 32] = right_root_raw + .try_into() + .expect("Failed to convert RIGHT to array"); + + // faça o node passando right e 1 (direita) + let node = make_node(right_root, 1); + + // adicione o node na lista de prova + proof.push(node); + + current_leaves = left.to_vec(); + } else { + // se o index estiver em right + // faça a merkle root de left + let left_ptrs: Vec<*const u8> = left.iter().map(|vec| vec.as_ptr()).collect(); + let left_ptr: *const *const u8 = left_ptrs.as_ptr(); + + // TODO: make root must be a async (using other thread) + let left_root_ptr = make_root(left_ptr, left.len()); + let left_root_raw = slice::from_raw_parts(left_root_ptr, 32); + let left_root: [u8; 32] = left_root_raw + .try_into() + .expect("Failed to convert LEFT to array"); + + // faça o node passando left e 0 (esquerda) + let node = make_node(left_root, 0); + + // adicione o node na lista de prova + proof.push(node); + + current_leaves = right.to_vec(); + } + } + + proof.reverse(); + let len = proof.len(); + let proof_pointers: Vec<*mut u8> = proof + .into_iter() + .map(|node| { + let boxed_node = Box::new(node); + Box::into_raw(boxed_node) as *mut u8 + }) + .collect(); + let boxed_proof = proof_pointers.into_boxed_slice(); + let ptr = Box::into_raw(boxed_proof) as *mut *mut u8; + MerkleProof { ptr, len } +} + +#[no_mangle] +pub unsafe extern "C" fn free_proof(ptr: *mut *mut u8, len: usize) { + let slice = slice::from_raw_parts_mut(ptr, len); + for &mut inner_ptr in slice.iter_mut() { + let _ = Box::from_raw(inner_ptr); + } + let _ = Box::from_raw(ptr); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn setup_leaves() -> Vec<[u8; 32]> { + vec![ + // a + [ + 58, 194, 37, 22, 141, 245, 66, 18, 162, 92, 28, 1, 253, 53, 190, 191, 234, 64, 143, + 218, 194, 227, 29, 221, 111, 128, 164, 187, 249, 165, 241, 203, + ], + // b + [ + 181, 85, 61, 227, 21, 224, 237, 245, 4, 217, 21, 10, 248, 45, 175, 165, 196, 102, + 127, 166, 24, 237, 10, 111, 25, 198, 155, 65, 22, 108, 85, 16, + ], + // c + [ + 11, 66, 182, 57, 60, 31, 83, 6, 15, 227, 221, 191, 205, 122, 173, 204, 168, 148, + 70, 90, 90, 67, 143, 105, 200, 125, 121, 11, 34, 153, 185, 178, + ], + // d + [ + 241, 145, 142, 133, 98, 35, 110, 177, 122, 220, 133, 2, 51, 47, 76, 156, 130, 188, + 20, 225, 155, 252, 10, 161, 10, 182, 116, 255, 117, 179, 210, 243, + ], + ] + } + + fn setup_proof() -> Vec<[u8; 33]> { + vec![ + [ + 181, 85, 61, 227, 21, 224, 237, 245, 4, 217, 21, 10, 248, 45, 175, 165, 196, 102, + 127, 166, 24, 237, 10, 111, 25, 198, 155, 65, 22, 108, 85, 16, 1, + ], + [ + 210, 83, 165, 45, 76, 176, 13, 226, 137, 94, 133, 242, 82, 158, 41, 118, 230, 170, + 170, 92, 24, 16, 107, 104, 171, 102, 129, 62, 20, 65, 86, 105, 1, + ], + ] + } + #[test] + fn test_make_root() { + let leaves = setup_leaves(); + let proof = setup_proof(); + let leaves_ptrs: Vec<*const u8> = leaves.iter().map(|leaf| leaf.as_ptr()).collect(); + let leaf = leaves[0]; + let leaf_ptr = leaf.as_ptr(); + + let result = unsafe { make_proof(leaves_ptrs.as_ptr(), leaves.len(), leaf_ptr) }; + let result_slice = unsafe { slice::from_raw_parts(result.ptr, result.len) } + .iter() + .map(|leaf_ptr| unsafe { slice::from_raw_parts(*leaf_ptr, 33).to_vec() }) + .collect::>>(); + + assert_eq!(result_slice, proof); + + unsafe { free_proof(result.ptr, result.len) } + let result_dangling_ptr = unsafe { slice::from_raw_parts(result.ptr, result.len) } + .iter() + .map(|leaf_ptr| unsafe { slice::from_raw_parts(*leaf_ptr, 33).to_vec() }) + .collect::>>(); + let expected: Vec> = vec![vec![0; 33]; 2]; + assert_eq!(result_dangling_ptr, expected); + } +} diff --git a/merkly/accelerator/src/merkle_root.rs b/merkly/accelerator/src/merkle_root.rs new file mode 100644 index 0000000..fc2d454 --- /dev/null +++ b/merkly/accelerator/src/merkle_root.rs @@ -0,0 +1,92 @@ +use crate::utils::hash_function; +use std::slice; + +/// # Safety +/// +/// FFI to Python. +#[no_mangle] +pub unsafe extern "C" fn make_root(leaves_ptr: *const *const u8, len_leaves: usize) -> *mut u8 { + let mut leaves = unsafe { slice::from_raw_parts(leaves_ptr, len_leaves) } + .iter() + .map(|leaf_ptr| unsafe { slice::from_raw_parts(*leaf_ptr, 32).to_vec() }) + .collect::>>(); + let mut node = [0u8; 32]; + + while leaves.len() > 1 { + let mut next_level = Vec::new(); + + for leaf_pair in leaves.chunks(2) { + match leaf_pair { + [left, right] => hash_function(left, right, &mut node), + [left] => node.copy_from_slice(left), + _ => unreachable!(), + }; + next_level.push(node.to_vec()); + } + + leaves = next_level; + } + + let root = node; + let boxed_root = root.to_vec().into_boxed_slice(); + Box::into_raw(boxed_root) as *mut u8 +} + +/// # Safety +/// +#[no_mangle] +pub unsafe extern "C" fn free_root(ptr: *mut u8) { + unsafe { + let _ = Box::from_raw(slice::from_raw_parts_mut(ptr, 32)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn setup_leaves() -> Vec<[u8; 32]> { + vec![ + // a + [ + 58, 194, 37, 22, 141, 245, 66, 18, 162, 92, 28, 1, 253, 53, 190, 191, 234, 64, 143, + 218, 194, 227, 29, 221, 111, 128, 164, 187, 249, 165, 241, 203, + ], + // b + [ + 181, 85, 61, 227, 21, 224, 237, 245, 4, 217, 21, 10, 248, 45, 175, 165, 196, 102, + 127, 166, 24, 237, 10, 111, 25, 198, 155, 65, 22, 108, 85, 16, + ], + // c + [ + 11, 66, 182, 57, 60, 31, 83, 6, 15, 227, 221, 191, 205, 122, 173, 204, 168, 148, + 70, 90, 90, 67, 143, 105, 200, 125, 121, 11, 34, 153, 185, 178, + ], + // d + [ + 241, 145, 142, 133, 98, 35, 110, 177, 122, 220, 133, 2, 51, 47, 76, 156, 130, 188, + 20, 225, 155, 252, 10, 161, 10, 182, 116, 255, 117, 179, 210, 243, + ], + ] + } + + #[test] + fn test_make_root() { + let root = [ + 104, 32, 63, 144, 233, 208, 125, 197, 133, 146, 89, 215, 83, 110, 135, 166, 186, 157, + 52, 95, 37, 82, 181, 185, 222, 41, 153, 221, 206, 156, 225, 191, + ]; + let leaves = setup_leaves(); + let leaves_ptrs: Vec<*const u8> = leaves.iter().map(|leaf| leaf.as_ptr()).collect(); + + let root_ptr = unsafe { make_root(leaves_ptrs.as_ptr(), leaves.len()) }; + let result_slice = unsafe { slice::from_raw_parts(root_ptr, 32) }; + let result = result_slice.to_vec(); + + assert_eq!(result, root); + unsafe { free_root(root_ptr) }; + + let result_dangling_ptr = unsafe { slice::from_raw_parts(root_ptr, 32) }; + assert_eq!(result_dangling_ptr, [0u8; 32]); + } +} diff --git a/merkly/accelerator/src/utils.rs b/merkly/accelerator/src/utils.rs new file mode 100644 index 0000000..e78f048 --- /dev/null +++ b/merkly/accelerator/src/utils.rs @@ -0,0 +1,21 @@ +use tiny_keccak::{Hasher, Keccak}; + +pub fn hash_it(data: &[u8], buffer: &mut [u8; 32]) { + let mut k256 = Keccak::v256(); + + k256.update(data); + k256.finalize(buffer); +} + +pub fn hash_function(left: &[u8], right: &[u8], buffer: &mut [u8; 32]) { + let concat = [left, right].concat(); + + hash_it(&concat, buffer) +} + +pub fn make_node(hash: [u8; 32], side: u8) -> [u8; 33] { + let mut node = [0u8; 33]; + node[..32].copy_from_slice(&hash); + node[32] = side; + node +} diff --git a/merkly/mtree.py b/merkly/mtree.py index dc15319..5ead677 100644 --- a/merkly/mtree.py +++ b/merkly/mtree.py @@ -56,7 +56,7 @@ def proof(self, raw_leaf: str) -> List[Node]: self.leaves, [], self.hash_function(raw_leaf.encode(), bytes()) ) - def verify(self, proof: List[bytes], raw_leaf: str) -> bool: + def verify(self, proof: List[Node], raw_leaf: str) -> bool: full_proof = [self.hash_function(raw_leaf.encode(), bytes())] full_proof.extend(proof) @@ -134,10 +134,12 @@ def make_proof( left, right = half(leaves) if index < len(leaves) / 2: - proof.append(Node(data=self.make_root(right), side=Side.RIGHT)) + node = Node(data=self.make_root(right), side=Side.RIGHT) + proof.append(node) return self.make_proof(left, proof, leaf) else: - proof.append(Node(data=self.make_root(left), side=Side.LEFT)) + node = Node(data=self.make_root(left), side=Side.LEFT) + proof.append(node) return self.make_proof(right, proof, leaf) def mix_tree( diff --git a/pyproject.toml b/pyproject.toml index ffdc42b..5dae7f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "merkly" -version = "1.1.0" +version = "1.2.0-beta" description = "🌳 The simple and easy implementation of Merkle Tree" authors = ["Lucas Oliveira "] repository = "https://github.com/olivmath/merkly.git" @@ -26,21 +26,22 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Security :: Cryptography" + "Topic :: Security :: Cryptography", ] [tool.poetry.dependencies] - pycryptodome = "^3.19.0" - pydantic = "^1.10.2" - python = "^3.8" +pycryptodome = "^3.19.0" +pydantic = "^1.10.2" +python = "^3.8" +keccaky = "^0.3.1" [tool.poetry.dev-dependencies] - conventional-pre-commit = "^3.0.0" - pre-commit = "^3.0.3" - coverage = "^7.2.7" - pyclean = "^2.2.0" - pytest = "^7.2.1" - black = "^24.1.1" +conventional-pre-commit = "^3.0.0" +pre-commit = "^3.0.3" +coverage = "^7.2.7" +pyclean = "^2.2.0" +pytest = "^7.2.1" +black = "^24.1.1" [build-system] requires = ["poetry-core>=1.0.0"] @@ -52,6 +53,7 @@ lint = "scripts.poetry:lint" [tool.pytest.ini_options] markers = [ + "ffi: mark test as requiring Rust FFI compilation (deselect with '-m \"not ffi\"')", "merkletreejs: marks tests as merkletreejs (deselect with '-m \"not merkletreejs\"')", "benchmark: marks tests as benchmark (deselect with '-m \"not benchmark\"')", -] \ No newline at end of file +] diff --git a/test/accelerator/test_accelerator.py b/test/accelerator/test_accelerator.py new file mode 100644 index 0000000..a0c6211 --- /dev/null +++ b/test/accelerator/test_accelerator.py @@ -0,0 +1,32 @@ +from merkly.accelerator.mtreers import MTreers +from merkly.mtree import MerkleTree +from keccaky import hash_it_bytes +from merkly.node import Node +from pytest import mark + + +@mark.ffi +def test_merkle_root_ffi(compile_rust_ffi): + leaves_short = ["a", "b", "c", "d"] + leaves_bytes = list(map(lambda x: hash_it_bytes(x.encode()), leaves_short)) + + tree = MerkleTree(leaves_short) + treers = MTreers() + + root = tree.root + result = treers.make_root(leaves_bytes) + + assert list(root) == list(result) + + +@mark.ffi +def test_merkle_proof_ffi(compile_rust_ffi): + leaves_short = ["a", "b", "c", "d"] + leaves_bytes = list(map(lambda x: hash_it_bytes(x.encode()), leaves_short)) + + tree = MerkleTree(leaves_short) + treers = MTreers() + + proof = tree.proof("a") + result = treers.make_proof(leaves_bytes, leaves_bytes[0]) + assert proof == result diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..0f611e6 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,27 @@ +import subprocess + +from pytest import fixture + + +@fixture(scope="session", autouse=True) +def compile_rust_ffi(request): + if any("ffi" in item.keywords for item in request.session.items): + print("Compiling Rust FFI") + result = subprocess.run( + ["./build.sh"], + cwd="./merkly/accelerator", + check=False, + capture_output=True, + text=True, + ) + print("Status:", result.returncode) + print("Output:", result.stdout) + print("Error:", result.stderr) + assert result.returncode == 0, "Falha ao compilar o código Rust" + + +@fixture(scope="session", autouse=True) +def install_js_deps(request): + if any("merkletreejs" in item.keywords for item in request.session.items): + print("Install js dependencies") + subprocess.run(["yarn"], cwd="./test/merkletreejs", check=True) diff --git a/test/merkletreejs/test_merkle_proof_compatibility.py b/test/merkletreejs/test_merkle_proof_compatibility.py index ad756dc..7fe5341 100644 --- a/test/merkletreejs/test_merkle_proof_compatibility.py +++ b/test/merkletreejs/test_merkle_proof_compatibility.py @@ -4,14 +4,15 @@ @mark.merkletreejs -def test_merkle_proof_compatibility_between_merkletreejs_and_merkly(): +def test_merkle_proof_compatibility_between_merkletreejs_and_merkly(install_js_deps): result = subprocess.run(["yarn"], check=False) assert result.returncode == 0, result.stderr result_js = subprocess.run( - ["node", "./test/merkletreejs/merkle_proof/merkle_proof_test.js"], + ["node", "./merkle_proof/merkle_proof_test.js"], capture_output=True, + cwd="./test/merkletreejs", text=True, check=True, ) diff --git a/test/merkletreejs/test_merkle_proof_verify_compatibility.py b/test/merkletreejs/test_merkle_proof_verify_compatibility.py index e937b23..4b22f1f 100644 --- a/test/merkletreejs/test_merkle_proof_verify_compatibility.py +++ b/test/merkletreejs/test_merkle_proof_verify_compatibility.py @@ -8,13 +8,16 @@ @mark.merkletreejs -def test_merkle_proof_verify_compatibility_between_merkletreejs_and_merkly(): +def test_merkle_proof_verify_compatibility_between_merkletreejs_and_merkly( + install_js_deps, +): result = subprocess.run(["yarn"], check=False) assert result.returncode == 0, result.stderr result_js = subprocess.run( - ["node", "./test/merkletreejs/merkle_proof/merkle_proof_test.js"], + ["node", "./merkle_proof/merkle_proof_test.js"], + cwd="./test/merkletreejs", capture_output=True, text=True, check=True, diff --git a/test/merkletreejs/test_merkle_root_compatibility.py b/test/merkletreejs/test_merkle_root_compatibility.py index 44a69a6..86c43ac 100644 --- a/test/merkletreejs/test_merkle_root_compatibility.py +++ b/test/merkletreejs/test_merkle_root_compatibility.py @@ -4,13 +4,14 @@ @mark.merkletreejs -def test_merkle_root_compatibility_between_merkletreejs_and_merkly(): +def test_merkle_root_compatibility_between_merkletreejs_and_merkly(install_js_deps): result = subprocess.run(["yarn"], check=False) assert result.returncode == 0, result.stderr result_js = subprocess.run( - ["node", "./test/merkletreejs/merkle_root/merkle_root_test.js"], + ["node", "./merkle_root/merkle_root_test.js"], + cwd="./test/merkletreejs", capture_output=True, text=True, check=False,