diff --git a/db/src/storage.rs b/db/src/storage.rs index b184a539..7133f4cb 100644 --- a/db/src/storage.rs +++ b/db/src/storage.rs @@ -234,10 +234,10 @@ impl Storage { // another iteration skipping coinbase transaction for accepted_tx in accepted_txs.iter().skip(1) { - for (input_idx, input) in accepted_tx.inputs.iter().enumerate() { + for input in accepted_tx.inputs.iter() { if !match meta_buf.get_mut(&input.previous_output.hash) { Some(ref mut meta) => { - meta.note_used(input_idx); + meta.note_used(input.previous_output.index as usize); true }, None => false, @@ -249,7 +249,7 @@ impl Storage { &input.previous_output.hash )); - meta.note_used(input_idx); + meta.note_used(input.previous_output.index as usize); meta_buf.insert( input.previous_output.hash.clone(), @@ -532,4 +532,53 @@ mod tests { assert!(meta.is_spent(0), "Transaction #1 first output in the new block should be recorded as spent"); assert!(!meta.is_spent(1), "Transaction #1 second output in the new block should be recorded as unspent"); } + + #[test] + fn transaction_meta_complex() { + + let path = RandomTempPath::create_dir(); + let store = Storage::new(path.as_path()).unwrap(); + + let genesis = test_data::genesis(); + store.insert_block(&genesis).unwrap(); + let genesis_coinbase = genesis.transactions()[0].hash(); + + let block1 = test_data::block_builder() + .header().parent(genesis.hash()).build() + .transaction().coinbase().build() + .transaction() + .input().hash(genesis_coinbase).build() + .output().value(10).build() + .output().value(15).build() + .output().value(10).build() + .output().value(1).build() + .output().value(4).build() + .output().value(10).build() + .build() + .build(); + + store.insert_block(&block1).unwrap(); + + let tx_big = block1.transactions()[1].hash(); + let block2 = test_data::block_builder() + .header().parent(block1.hash()).build() + .transaction().coinbase().build() + .transaction() + .input().hash(tx_big.clone()).index(0).build() + .input().hash(tx_big.clone()).index(2).build() + .input().hash(tx_big.clone()).index(5).build() + .output().value(30).build() + .build() + .build(); + + store.insert_block(&block2).unwrap(); + + let meta = store.transaction_meta(&tx_big).unwrap(); + assert!(meta.is_spent(0), "Transaction #1 output #0 in the new block should be recorded as spent"); + assert!(meta.is_spent(2), "Transaction #1 output #2 in the new block should be recorded as spent"); + assert!(meta.is_spent(5), "Transaction #1 output #5 in the new block should be recorded as spent"); + + assert!(!meta.is_spent(1), "Transaction #1 output #1 in the new block should be recorded as unspent"); + assert!(!meta.is_spent(3), "Transaction #1 second #3 in the new block should be recorded as unspent"); + } } diff --git a/test-data/src/block.rs b/test-data/src/block.rs index 4acfafdb..89377770 100644 --- a/test-data/src/block.rs +++ b/test-data/src/block.rs @@ -63,6 +63,27 @@ impl BlockBuilder where F: Invoke { TransactionBuilder::with_callback(self).input().hash(tx.hash()).index(output_idx).build() } + // use vec![(0, 1), (0, 2), (1, 1)] + pub fn derived_transactions(self, outputs: I) -> TransactionBuilder + where I: IntoIterator + { + let mut derives = Vec::new(); + for (tx_idx, output_idx) in outputs { + derives.push( + ( + self.transactions.get(tx_idx).expect(&format!("using derive_transaction with the wrong index ({})", tx_idx)).hash(), + output_idx + ) + ); + } + + let mut builder = TransactionBuilder::with_callback(self); + for (tx_hash, output_idx) in derives { + builder = builder.input().hash(tx_hash).index(output_idx).build(); + } + builder + } + pub fn build(self) -> F::Result { self.callback.invoke( chain::Block::new(