Minor: naming and doc tweaks in Merkle tree code.

This commit is contained in:
Mike Hearn 2017-02-18 16:30:22 +00:00
parent b8942a2cc9
commit 8b1864e07c
2 changed files with 40 additions and 36 deletions

View File

@ -2,11 +2,9 @@ package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.createKryo import net.corda.core.serialization.createKryo
import net.corda.core.serialization.extendKryoHash import net.corda.core.serialization.extendKryoHash
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.util.*
fun <T : Any> serializedHash(x: T): SecureHash { fun <T : Any> serializedHash(x: T): SecureHash {
val kryo = extendKryoHash(createKryo()) // Dealing with HashMaps inside states. val kryo = extendKryoHash(createKryo()) // Dealing with HashMaps inside states.
@ -14,9 +12,13 @@ fun <T : Any> serializedHash(x: T): SecureHash {
} }
/** /**
* Interface implemented by WireTransaction and FilteredLeaves. * Implemented by [WireTransaction] and [FilteredLeaves]. A TraversableTransaction allows you to iterate
* Property traversableList assures that we always calculate hashes in the same order, lets us define which * over the flattened components of the underlying transaction structure, taking into account that some
* fields of WireTransaction will be included in id calculation or partial merkle tree building. * 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 { interface TraversableTransaction {
val inputs: List<StateRef> val inputs: List<StateRef>
@ -29,29 +31,41 @@ interface TraversableTransaction {
val timestamp: Timestamp? val timestamp: Timestamp?
/** /**
* Traversing transaction fields with a list function over transaction contents. Used for leaves hashes calculation * Returns a flattened list of all the components that are present in the transaction, in the following order:
* and user provided filtering and checking of filtered transaction. *
* - 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. val availableComponents: List<Any>
// 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<Any>
get() { get() {
val traverseList = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList() // We may want to specify our own behaviour on certain tx fields.
if (notary != null) traverseList.add(notary!!) // Like if we include them at all, what to do with null values, if we treat list as one or not etc. for building
traverseList.addAll(mustSign) // torn-off transaction and id calculation.
if (type != null) traverseList.add(type!!) val result = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList()
if (timestamp != null) traverseList.add(timestamp!!) notary?.let { result += it }
return traverseList 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<SecureHash> = 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<SecureHash> get() = availableComponents.map { serializedHash(it) }
} }
/** /**
* Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every * 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( class FilteredLeaves(
override val inputs: List<StateRef>, override val inputs: List<StateRef>,
@ -73,7 +87,7 @@ class FilteredLeaves(
* @returns false if no elements were matched on a structure or checkingFun returned false. * @returns false if no elements were matched on a structure or checkingFun returned false.
*/ */
fun checkWithFun(checkingFun: (Any) -> Boolean): Boolean { fun checkWithFun(checkingFun: (Any) -> Boolean): Boolean {
val checkList = traversableList.map { checkingFun(it) } val checkList = availableComponents.map { checkingFun(it) }
return (!checkList.isEmpty()) && checkList.all { true } return (!checkList.isEmpty()) && checkList.all { true }
} }
} }
@ -99,8 +113,8 @@ class FilteredTransaction private constructor(
filtering: (Any) -> Boolean filtering: (Any) -> Boolean
): FilteredTransaction { ): FilteredTransaction {
val filteredLeaves = wtx.filterWithFun(filtering) val filteredLeaves = wtx.filterWithFun(filtering)
val merkleTree = wtx.getMerkleTree() val merkleTree = wtx.merkleTree
val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.calculateLeavesHashes()) val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.availableComponentHashes)
return FilteredTransaction(merkleTree.hash, filteredLeaves, pmt) return FilteredTransaction(merkleTree.hash, filteredLeaves, pmt)
} }
} }
@ -110,17 +124,9 @@ class FilteredTransaction private constructor(
*/ */
@Throws(MerkleTreeException::class) @Throws(MerkleTreeException::class)
fun verify(): Boolean { fun verify(): Boolean {
val hashes: List<SecureHash> = filteredLeaves.calculateLeavesHashes() val hashes: List<SecureHash> = filteredLeaves.availableComponentHashes
if (hashes.isEmpty()) if (hashes.isEmpty())
throw MerkleTreeException("Transaction without included leaves.") throw MerkleTreeException("Transaction without included leaves.")
return partialMerkleTree.verify(rootHash, hashes) 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) }
}
} }

View File

@ -42,7 +42,7 @@ class WireTransaction(
@Volatile @Transient private var cachedBytes: SerializedBytes<WireTransaction>? = null @Volatile @Transient private var cachedBytes: SerializedBytes<WireTransaction>? = null
val serialized: SerializedBytes<WireTransaction> get() = cachedBytes ?: serialize().apply { cachedBytes = this } val serialized: SerializedBytes<WireTransaction> get() = cachedBytes ?: serialize().apply { cachedBytes = this }
override val id: SecureHash by lazy { getMerkleTree().hash } override val id: SecureHash by lazy { merkleTree.hash }
companion object { companion object {
fun deserialize(data: SerializedBytes<WireTransaction>, kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction { fun deserialize(data: SerializedBytes<WireTransaction>, kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction {
@ -94,9 +94,7 @@ class WireTransaction(
/** /**
* Builds whole Merkle tree for a transaction. * Builds whole Merkle tree for a transaction.
*/ */
fun getMerkleTree(): MerkleTree { val merkleTree: MerkleTree by lazy { MerkleTree.getMerkleTree(availableComponentHashes) }
return MerkleTree.getMerkleTree(calculateLeavesHashes())
}
/** /**
* Construction of partial transaction from WireTransaction based on filtering. * Construction of partial transaction from WireTransaction based on filtering.