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.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 <T : Any> serializedHash(x: T): SecureHash {
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.
* 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<StateRef>
@ -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<Any>
val availableComponents: List<Any>
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<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
* field from WireTransaction can be used in PartialMerkleTree calculation.
* field from [WireTransaction] can be used in [PartialMerkleTree] calculation.
*/
class FilteredLeaves(
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.
*/
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<SecureHash> = filteredLeaves.calculateLeavesHashes()
val hashes: List<SecureHash> = 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) }
}
}

View File

@ -42,7 +42,7 @@ class WireTransaction(
@Volatile @Transient private var cachedBytes: SerializedBytes<WireTransaction>? = null
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 {
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.
*/
fun getMerkleTree(): MerkleTree {
return MerkleTree.getMerkleTree(calculateLeavesHashes())
}
val merkleTree: MerkleTree by lazy { MerkleTree.getMerkleTree(availableComponentHashes) }
/**
* Construction of partial transaction from WireTransaction based on filtering.