From 8b1864e07c5cb24ce9819bb6b79721111fed0411 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Sat, 18 Feb 2017 16:30:22 +0000 Subject: [PATCH] Minor: naming and doc tweaks in Merkle tree code. --- .../core/transactions/MerkleTransaction.kt | 70 ++++++++++--------- .../core/transactions/WireTransaction.kt | 6 +- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt index a662a4a5e8..3d17ce1b85 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt @@ -2,11 +2,9 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.* -import net.corda.core.crypto.SecureHash.Companion.zeroHash import net.corda.core.serialization.createKryo import net.corda.core.serialization.extendKryoHash import net.corda.core.serialization.serialize -import java.util.* fun serializedHash(x: T): SecureHash { val kryo = extendKryoHash(createKryo()) // Dealing with HashMaps inside states. @@ -14,9 +12,13 @@ fun serializedHash(x: T): SecureHash { } /** - * Interface implemented by WireTransaction and FilteredLeaves. - * Property traversableList assures that we always calculate hashes in the same order, lets us define which - * fields of WireTransaction will be included in id calculation or partial merkle tree building. + * Implemented by [WireTransaction] and [FilteredLeaves]. A TraversableTransaction allows you to iterate + * over the flattened components of the underlying transaction structure, taking into account that some + * may be missing in the case of this representing a "torn" transaction. Please see the user guide section + * "Transaction tear-offs" to learn more about this feature. + * + * The [availableComponents] property is used for calculation of the transaction's [MerkleTree], which is in + * turn used to derive the ID hash. */ interface TraversableTransaction { val inputs: List @@ -29,29 +31,41 @@ interface TraversableTransaction { val timestamp: Timestamp? /** - * Traversing transaction fields with a list function over transaction contents. Used for leaves hashes calculation - * and user provided filtering and checking of filtered transaction. + * Returns a flattened list of all the components that are present in the transaction, in the following order: + * + * - Each input that is present + * - Each attachment that is present + * - Each output that is present + * - Each command that is present + * - The notary [Party], if present + * - Each required signer ([mustSign]) that is present + * - The type of the transaction, if present + * - The timestamp of the transaction, if present */ - // We may want to specify our own behaviour on certain tx fields. - // Like if we include them at all, what to do with null values, if we treat list as one or not etc. for building - // torn-off transaction and id calculation. - val traversableList: List + val availableComponents: List get() { - val traverseList = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList() - if (notary != null) traverseList.add(notary!!) - traverseList.addAll(mustSign) - if (type != null) traverseList.add(type!!) - if (timestamp != null) traverseList.add(timestamp!!) - return traverseList + // We may want to specify our own behaviour on certain tx fields. + // Like if we include them at all, what to do with null values, if we treat list as one or not etc. for building + // torn-off transaction and id calculation. + val result = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList() + notary?.let { result += it } + result.addAll(mustSign) + type?.let { result += it } + timestamp?.let { result += it } + return result } - // Calculation of all leaves hashes that are needed for calculation of transaction id and partial Merkle branches. - fun calculateLeavesHashes(): List = traversableList.map { serializedHash(it) } + /** + * Calculate the hashes of the sub-components of the transaction, that are used to build its Merkle tree. + * The root of the tree is the transaction identifier. The tree structure is helpful for privacy, please + * see the user-guide section "Transaction tear-offs" to learn more about this topic. + */ + val availableComponentHashes: List get() = availableComponents.map { serializedHash(it) } } /** * Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every - * field from WireTransaction can be used in PartialMerkleTree calculation. + * field from [WireTransaction] can be used in [PartialMerkleTree] calculation. */ class FilteredLeaves( override val inputs: List, @@ -73,7 +87,7 @@ class FilteredLeaves( * @returns false if no elements were matched on a structure or checkingFun returned false. */ fun checkWithFun(checkingFun: (Any) -> Boolean): Boolean { - val checkList = traversableList.map { checkingFun(it) } + val checkList = availableComponents.map { checkingFun(it) } return (!checkList.isEmpty()) && checkList.all { true } } } @@ -99,8 +113,8 @@ class FilteredTransaction private constructor( filtering: (Any) -> Boolean ): FilteredTransaction { val filteredLeaves = wtx.filterWithFun(filtering) - val merkleTree = wtx.getMerkleTree() - val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.calculateLeavesHashes()) + val merkleTree = wtx.merkleTree + val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.availableComponentHashes) return FilteredTransaction(merkleTree.hash, filteredLeaves, pmt) } } @@ -110,17 +124,9 @@ class FilteredTransaction private constructor( */ @Throws(MerkleTreeException::class) fun verify(): Boolean { - val hashes: List = filteredLeaves.calculateLeavesHashes() + val hashes: List = filteredLeaves.availableComponentHashes if (hashes.isEmpty()) throw MerkleTreeException("Transaction without included leaves.") return partialMerkleTree.verify(rootHash, hashes) } - - /** - * Runs verification of Partial Merkle Branch against [rootHash]. Checks filteredLeaves with provided checkingFun. - */ - @Throws(MerkleTreeException::class) - fun verifyWithFunction(checkingFun: (Any) -> Boolean): Boolean { - return verify() && filteredLeaves.checkWithFun { checkingFun(it) } - } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index 999f4ff41c..1897bc070b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -42,7 +42,7 @@ class WireTransaction( @Volatile @Transient private var cachedBytes: SerializedBytes? = null val serialized: SerializedBytes get() = cachedBytes ?: serialize().apply { cachedBytes = this } - override val id: SecureHash by lazy { getMerkleTree().hash } + override val id: SecureHash by lazy { merkleTree.hash } companion object { fun deserialize(data: SerializedBytes, kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction { @@ -94,9 +94,7 @@ class WireTransaction( /** * Builds whole Merkle tree for a transaction. */ - fun getMerkleTree(): MerkleTree { - return MerkleTree.getMerkleTree(calculateLeavesHashes()) - } + val merkleTree: MerkleTree by lazy { MerkleTree.getMerkleTree(availableComponentHashes) } /** * Construction of partial transaction from WireTransaction based on filtering.