@@ -91,10 +91,13 @@ impl MassCalculator {
9191 let ins_len = tx. tx ( ) . inputs . len ( ) as u64 ;
9292
9393 /*
94- KIP-0009 relaxed formula for the cases |O| = 1 OR |O| <= |I| <= 2:
95- max( 0 , C·( |O|/H(O) - |I|/H(I) ) )
94+ KIP-0009 relaxed formula for the cases |O| = 1 OR |O| <= |I| <= 2:
95+ max( 0 , C·( |O|/H(O) - |I|/H(I) ) )
96+
97+ Note: in the case |I| = 1 both formulas are equal, yet the following code (harmonic_ins) is a bit more efficient.
98+ Hence, we transform the condition to |O| = 1 OR |I| = 1 OR |O| = |I| = 2 which is equivalent (and faster).
9699 */
97- if version == Kip9Version :: Beta && ( outs_len == 1 || ( outs_len <= ins_len && ins_len < = 2 ) ) {
100+ if version == Kip9Version :: Beta && ( outs_len == 1 || ins_len == 1 || ( outs_len == 2 && ins_len = = 2 ) ) {
98101 let harmonic_ins = tx
99102 . populated_inputs ( )
100103 . map ( |( _, entry) | self . storage_mass_parameter / entry. amount )
@@ -144,63 +147,8 @@ mod tests {
144147
145148 #[ test]
146149 fn test_mass_storage ( ) {
147- let script_pub_key = ScriptVec :: from_slice ( & [ ] ) ;
148- let prev_tx_id = TransactionId :: from_str ( "880eb9819a31821d9d2399e2f35e2433b72637e393d71ecc9b8d0250f49153c3" ) . unwrap ( ) ;
149-
150150 // Tx with less outs than ins
151- let tx = Transaction :: new (
152- 0 ,
153- vec ! [
154- TransactionInput {
155- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 0 } ,
156- signature_script: vec![ ] ,
157- sequence: 0 ,
158- sig_op_count: 0 ,
159- } ,
160- TransactionInput {
161- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 1 } ,
162- signature_script: vec![ ] ,
163- sequence: 1 ,
164- sig_op_count: 0 ,
165- } ,
166- TransactionInput {
167- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 2 } ,
168- signature_script: vec![ ] ,
169- sequence: 2 ,
170- sig_op_count: 0 ,
171- } ,
172- ] ,
173- vec ! [
174- TransactionOutput { value: 300 , script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) } ,
175- TransactionOutput { value: 300 , script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) } ,
176- ] ,
177- 1615462089000 ,
178- SubnetworkId :: from_bytes ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ,
179- 0 ,
180- vec ! [ ] ,
181- ) ;
182-
183- let entries = vec ! [
184- UtxoEntry {
185- amount: 100 ,
186- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
187- block_daa_score: 0 ,
188- is_coinbase: false ,
189- } ,
190- UtxoEntry {
191- amount: 200 ,
192- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
193- block_daa_score: 0 ,
194- is_coinbase: false ,
195- } ,
196- UtxoEntry {
197- amount: 300 ,
198- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
199- block_daa_score: 0 ,
200- is_coinbase: false ,
201- } ,
202- ] ;
203- let mut tx = MutableTransaction :: with_entries ( tx, entries) ;
151+ let mut tx = generate_tx_from_amounts ( & [ 100 , 200 , 300 ] , & [ 300 , 300 ] ) ;
204152 let test_version = Kip9Version :: Alpha ;
205153
206154 // Assert the formula: max( 0 , C·( |O|/H(O) - |I|/A(I) ) )
@@ -218,74 +166,8 @@ mod tests {
218166 assert_eq ! ( storage_mass, storage_mass_parameter / 50 + storage_mass_parameter / 550 - 3 * ( storage_mass_parameter / 200 ) ) ;
219167
220168 // Create a tx with more outs than ins
221- let tx = Transaction :: new (
222- 0 ,
223- vec ! [
224- TransactionInput {
225- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 0 } ,
226- signature_script: vec![ ] ,
227- sequence: 0 ,
228- sig_op_count: 0 ,
229- } ,
230- TransactionInput {
231- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 1 } ,
232- signature_script: vec![ ] ,
233- sequence: 1 ,
234- sig_op_count: 0 ,
235- } ,
236- TransactionInput {
237- previous_outpoint: TransactionOutpoint { transaction_id: prev_tx_id, index: 2 } ,
238- signature_script: vec![ ] ,
239- sequence: 2 ,
240- sig_op_count: 0 ,
241- } ,
242- ] ,
243- vec ! [
244- TransactionOutput {
245- value: 10_000 * SOMPI_PER_KASPA ,
246- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
247- } ,
248- TransactionOutput {
249- value: 10_000 * SOMPI_PER_KASPA ,
250- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
251- } ,
252- TransactionOutput {
253- value: 10_000 * SOMPI_PER_KASPA ,
254- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
255- } ,
256- TransactionOutput {
257- value: 10_000 * SOMPI_PER_KASPA ,
258- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
259- } ,
260- ] ,
261- 1615462089000 ,
262- SubnetworkId :: from_bytes ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ,
263- 0 ,
264- vec ! [ ] ,
265- ) ;
266-
267- let entries = vec ! [
268- UtxoEntry {
269- amount: 10_000 * SOMPI_PER_KASPA ,
270- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
271- block_daa_score: 0 ,
272- is_coinbase: false ,
273- } ,
274- UtxoEntry {
275- amount: 10_000 * SOMPI_PER_KASPA ,
276- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
277- block_daa_score: 0 ,
278- is_coinbase: false ,
279- } ,
280- UtxoEntry {
281- amount: 20_000 * SOMPI_PER_KASPA ,
282- script_public_key: ScriptPublicKey :: new( 0 , script_pub_key. clone( ) ) ,
283- block_daa_score: 0 ,
284- is_coinbase: false ,
285- } ,
286- ] ;
287- let mut tx = MutableTransaction :: with_entries ( tx, entries) ;
288-
169+ let base_value = 10_000 * SOMPI_PER_KASPA ;
170+ let mut tx = generate_tx_from_amounts ( & [ base_value, base_value, base_value * 2 ] , & [ base_value; 4 ] ) ;
289171 let storage_mass_parameter = STORAGE_MASS_PARAMETER ;
290172 let storage_mass =
291173 MassCalculator :: new ( 0 , 0 , 0 , storage_mass_parameter) . calc_tx_storage_mass ( & tx. as_verifiable ( ) , test_version) . unwrap ( ) ;
@@ -305,7 +187,70 @@ mod tests {
305187 let storage_mass =
306188 MassCalculator :: new ( 0 , 0 , 0 , storage_mass_parameter) . calc_tx_storage_mass ( & tx. as_verifiable ( ) , test_version) . unwrap ( ) ;
307189 assert_eq ! ( storage_mass, 0 ) ;
190+ }
191+
192+ #[ test]
193+ fn test_mass_storage_beta ( ) {
194+ // 2:2 transaction
195+ let mut tx = generate_tx_from_amounts ( & [ 100 , 200 ] , & [ 50 , 250 ] ) ;
196+ let storage_mass_parameter = 10u64 . pow ( 12 ) ;
197+ let test_version = Kip9Version :: Beta ;
198+ // Assert the formula: max( 0 , C·( |O|/H(O) - |I|/O(I) ) )
199+
200+ let storage_mass =
201+ MassCalculator :: new ( 0 , 0 , 0 , storage_mass_parameter) . calc_tx_storage_mass ( & tx. as_verifiable ( ) , test_version) . unwrap ( ) ;
202+ assert_eq ! ( storage_mass, 9000000000 ) ;
203+
204+ // Set outputs to be equal to inputs
205+ tx. tx . outputs [ 0 ] . value = 100 ;
206+ tx. tx . outputs [ 1 ] . value = 200 ;
207+ let storage_mass =
208+ MassCalculator :: new ( 0 , 0 , 0 , storage_mass_parameter) . calc_tx_storage_mass ( & tx. as_verifiable ( ) , test_version) . unwrap ( ) ;
209+ assert_eq ! ( storage_mass, 0 ) ;
308210
309- drop ( script_pub_key) ;
211+ // Remove an output and make sure the other is small enough to make storage mass greater than zero
212+ tx. tx . outputs . pop ( ) ;
213+ tx. tx . outputs [ 0 ] . value = 50 ;
214+ let storage_mass =
215+ MassCalculator :: new ( 0 , 0 , 0 , storage_mass_parameter) . calc_tx_storage_mass ( & tx. as_verifiable ( ) , test_version) . unwrap ( ) ;
216+ assert_eq ! ( storage_mass, 5000000000 ) ;
217+ }
218+
219+ fn generate_tx_from_amounts ( ins : & [ u64 ] , outs : & [ u64 ] ) -> MutableTransaction < Transaction > {
220+ let script_pub_key = ScriptVec :: from_slice ( & [ ] ) ;
221+ let prev_tx_id = TransactionId :: from_str ( "880eb9819a31821d9d2399e2f35e2433b72637e393d71ecc9b8d0250f49153c3" ) . unwrap ( ) ;
222+ let tx = Transaction :: new (
223+ 0 ,
224+ ( 0 ..ins. len ( ) )
225+ . map ( |i| TransactionInput {
226+ previous_outpoint : TransactionOutpoint { transaction_id : prev_tx_id, index : i as u32 } ,
227+ signature_script : vec ! [ ] ,
228+ sequence : 0 ,
229+ sig_op_count : 0 ,
230+ } )
231+ . collect ( ) ,
232+ outs. iter ( )
233+ . copied ( )
234+ . map ( |out_amount| TransactionOutput {
235+ value : out_amount,
236+ script_public_key : ScriptPublicKey :: new ( 0 , script_pub_key. clone ( ) ) ,
237+ } )
238+ . collect ( ) ,
239+ 1615462089000 ,
240+ SubnetworkId :: from_bytes ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ,
241+ 0 ,
242+ vec ! [ ] ,
243+ ) ;
244+ let entries = ins
245+ . iter ( )
246+ . copied ( )
247+ . map ( |in_amount| UtxoEntry {
248+ amount : in_amount,
249+ script_public_key : ScriptPublicKey :: new ( 0 , script_pub_key. clone ( ) ) ,
250+ block_daa_score : 0 ,
251+ is_coinbase : false ,
252+ } )
253+ . collect ( ) ;
254+ MutableTransaction :: with_entries ( tx, entries)
310255 }
311256}
0 commit comments