// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Tests for transaction-storage pallet. use super::{Pallet as TransactionStorage, *}; use crate::mock::*; use pezframe_support::{assert_noop, assert_ok}; use pezframe_system::RawOrigin; use pezsp_runtime::{DispatchError, TokenError::FundsUnavailable}; use pezsp_transaction_storage_proof::{registration::build_proof, CHUNK_SIZE}; const MAX_DATA_SIZE: u32 = DEFAULT_MAX_TRANSACTION_SIZE; #[test] fn discards_data() { new_test_ext().execute_with(|| { run_to_block(1, || None); let caller = 1; assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), vec![0u8; 2000 as usize] )); assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), vec![0u8; 2000 as usize] )); let proof_provider = || { let block_num = pezframe_system::Pallet::::block_number(); if block_num == 11 { let parent_hash = pezframe_system::Pallet::::parent_hash(); build_proof(parent_hash.as_ref(), vec![vec![0u8; 2000], vec![0u8; 2000]]).unwrap() } else { None } }; run_to_block(11, proof_provider); assert!(Transactions::::get(1).is_some()); let transactions = Transactions::::get(1).unwrap(); assert_eq!(transactions.len(), 2); assert_eq!(TransactionInfo::total_chunks(&transactions), 16); run_to_block(12, proof_provider); assert!(Transactions::::get(1).is_none()); }); } #[test] fn burns_fee() { new_test_ext().execute_with(|| { run_to_block(1, || None); let caller = 1; assert_noop!( TransactionStorage::::store( RawOrigin::Signed(5).into(), vec![0u8; 2000 as usize] ), DispatchError::Token(FundsUnavailable), ); assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), vec![0u8; 2000 as usize] )); assert_eq!(Balances::free_balance(1), 1_000_000_000 - 2000 * 2 - 200); }); } #[test] fn checks_proof() { new_test_ext().execute_with(|| { run_to_block(1, || None); let caller = 1; assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), vec![0u8; MAX_DATA_SIZE as usize] )); run_to_block(10, || None); let parent_hash = pezframe_system::Pallet::::parent_hash(); let proof = build_proof(parent_hash.as_ref(), vec![vec![0u8; MAX_DATA_SIZE as usize]]) .unwrap() .unwrap(); assert_noop!( TransactionStorage::::check_proof(RuntimeOrigin::none(), proof,), Error::::UnexpectedProof, ); run_to_block(11, || None); let parent_hash = pezframe_system::Pallet::::parent_hash(); let invalid_proof = build_proof(parent_hash.as_ref(), vec![vec![0u8; 1000]]).unwrap().unwrap(); assert_noop!( TransactionStorage::::check_proof(RuntimeOrigin::none(), invalid_proof,), Error::::InvalidProof, ); let proof = build_proof(parent_hash.as_ref(), vec![vec![0u8; MAX_DATA_SIZE as usize]]) .unwrap() .unwrap(); assert_ok!(TransactionStorage::::check_proof(RuntimeOrigin::none(), proof)); }); } #[test] fn verify_chunk_proof_works() { new_test_ext().execute_with(|| { // Prepare a bunch of transactions with variable chunk sizes. let transactions = vec![ vec![0u8; CHUNK_SIZE - 1], vec![1u8; CHUNK_SIZE], vec![2u8; CHUNK_SIZE + 1], vec![3u8; 2 * CHUNK_SIZE - 1], vec![3u8; 2 * CHUNK_SIZE], vec![3u8; 2 * CHUNK_SIZE + 1], vec![4u8; 7 * CHUNK_SIZE - 1], vec![4u8; 7 * CHUNK_SIZE], vec![4u8; 7 * CHUNK_SIZE + 1], ]; let expected_total_chunks = transactions.iter().map(|t| t.len().div_ceil(CHUNK_SIZE) as u32).sum::(); // Store a couple of transactions in one block. run_to_block(1, || None); let caller = 1; for transaction in transactions.clone() { assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), transaction )); } run_to_block(2, || None); // Read all the block transactions metadata. let tx_infos = Transactions::::get(1).unwrap(); let total_chunks = TransactionInfo::total_chunks(&tx_infos); assert_eq!(expected_total_chunks, total_chunks); assert_eq!(9, tx_infos.len()); // Verify proofs for all possible chunk indexes. for chunk_index in 0..total_chunks { // chunk index randomness let mut random_hash = [0u8; 32]; random_hash[..8].copy_from_slice(&(chunk_index as u64).to_be_bytes()); let selected_chunk_index = random_chunk(random_hash.as_ref(), total_chunks); assert_eq!(selected_chunk_index, chunk_index); // build/check chunk proof roundtrip let proof = build_proof(random_hash.as_ref(), transactions.clone()) .expect("valid proof") .unwrap(); assert_ok!(TransactionStorage::::verify_chunk_proof( proof, random_hash.as_ref(), tx_infos.to_vec() )); } }); } #[test] fn renews_data() { new_test_ext().execute_with(|| { run_to_block(1, || None); let caller = 1; assert_noop!( TransactionStorage::::store(RawOrigin::Signed(caller).into(), vec![]), Error::::EmptyTransaction ); assert_ok!(TransactionStorage::::store( RawOrigin::Signed(caller).into(), vec![0u8; 2000] )); let info = BlockTransactions::::get().last().unwrap().clone(); run_to_block(6, || None); assert_ok!(TransactionStorage::::renew( RawOrigin::Signed(caller).into(), 1, // block 0, // transaction )); assert_eq!(Balances::free_balance(1), 1_000_000_000 - 4000 * 2 - 200 * 2); let proof_provider = || { let block_num = pezframe_system::Pallet::::block_number(); if block_num == 11 || block_num == 16 { let parent_hash = pezframe_system::Pallet::::parent_hash(); build_proof(parent_hash.as_ref(), vec![vec![0u8; 2000]]).unwrap() } else { None } }; run_to_block(16, proof_provider); assert!(Transactions::::get(1).is_none()); assert_eq!(Transactions::::get(6).unwrap().get(0), Some(info).as_ref()); run_to_block(17, proof_provider); assert!(Transactions::::get(6).is_none()); }); }