mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
Filter, build and verify merklized transaction from a wire transaction.
Initial structure for a merkleized transaction, with full Merkle tree building.
This commit is contained in:
parent
de511e41b1
commit
1a8e47fcaa
@ -0,0 +1,93 @@
|
|||||||
|
package com.r3corda.core.transactions
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.Command
|
||||||
|
import com.r3corda.core.crypto.PartialMerkleTree
|
||||||
|
import com.r3corda.core.crypto.SecureHash
|
||||||
|
import com.r3corda.core.crypto.sha256
|
||||||
|
import com.r3corda.core.serialization.serialize
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/* Creation and verification of a Merkle Tree for a Wire Transaction
|
||||||
|
* Tree should be the same no matter the ordering of outputs, inputs, attachments and commands. */
|
||||||
|
|
||||||
|
/* Transaction is split into following blocks:
|
||||||
|
inputs, outputs, commands, attachments' refs
|
||||||
|
If a row in a tree has odd number of elements - the final hash is hashed with itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun SecureHash.hashConcat(other: SecureHash) = (this.bits + other.bits).sha256()
|
||||||
|
|
||||||
|
class MerkleTransaction(
|
||||||
|
val merkleRoot: SecureHash, //todo that should be in a wire tx? not with PMT and filtered commands
|
||||||
|
val filteredCommands : List<Command>, //todo + <Command> do we want to also filter something else than commands?
|
||||||
|
val partialMerkleTree: PartialMerkleTree
|
||||||
|
){
|
||||||
|
companion object {
|
||||||
|
fun buildMerkleTransaction(wtx: WireTransaction, filterFunction: (Command) -> Boolean): MerkleTransaction {
|
||||||
|
val merkleTree: List<SecureHash> = buildMerkleTree(wtx) //todo change
|
||||||
|
val merkleRoot = merkleTree.last()
|
||||||
|
|
||||||
|
val allLeavesHashes: MutableList<SecureHash> = ArrayList()
|
||||||
|
getTransactionBlocks(wtx).mapTo(allLeavesHashes, { it.sha256() })
|
||||||
|
val filteredCommands: MutableList<Command> = ArrayList()
|
||||||
|
val includeLeaves: MutableList<Boolean> = ArrayList()
|
||||||
|
wtx.commands.forEach {
|
||||||
|
val include = filterFunction(it)
|
||||||
|
if(include) filteredCommands.add(it)
|
||||||
|
includeLeaves.add(include)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pmt = PartialMerkleTree.build(includeLeaves, allLeavesHashes)
|
||||||
|
return MerkleTransaction(merkleRoot, filteredCommands, pmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function that splits the transaction into serialized blocks.
|
||||||
|
Blocks: inputs, outputs, attachments, commands. */
|
||||||
|
private fun getTransactionBlocks(wtx: WireTransaction) : MutableList<ByteArray> {
|
||||||
|
val blocks: MutableList<ByteArray> = ArrayList()
|
||||||
|
val toBlockList = listOf(wtx.inputs, wtx.outputs, wtx.attachments, wtx.commands) //todo ordering
|
||||||
|
toBlockList.flatMapTo(blocks, { listOf(it.serialize().bits) } )
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start building a Merkle tree from the transaction.
|
||||||
|
Calls helper tailrecursive function with an accumulator and initial hashedBlocks */
|
||||||
|
fun buildMerkleTree(wtx: WireTransaction): MutableList<SecureHash>{
|
||||||
|
val blocks = getTransactionBlocks(wtx)
|
||||||
|
val hashedBlocks: MutableList<SecureHash> = ArrayList()
|
||||||
|
blocks.mapTo(hashedBlocks, { it.sha256() })
|
||||||
|
val merkleTreeList = ArrayList<SecureHash>()
|
||||||
|
merkleTreeList.addAll(hashedBlocks)
|
||||||
|
buildMerkleTree(merkleTreeList, hashedBlocks)
|
||||||
|
return merkleTreeList
|
||||||
|
}
|
||||||
|
|
||||||
|
tailrec fun buildMerkleTree(
|
||||||
|
resultHashes: MutableList<SecureHash>,
|
||||||
|
lastHashList: List<SecureHash>){
|
||||||
|
if(lastHashList.size <= 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
val newLevelHashes: MutableList<SecureHash> = ArrayList()
|
||||||
|
var i = 0
|
||||||
|
while(i < lastHashList.size){
|
||||||
|
val left = lastHashList[i]
|
||||||
|
//If there is an odd number of elements, the last element is hashed with itself
|
||||||
|
val right = lastHashList[Math.min(i+1, lastHashList.size - 1)]
|
||||||
|
val combined = left.hashConcat(right)
|
||||||
|
resultHashes.add(combined)
|
||||||
|
newLevelHashes.add(combined)
|
||||||
|
i+=2
|
||||||
|
}
|
||||||
|
buildMerkleTree(resultHashes, newLevelHashes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo exception
|
||||||
|
fun verify():Boolean{
|
||||||
|
val hashes: List<SecureHash> = filteredCommands.map { it.serialize().sha256() }
|
||||||
|
return partialMerkleTree.verify(hashes, merkleRoot)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user