diff --git a/src/main/scala/com/fluency03/blockchain/core/Block.scala b/src/main/scala/com/fluency03/blockchain/core/Block.scala index 30f76e8..19c0415 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Block.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Block.scala @@ -6,6 +6,8 @@ import com.fluency03.blockchain.core.Transaction.createCoinbaseTx import org.json4s.native.JsonMethods.{compact, render} import org.json4s.{Extraction, JValue} +import scala.collection.mutable + /** * Block on the chain. * @param header Header of current Block @@ -37,6 +39,8 @@ case class Block(header: BlockHeader, transactions: Seq[Transaction], hash: Stri def hasValidHeaderHash: Boolean = hash == header.hash + def allTransactionsValid(uTxOs: mutable.Map[Outpoint, TxOut]): Boolean = transactions.forall(tx => tx.isValid(uTxOs)) + def toJson: JValue = Extraction.decompose(this) override def toString: String = compact(render(toJson)) diff --git a/src/main/scala/com/fluency03/blockchain/core/Transaction.scala b/src/main/scala/com/fluency03/blockchain/core/Transaction.scala index d3260d1..5baae69 100644 --- a/src/main/scala/com/fluency03/blockchain/core/Transaction.scala +++ b/src/main/scala/com/fluency03/blockchain/core/Transaction.scala @@ -134,12 +134,17 @@ object Transaction { .map(t => t.txOuts.zipWithIndex.map { case (txOut, index) => Outpoint(t.id, index) -> txOut }.toMap) - .reduce { _ ++ _ } + .foldLeft(Map.empty[Outpoint, TxOut])(_ ++ _) def getConsumedUTxOs(transactions: Seq[Transaction]): Map[Outpoint, TxOut] = transactions.map(_.txIns) - .reduce { _ ++ _ } + .foldLeft(Seq.empty[TxIn])(_ ++ _) .map(txIn => Outpoint(txIn.previousOut.id, txIn.previousOut.index) -> TxOut("", 0)) .toMap + def noDuplicateTxIn(transactions: Seq[Transaction]): Boolean = { + val allRefs = transactions.map(_.txIns.map(_.previousOut)).foldLeft(Seq.empty[Outpoint])(_ ++ _) + allRefs.distinct.length == allRefs.length + } + } diff --git a/src/test/scala/com/fluency03/blockchain/core/TransactionTest.scala b/src/test/scala/com/fluency03/blockchain/core/TransactionTest.scala index c8e5c01..0809724 100644 --- a/src/test/scala/com/fluency03/blockchain/core/TransactionTest.scala +++ b/src/test/scala/com/fluency03/blockchain/core/TransactionTest.scala @@ -207,4 +207,37 @@ class TransactionTest extends FlatSpec with Matchers { signedTx2.isValid(uTxOs) shouldEqual true } + "noDuplicateTxIn" should "detect whether Seq of Transactions contians duplicate TxIns." in { + noDuplicateTxIn(Seq.empty[Transaction]) shouldEqual true + val tx1: Transaction = Transaction( + Seq(TxIn(Outpoint("a", 0), ""), + TxIn(Outpoint("b", 1), "")), + Seq(TxOut("o", 40)), + genesisTimestamp + ) + noDuplicateTxIn(Seq(tx1)) shouldEqual true + val tx2: Transaction = Transaction( + Seq(TxIn(Outpoint("c", 0), ""), + TxIn(Outpoint("d", 1), "")), + Seq(TxOut("o", 40)), + genesisTimestamp + ) + noDuplicateTxIn(Seq(tx1, tx2)) shouldEqual true + val tx3: Transaction = Transaction( + Seq(TxIn(Outpoint("e", 0), ""), + TxIn(Outpoint("a", 1), "")), + Seq(TxOut("o", 40)), + genesisTimestamp + ) + noDuplicateTxIn(Seq(tx1, tx2, tx3)) shouldEqual true + val tx4: Transaction = Transaction( + Seq(TxIn(Outpoint("a", 0), ""), + TxIn(Outpoint("f", 1), "")), + Seq(TxOut("o", 40)), + genesisTimestamp + ) + noDuplicateTxIn(Seq(tx1, tx2, tx3, tx4)) shouldEqual false + } + + }