Backward compatible fix for Merkle tree case where a single leaf becomes the tree root (#6895)

This commit is contained in:
Edoardo Ierina
2021-04-12 11:09:39 +01:00
committed by GitHub
parent bde2d2a8c7
commit 241170ffa4
7 changed files with 242 additions and 23 deletions

View File

@ -37,19 +37,19 @@ sealed class MerkleTree {
require(algorithms.size == 1) {
"Cannot build Merkle tree with multiple hash algorithms: $algorithms"
}
val leaves = padWithZeros(allLeavesHashes).map { Leaf(it) }
val leaves = padWithZeros(allLeavesHashes, nodeDigestService.hashAlgorithm == SecureHash.SHA2_256).map { Leaf(it) }
return buildMerkleTree(leaves, nodeDigestService)
}
// If number of leaves in the tree is not a power of 2, we need to pad it with zero hashes.
private fun padWithZeros(allLeavesHashes: List<SecureHash>): List<SecureHash> {
private fun padWithZeros(allLeavesHashes: List<SecureHash>, singleLeafWithoutPadding: Boolean): List<SecureHash> {
var n = allLeavesHashes.size
if (isPow2(n)) return allLeavesHashes
if (isPow2(n) && (n > 1 || singleLeafWithoutPadding)) return allLeavesHashes
val paddedHashes = ArrayList(allLeavesHashes)
val zeroHash = SecureHash.zeroHashFor(paddedHashes[0].algorithm)
while (!isPow2(n++)) {
do {
paddedHashes.add(zeroHash)
}
} while (!isPow2(++n))
return paddedHashes
}

View File

@ -1,33 +1,19 @@
package net.corda.core.crypto
import net.corda.core.crypto.internal.DigestAlgorithmFactory
import org.bouncycastle.crypto.digests.Blake2sDigest
import net.corda.core.internal.BLAKE2s256DigestAlgorithm
import org.junit.Assert.assertArrayEquals
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
class Blake2s256DigestServiceTest {
class BLAKE2s256DigestService : DigestAlgorithm {
override val algorithm = "BLAKE_TEST"
override val digestLength = 32
override fun digest(bytes: ByteArray): ByteArray {
val blake2s256 = Blake2sDigest(null, digestLength, null, "12345678".toByteArray())
blake2s256.reset()
blake2s256.update(bytes, 0, bytes.size)
val hash = ByteArray(digestLength)
blake2s256.doFinal(hash, 0)
return hash
}
}
private val service = DigestService("BLAKE_TEST")
@Before
fun before() {
DigestAlgorithmFactory.registerClass(BLAKE2s256DigestService::class.java.name)
DigestAlgorithmFactory.registerClass(BLAKE2s256DigestAlgorithm::class.java.name)
}
@Test(timeout = 300_000)

View File

@ -0,0 +1,36 @@
package net.corda.core.internal
import net.corda.core.crypto.DigestAlgorithm
import net.corda.core.crypto.SecureHash
import org.bouncycastle.crypto.digests.Blake2sDigest
/**
* A set of custom hash algorithms
*/
open class BLAKE2s256DigestAlgorithm : DigestAlgorithm {
override val algorithm = "BLAKE_TEST"
override val digestLength = 32
protected fun blake2sHash(bytes: ByteArray): ByteArray {
val blake2s256 = Blake2sDigest(null, digestLength, null, "12345678".toByteArray())
blake2s256.reset()
blake2s256.update(bytes, 0, bytes.size)
val hash = ByteArray(digestLength)
blake2s256.doFinal(hash, 0)
return hash
}
override fun digest(bytes: ByteArray): ByteArray = blake2sHash(bytes)
}
class SHA256BLAKE2s256DigestAlgorithm : BLAKE2s256DigestAlgorithm() {
override val algorithm = "SHA256-BLAKE2S256-TEST"
override fun digest(bytes: ByteArray): ByteArray = SecureHash.hashAs(SecureHash.SHA2_256, bytes).bytes
override fun componentDigest(bytes: ByteArray): ByteArray = blake2sHash(bytes)
override fun nonceDigest(bytes: ByteArray): ByteArray = blake2sHash(bytes)
}

View File

@ -18,6 +18,7 @@ fun WireTransaction.accessGroupHashes() = this.groupHashes
fun WireTransaction.accessGroupMerkleRoots() = this.groupsMerkleRoots
fun WireTransaction.accessAvailableComponentHashes() = this.availableComponentHashes
fun WireTransaction.accessAvailableComponentNonces() = this.availableComponentNonces
@Suppress("LongParameterList")
fun createLedgerTransaction(