|
| 1 | +// Copyright (c) 2025 RBB S.r.l |
| 2 | + |
| 3 | +// SPDX-License-Identifier: MIT |
| 4 | +// Licensed under the MIT License; |
| 5 | +// you may not use this file except in compliance with the License. |
| 6 | +// You may obtain a copy of the License at |
| 7 | +// |
| 8 | +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE |
| 9 | +// |
| 10 | +// Unless required by applicable law or agreed to in writing, software |
| 11 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +// See the License for the specific language governing permissions and |
| 14 | +// limitations under the License. |
| 15 | + |
| 16 | +use randomness::Rng; |
| 17 | +use rstest::rstest; |
| 18 | +use test_utils::random::{make_seedable_rng, Seed}; |
| 19 | + |
| 20 | +use crate::chain::{ |
| 21 | + signature::{ |
| 22 | + inputsig::{standard_signature::StandardInputSignature, InputWitness}, |
| 23 | + sighash::sighashtype::SigHashType, |
| 24 | + }, |
| 25 | + OutPointSourceId, SignedTransaction, Transaction, TxInput, |
| 26 | +}; |
| 27 | +use crate::primitives::{Amount, Id}; |
| 28 | + |
| 29 | +#[rstest] |
| 30 | +#[trace] |
| 31 | +#[case(Seed::from_entropy())] |
| 32 | +fn estimate_tx_size( |
| 33 | + #[case] seed: Seed, |
| 34 | + #[values(1..64, 64..0x4000, 0x4000..0x4001)] inputs_range: std::ops::Range<u32>, |
| 35 | + #[values(1..64, 64..0x4000, 0x4000..0x4001)] outputs_range: std::ops::Range<u32>, |
| 36 | +) { |
| 37 | + use crypto::key::{KeyKind, PrivateKey}; |
| 38 | + use serialization::Encode; |
| 39 | + |
| 40 | + use crate::{ |
| 41 | + chain::{ |
| 42 | + output_value::OutputValue, |
| 43 | + signature::inputsig::authorize_pubkey_spend::AuthorizedPublicKeySpend, Destination, |
| 44 | + TxOutput, |
| 45 | + }, |
| 46 | + size_estimation::tx_size_with_num_inputs_and_outputs, |
| 47 | + }; |
| 48 | + |
| 49 | + let mut rng = make_seedable_rng(seed); |
| 50 | + |
| 51 | + let num_inputs = rng.gen_range(inputs_range); |
| 52 | + let inputs = (0..num_inputs) |
| 53 | + .map(|_| { |
| 54 | + TxInput::from_utxo( |
| 55 | + OutPointSourceId::Transaction(Id::random_using(&mut rng)), |
| 56 | + rng.gen_range(0..100), |
| 57 | + ) |
| 58 | + }) |
| 59 | + .collect(); |
| 60 | + |
| 61 | + let num_outputs = rng.gen_range(outputs_range); |
| 62 | + let outputs = (0..num_outputs) |
| 63 | + .map(|_| { |
| 64 | + let destination = Destination::PublicKey( |
| 65 | + crypto::key::PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr).1, |
| 66 | + ); |
| 67 | + |
| 68 | + TxOutput::Transfer( |
| 69 | + OutputValue::Coin(Amount::from_atoms(rng.gen_range(1..10000))), |
| 70 | + destination, |
| 71 | + ) |
| 72 | + }) |
| 73 | + .collect(); |
| 74 | + |
| 75 | + let tx = Transaction::new(0, inputs, outputs).unwrap(); |
| 76 | + let signatures = (0..num_inputs) |
| 77 | + .map(|_| { |
| 78 | + let private_key = |
| 79 | + PrivateKey::new_from_rng(&mut rng, crypto::key::KeyKind::Secp256k1Schnorr).0; |
| 80 | + let signature = private_key.sign_message(&[0; 32], &mut rng).unwrap(); |
| 81 | + let raw_signature = AuthorizedPublicKeySpend::new(signature).encode(); |
| 82 | + let standard = StandardInputSignature::new(SigHashType::all(), raw_signature); |
| 83 | + InputWitness::Standard(standard) |
| 84 | + }) |
| 85 | + .collect(); |
| 86 | + let tx = SignedTransaction::new(tx, signatures).unwrap(); |
| 87 | + |
| 88 | + let estimated_tx_size = |
| 89 | + tx_size_with_num_inputs_and_outputs(num_outputs as usize, num_inputs as usize).unwrap() |
| 90 | + + tx.inputs().iter().map(Encode::encoded_size).sum::<usize>() |
| 91 | + + tx.signatures().iter().map(Encode::encoded_size).sum::<usize>() |
| 92 | + + tx.outputs().iter().map(Encode::encoded_size).sum::<usize>(); |
| 93 | + |
| 94 | + let expected_tx_size = Encode::encoded_size(&tx); |
| 95 | + |
| 96 | + assert_eq!(estimated_tx_size, expected_tx_size); |
| 97 | +} |
0 commit comments