Skip to content

Commit 025bb56

Browse files
committed
fix comments
1 parent 5a4271e commit 025bb56

File tree

9 files changed

+313
-182
lines changed

9 files changed

+313
-182
lines changed

common/src/size_estimation/mod.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::{
1919
};
2020

2121
use crypto::key::{PrivateKey, PublicKey, Signature};
22-
use serialization::Encode;
22+
use serialization::{CompactLen, Encode};
2323

2424
use crate::chain::{
2525
classic_multisig::ClassicMultisigChallenge,
@@ -40,6 +40,8 @@ use crate::chain::{
4040
pub enum SizeEstimationError {
4141
#[error("Unsupported input destination")]
4242
UnsupportedInputDestination(Destination),
43+
#[error("Attempted to estimate the size of a TX with too many inputs or outputs {0}")]
44+
TooManyElements(usize),
4345
}
4446

4547
/// Return the encoded size of an input signature.
@@ -194,31 +196,40 @@ pub fn input_signature_size_from_destination(
194196

195197
/// Return the encoded size for a SignedTransaction also accounting for the compact encoding of the
196198
/// vectors for the specified number of inputs and outputs
197-
pub fn tx_size_with_num_inputs_and_outputs(num_outputs: usize, num_inputs: usize) -> usize {
198-
#[derive(Encode)]
199-
struct CompactSize {
200-
#[codec(compact)]
201-
value: u64,
199+
pub fn tx_size_with_num_inputs_and_outputs(
200+
num_outputs: usize,
201+
num_inputs: usize,
202+
) -> Result<usize, SizeEstimationError> {
203+
lazy_static::lazy_static! {
204+
static ref EMPTY_SIGNED_TX_SIZE: usize = {
205+
let tx = SignedTransaction::new(
206+
Transaction::new(1, vec![], vec![]).expect("should not fail"),
207+
vec![],
208+
)
209+
.expect("should not fail");
210+
serialization::Encode::encoded_size(&tx)
211+
};
212+
}
213+
lazy_static::lazy_static! {
214+
static ref ZERO_COMPACT_SIZE: usize = {
215+
serialization::Compact::<u32>::compact_len(&0)
216+
};
202217
}
203218

204-
let tx = SignedTransaction::new(
205-
Transaction::new(1, vec![], vec![]).expect("should not fail"),
206-
vec![],
207-
)
208-
.expect("should not fail");
209-
let size = serialization::Encode::encoded_size(&tx);
210-
211-
let input_compact_size_diff =
212-
serialization::Encode::encoded_size(&CompactSize {
213-
value: num_inputs as u64,
214-
}) - serialization::Encode::encoded_size(&CompactSize { value: 0 });
219+
let input_compact_size_diff = serialization::Compact::<u32>::compact_len(
220+
&(num_inputs
221+
.try_into()
222+
.map_err(|_| SizeEstimationError::TooManyElements(num_inputs))?),
223+
) - *ZERO_COMPACT_SIZE;
215224

216-
let output_compact_size_diff =
217-
serialization::Encode::encoded_size(&CompactSize {
218-
value: num_outputs as u64,
219-
}) - serialization::Encode::encoded_size(&CompactSize { value: 0 });
225+
let output_compact_size_diff = serialization::Compact::<u32>::compact_len(
226+
&(num_outputs
227+
.try_into()
228+
.map_err(|_| SizeEstimationError::TooManyElements(num_inputs))?),
229+
) - *ZERO_COMPACT_SIZE;
220230

221-
size + output_compact_size_diff + (input_compact_size_diff * 2) // 2 for number of inputs and number of input signatures
231+
// 2 for number of inputs and number of input signatures
232+
Ok(*EMPTY_SIGNED_TX_SIZE + output_compact_size_diff + (input_compact_size_diff * 2))
222233
}
223234

224235
pub fn outputs_encoded_size(outputs: &[TxOutput]) -> usize {
@@ -241,3 +252,6 @@ fn get_tx_output_destination(txo: &TxOutput) -> Option<&Destination> {
241252
| TxOutput::CreateOrder(_) => None,
242253
}
243254
}
255+
256+
#[cfg(test)]
257+
mod tests;

common/src/size_estimation/tests.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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(#[case] seed: Seed) {
33+
use crypto::key::{KeyKind, PrivateKey};
34+
use serialization::Encode;
35+
36+
use crate::{
37+
chain::{
38+
output_value::OutputValue,
39+
signature::inputsig::authorize_pubkey_spend::AuthorizedPublicKeySpend, Destination,
40+
TxOutput,
41+
},
42+
size_estimation::tx_size_with_num_inputs_and_outputs,
43+
};
44+
45+
let mut rng = make_seedable_rng(seed);
46+
47+
let num_inputs = rng.gen_range(1..10_000);
48+
let inputs = (0..num_inputs)
49+
.map(|_| {
50+
TxInput::from_utxo(
51+
OutPointSourceId::Transaction(Id::random_using(&mut rng)),
52+
rng.gen_range(0..100),
53+
)
54+
})
55+
.collect();
56+
57+
let num_outputs = rng.gen_range(1..10_000);
58+
let outputs = (0..num_outputs)
59+
.map(|_| {
60+
let destination = Destination::PublicKey(
61+
crypto::key::PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr).1,
62+
);
63+
64+
TxOutput::Transfer(
65+
OutputValue::Coin(Amount::from_atoms(rng.gen_range(1..10000))),
66+
destination,
67+
)
68+
})
69+
.collect();
70+
71+
let tx = Transaction::new(0, inputs, outputs).unwrap();
72+
let signatures = (0..num_inputs)
73+
.map(|_| {
74+
let private_key =
75+
PrivateKey::new_from_rng(&mut rng, crypto::key::KeyKind::Secp256k1Schnorr).0;
76+
let signature = private_key.sign_message(&[0; 32], &mut rng).unwrap();
77+
let raw_signature = AuthorizedPublicKeySpend::new(signature).encode();
78+
let standard = StandardInputSignature::new(SigHashType::all(), raw_signature);
79+
InputWitness::Standard(standard)
80+
})
81+
.collect();
82+
let tx = SignedTransaction::new(tx, signatures).unwrap();
83+
84+
let estimated_tx_size = tx_size_with_num_inputs_and_outputs(num_outputs, num_inputs).unwrap()
85+
+ tx.inputs().iter().map(Encode::encoded_size).sum::<usize>()
86+
+ tx.signatures().iter().map(Encode::encoded_size).sum::<usize>()
87+
+ tx.outputs().iter().map(Encode::encoded_size).sum::<usize>();
88+
89+
let expected_tx_size = Encode::encoded_size(&tx);
90+
91+
assert_eq!(estimated_tx_size, expected_tx_size);
92+
}

serialization/core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
1818
// Re-export SCALE traits
1919
pub use parity_scale_codec::{
20-
Codec, Decode, DecodeAll, Encode, EncodeLike, Input, Output, WrapperTypeDecode,
20+
Codec, CompactLen, Decode, DecodeAll, Encode, EncodeLike, Input, Output, WrapperTypeDecode,
2121
WrapperTypeEncode,
2222
};
2323

0 commit comments

Comments
 (0)