diff --git a/.github/workflows/aptos_test.yaml b/.github/workflows/aptos_test.yaml index 140835a..c3f0ad2 100644 --- a/.github/workflows/aptos_test.yaml +++ b/.github/workflows/aptos_test.yaml @@ -19,4 +19,4 @@ jobs: with: version: 4.0.0 - name: test_contract - run: (cd libs && aptos move test --skip-fetch-latest-git-deps --skip-attribute-checks) & (cd verifier && aptos move test --skip-fetch-latest-git-deps --skip-attribute-checks) \ No newline at end of file + run: (cd libs && aptos move test --coverage --dev --skip-fetch-latest-git-deps --skip-attribute-checks) & (cd verifier && aptos move test --coverage --dev --skip-fetch-latest-git-deps --skip-attribute-checks) \ No newline at end of file diff --git a/libs/sources/bytes.move b/libs/sources/bytes.move index db82a83..110c623 100644 --- a/libs/sources/bytes.move +++ b/libs/sources/bytes.move @@ -4,50 +4,6 @@ module lib_addr::bytes { use std::vector::{append, for_each_ref}; use aptos_std::from_bcs::to_u256; - // Pads a vector with a specified byte value up to the desired length - public fun pad(v: vector, desired_length: u64, pad_byte: u8, pad_left: bool): vector { - let current_length = vector::length(&v); - - if (current_length >= desired_length) { - return v - }; - - let pad = vector::empty(); - let pad_length = desired_length - current_length; - - let i = 0; - while (i < pad_length) { - vector::push_back(&mut pad, pad_byte); - i = i + 1; - }; - - let padded = vector[]; - - if (pad_left) { - vector::append(&mut padded, v); - vector::append(&mut padded, pad); - } else { - vector::append(&mut padded, pad); - vector::append(&mut padded, v); - }; - - return padded - } - - public fun reverse(x: vector): vector { - let result = vector::empty(); - let length = vector::length(&x); - let i = 0; - - while (i < length) { - let byte = vector::borrow(&x, length - 1 - i); - vector::push_back(&mut result, *byte); - i = i + 1; - }; - - return result - } - public fun vec_to_bytes_be(v: &vector): vector { let bytes: vector = vector[]; for_each_ref(v, |e| { @@ -73,19 +29,7 @@ module lib_addr::bytes { #[test_only] module lib_addr::bytes_test { - use std::bcs::to_bytes; - use std::vector; - - use lib_addr::bytes::{pad, vec_to_bytes_be}; - - #[test] - fun test_padding() { - let value = 0x123456; - let v = to_bytes(&value); - let padded = pad(v, 32, 0x00, true); - assert!(vector::length(&padded) == 32, 1); - assert!(padded == to_bytes(&0x123456u256), 1); - } + use lib_addr::bytes::vec_to_bytes_be; #[test] fun test_vec_to_bytes_be() { diff --git a/libs/sources/prime_field_element_0.move b/libs/sources/prime_field_element_0.move index 85c8e93..3e37f93 100644 --- a/libs/sources/prime_field_element_0.move +++ b/libs/sources/prime_field_element_0.move @@ -62,6 +62,10 @@ module lib_addr::prime_field_element_0 { res } + public inline fun fadd(a: u256, b: u256): u256 { + (a + b) % K_MODULUS + } + public inline fun fpow(val: u256, exp: u256): u256 { expmod(val, exp, K_MODULUS) } diff --git a/verifier/sources/cpu/memory_page_fact_registry.move b/verifier/sources/cpu/memory_page_fact_registry.move new file mode 100644 index 0000000..c748a63 --- /dev/null +++ b/verifier/sources/cpu/memory_page_fact_registry.move @@ -0,0 +1,190 @@ +module verifier_addr::memory_page_fact_registry { + use std::vector::{borrow, for_each, length}; + use aptos_std::aptos_hash::keccak256; + use aptos_framework::event::emit; + + use lib_addr::bytes::{bytes32_to_u256, vec_to_bytes_be}; + use lib_addr::prime_field_element_0::{fadd, fmul}; + use verifier_addr::fact_registry::register_fact; + + // This line is used for generating constants DO NOT REMOVE! + // 1 + const CONTINUOUS_PAGE: u256 = 0x1; + // 4 + const EINVALID_VALUE_OF_ALPHA: u64 = 0x4; + // 6 + const EINVALID_VALUE_OF_START_ADDRESS: u64 = 0x6; + // 3 + const EINVALID_VALUE_OF_Z: u64 = 0x3; + // 5 + const EPRIME_IS_TOO_BIG: u64 = 0x5; + // 2 + const ESIZE_OF_MEMORYPAIRS_MUST_BE_EVEN: u64 = 0x2; + // 1 + const ETOO_MANY_MEMORY_VALUES: u64 = 0x1; + // 0 + const REGULAR_PAGE: u256 = 0x0; + // 3618502788666131213697322783095070105623107215331596699973092056135872020481 + const K_MODULUS: u256 = 0x800000000000011000000000000000000000000000000000000000000000001; + // End of generating constants! + + #[event] + struct LogMemorypPageFactRegular has store, drop { + fact_hash: u256, + memory_hash: u256, + prod: u256 + } + + #[event] + struct LogMemoryPageFactContinuous has store, drop { + fact_hash: vector, + memory_hash: u256, + prod: u256 + } + + struct Ptr has key, store { + addr: u256, + value_ptr: u64, + prod: u256 + } + + public fun register_regular_memorypage( + signer: &signer, + memory_pairs: vector, + z: u256, + alpha: u256, + prime: u256 + ): (u256, u256, u256) { + assert!(length(&memory_pairs) < (1 << 20), ETOO_MANY_MEMORY_VALUES); + assert!((length(&memory_pairs) & 1) == 0, ESIZE_OF_MEMORYPAIRS_MUST_BE_EVEN); + assert!(z < prime, EINVALID_VALUE_OF_Z); + assert!(alpha < prime, EINVALID_VALUE_OF_ALPHA); + + let (fact_hash, memory_hash, prod) = compute_fact_hash(memory_pairs, z, alpha, prime); + emit(LogMemorypPageFactRegular { fact_hash, memory_hash, prod }); + + register_fact(signer, fact_hash); + (fact_hash, memory_hash, prod) + } + + fun compute_fact_hash( + memory_pairs: vector, + z: u256, + alpha: u256, + prime: u256 + ): (u256, u256, u256) { + let n = length(&memory_pairs); + let memory_size = n / 2; // NOLINT: divide-before-multiply. + + let prod = 1u256; + let memory_ptr = 0; + while (memory_ptr < n) { + // Compute address + alpha * value. + let address_value_lin_comb = fadd( + // address + *borrow(&memory_pairs, memory_ptr), + fmul( + // value + *borrow(&memory_pairs, memory_ptr + 1), + alpha) + ); + prod = fmul(prod, z + prime - address_value_lin_comb); + memory_ptr = memory_ptr + 2; + }; + + let memory_hash = bytes32_to_u256(keccak256(vec_to_bytes_be(&memory_pairs))); + let fact_hash = bytes32_to_u256(keccak256( + vec_to_bytes_be(&vector[REGULAR_PAGE, prime, (memory_size as u256), z, alpha, prod, memory_hash, 0u256]) + )); + (fact_hash, memory_hash, prod) + } + + /* + Receives a list of MemoryPageEntry. Each element in the list holds arguments for a seperate + call to registerContinuousMemoryPage. + */ + //TODO: assert admin + public entry fun register_continuous_page_batch( + s: &signer, + start_addr: vector, + values: vector>, + z: u256, + alpha: u256, + prime: u256 + ) { + for (i in 0..length(&start_addr) ) { + register_continuous_memorypage(s, *borrow(&start_addr, i), *borrow(&values, i), z, alpha, prime); + } + } + /* + Registers a fact based on the given values, assuming continuous addresses. + values should be [value at startAddr, value at (startAddr + 1), ...]. + */ + //TODO: assert admin + public entry fun register_continuous_memorypage( + s: &signer, + start_address: u256, + values: vector, + z: u256, + alpha: u256, + prime: u256 + ) { + assert!(length(&values) < (1 << 20), ETOO_MANY_MEMORY_VALUES); + assert!(prime < (1u256 << 254), EPRIME_IS_TOO_BIG); + assert!(z < prime, EINVALID_VALUE_OF_Z); + assert!(alpha < prime, EINVALID_VALUE_OF_ALPHA); + // Ensure 'startAddr' less then prime and bounded as a sanity check (the bound is somewhat arbitrary). + assert!(start_address < prime && start_address < (1u256 << 64), EINVALID_VALUE_OF_START_ADDRESS); + + let n_values = (length(&values) as u256); + // Initialize prod to 1. + let prod = 1; + // Initialize valuesPtr to point to the first value in the array. + let value_ptr = 0u64; + + let minus_z = (prime - z) % prime; + + // Start by processing full batches of 8 cells, addr represents the last address in each + // batch. + let addr = start_address + 7; + let last_addr = start_address + n_values; + + while (addr < last_addr) { + // Compute the product of (lin_comb - z) instead of (z - lin_comb), since we're + // doing an even number of iterations, the result is the same. + for_each(vector[0u64, 2u64, 4u64, 6u64], |offset| { + prod = fmul(prod, + fmul( + addr - 7 + (offset as u256) + fmul(alpha, *borrow(&values, value_ptr + offset)) + minus_z, + addr - 7 + (offset as u256) + 1 + fmul( + alpha, + *borrow(&values, value_ptr + offset + 1) + ) + minus_z + )); + }); + value_ptr = value_ptr + 8; + addr = addr + 8; + }; + + // Handle leftover. + // Translate addr to the beginning of the last incomplete batch. + addr = addr - 7; + while (addr < last_addr) { + let address_value_lin_comb = fadd(addr, fmul(*borrow(&values, value_ptr), alpha)); + prod = fmul(prod, z + prime - address_value_lin_comb); + addr = addr + 1; + value_ptr = value_ptr + 1; + }; + + let memory_hash = bytes32_to_u256(keccak256(vec_to_bytes_be(&values))); + let fact_hash = keccak256( + vec_to_bytes_be(&vector[CONTINUOUS_PAGE, prime, n_values, z, alpha, prod, memory_hash, start_address]) + ); + emit(LogMemoryPageFactContinuous { + fact_hash, + memory_hash, + prod + }); + register_fact(s, bytes32_to_u256(fact_hash)); + } +} \ No newline at end of file diff --git a/verifier/sources/test/test_memory_page_fact_registry.move b/verifier/sources/test/test_memory_page_fact_registry.move new file mode 100644 index 0000000..3836d6a --- /dev/null +++ b/verifier/sources/test/test_memory_page_fact_registry.move @@ -0,0 +1,51 @@ +#[test_only] +module verifier_addr::mpfr_test { + use std::signer::address_of; + + use verifier_addr::fact_registry::is_valid; + use verifier_addr::memory_page_fact_registry::{register_continuous_memorypage, + register_continuous_page_batch + }; + + #[test(signer = @verifier_addr)] + fun test_register_continuous_memorypage(signer: &signer) { + register_continuous_memorypage( + signer, + 2971260, + vector[ + 1723587082856532763241173775465496577348305577532331450336061658809521876102, + 2479248348687909740970436565718726357572221543762678024250834744245756360726, + 587272, + 2177570517647428816133395681679456086343281988787809822104528418476218261377, + 2590421891839256512113614983194993186457498815986333310670788206383913888162, + 0, + 0 + ], + 3035248388910680138215389260643346358343414931640145853107361271346254998038, + 220574768071472005565941019352306850224879407895315608807402130378653737764, + 3618502788666131213697322783095070105623107215331596699973092056135872020481 + ); + } + + #[test(s = @verifier_addr)] + // Transaction hash on ETH mainnet for this test: 0x6f59bed6f3df4b87c03c49f11e627e842ae5708a3670f428ddfb83c5b98d3754. + fun test_register_continuous_page_batch(s: &signer) { + register_continuous_page_batch( + s, + vector[1771799, 1771808], + vector[vector[1007, 1006, 1005, 1004, 1003, 1002, 1001], + vector[1008, 1007, 1006, 1005, 1004, 1003, 1002, 1001]], + 3199940278565943790978406278706496237292797978280982699986488410844249594708, + 195072032121178106591923000375621188629735561133807175660265096969353999946, + 3618502788666131213697322783095070105623107215331596699973092056135872020481 + ); + is_valid( + address_of(s), + 49238381412124717490517111631696093427076824100526472039743966257691104387218 + ); + is_valid( + address_of(s), + 54205816271920378481316162362155116341907231556132238625024261418992095639341 + ); + } +} \ No newline at end of file