-
Notifications
You must be signed in to change notification settings - Fork 6
Publish milestone together with virtual transactions #188 #200
base: dev-vtx
Are you sure you want to change the base?
Changes from 2 commits
c2bd10e
752f17d
39fb050
bc91d28
6290a45
588dd0f
1368ef6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -4,7 +4,10 @@ | |||
import net.helix.pendulum.controllers.TransactionViewModel; | ||||
import net.helix.pendulum.model.Hash; | ||||
import net.helix.pendulum.model.HashFactory; | ||||
import net.helix.pendulum.utils.bundle.BundleUtils; | ||||
import org.bouncycastle.util.encoders.Hex; | ||||
import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | ||||
|
||||
import java.io.*; | ||||
import java.nio.ByteBuffer; | ||||
|
@@ -14,6 +17,8 @@ | |||
|
||||
public class Merkle { | ||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Merkle.class); | ||||
|
||||
public static byte[] getMerkleRoot(SpongeFactory.Mode mode, byte[] hash, byte[] bytes, int offset, final int indexIn, int size) { | ||||
int index = indexIn; | ||||
final Sponge sha3 = SpongeFactory.create(mode); | ||||
|
@@ -55,6 +60,44 @@ public static List<List<Hash>> buildMerkleKeyTree(String seed, int pubkeyDepth, | |||
} | ||||
|
||||
|
||||
public static List<byte[]> buildMerkleTransactionTree(List<Hash> leaves, Hash milestoneHash, Hash address){ | ||||
if (leaves.isEmpty()) { | ||||
leaves.add(Hash.NULL_HASH); | ||||
} | ||||
byte[] buffer; | ||||
Sponge sha3 = SpongeFactory.create(SpongeFactory.Mode.S256); | ||||
int depth = (int) Math.ceil(Math.sqrt(leaves.size())); | ||||
int row = 1; | ||||
List<byte[]> virtualTransactionList = new ArrayList<byte[]>(); | ||||
while (leaves.size() > 1) { | ||||
List<Hash> nextKeys = Arrays.asList(new Hash[(leaves.size() / 2)]); | ||||
for (int i = 0; i < nextKeys.size(); i++) { | ||||
if (areLeavesNull(leaves, i)) continue; | ||||
sha3.reset(); | ||||
Hash k1 = leaves.get(i * 2); | ||||
Hash k2 = leaves.get(i * 2 + 1); | ||||
buffer = computeParentHash(sha3, k1, k2); | ||||
Hash parentHash = HashFactory.TRANSACTION.create(buffer); | ||||
nextKeys.set(i, parentHash); | ||||
// if( i == 0) add only one virtual transaction for each level | ||||
virtualTransactionList.add(createVirtualTransaction(k1, k2, row, milestoneHash, address, parentHash)); | ||||
} | ||||
leaves = nextKeys; | ||||
row++; | ||||
} | ||||
return virtualTransactionList; | ||||
} | ||||
|
||||
private static byte[] computeParentHash(Sponge sha3, Hash k1, Hash k2) { | ||||
byte[] buffer; | ||||
buffer = Arrays.copyOfRange(k1 == null ? Hex.decode("0000000000000000000000000000000000000000000000000000000000000000") : k1.bytes(), 0, 32); | ||||
sha3.absorb(buffer, 0, buffer.length); | ||||
buffer = Arrays.copyOfRange(k2 == null ? Hex.decode("0000000000000000000000000000000000000000000000000000000000000000") : k2.bytes(), 0, 32); | ||||
cristina-vasiu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
sha3.absorb(buffer, 0, buffer.length); | ||||
sha3.squeeze(buffer, 0, buffer.length); | ||||
return buffer; | ||||
} | ||||
|
||||
public static List<List<Hash>> buildMerkleTree(List<Hash> leaves){ | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are the pros and cons of having There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends on what we want to use it: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The trick is to use the compact array representation of a binary tree. If you have say 8 leaves, you pack it into array of size
The level There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be more precise: children of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest introducing a separate interface MerkleTree and MerkleTreeImpl, where this builder can be moved. Also, I don't think that we should replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no padding to merkle tree leaves, it just take the leaves and pair them 2 by 2 and compute the hashes, when there is an odd number of leaves the last one is not taken into consideration.
|
||||
if (leaves.isEmpty()) { | ||||
leaves.add(Hash.NULL_HASH); | ||||
|
@@ -70,18 +113,11 @@ public static List<List<Hash>> buildMerkleTree(List<Hash> leaves){ | |||
// Take two following keys (i=0: (k0,k1), i=1: (k2,k3), ...) and get one crypto of them | ||||
cristina-vasiu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
List<Hash> nextKeys = Arrays.asList(new Hash[(leaves.size() / 2)]); | ||||
for (int i = 0; i < nextKeys.size(); i++) { | ||||
if (leaves.get(i * 2) == null && leaves.get(i * 2 + 1) == null) { | ||||
// leave the combined key null as well | ||||
continue; | ||||
} | ||||
if (areLeavesNull(leaves, i)) continue; | ||||
sha3.reset(); | ||||
Hash k1 = leaves.get(i * 2); | ||||
Hash k2 = leaves.get(i * 2 + 1); | ||||
buffer = Arrays.copyOfRange(k1 == null ? Hex.decode("0000000000000000000000000000000000000000000000000000000000000000") : k1.bytes(), 0, 32); | ||||
sha3.absorb(buffer, 0, buffer.length); | ||||
buffer = Arrays.copyOfRange(k2 == null ? Hex.decode("0000000000000000000000000000000000000000000000000000000000000000") : k2.bytes(), 0, 32); | ||||
sha3.absorb(buffer, 0, buffer.length); | ||||
sha3.squeeze(buffer, 0, buffer.length); | ||||
buffer = computeParentHash(sha3, k1, k2); | ||||
nextKeys.set(i, HashFactory.TRANSACTION.create(buffer)); | ||||
} | ||||
leaves = nextKeys; | ||||
|
@@ -90,6 +126,19 @@ public static List<List<Hash>> buildMerkleTree(List<Hash> leaves){ | |||
return merkleTree; | ||||
} | ||||
|
||||
private static boolean areLeavesNull(List<Hash> leaves, int i) { | ||||
if (leaves.get(i * 2) == null && leaves.get(i * 2 + 1) == null) { | ||||
// leave the combined key null as well | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
|
||||
private static byte[] createVirtualTransaction(Hash branchHash, Hash trunkHash, long merkleIndex, Hash milestoneHash, Hash address, Hash transactionHash) { | ||||
log.debug("New virtual transaction + " + transactionHash + " for milestone: " + milestoneHash + " with merkle index: " + merkleIndex + " [" + branchHash + ", " + trunkHash + " ]"); | ||||
return BundleUtils.createVirtualTransaction(branchHash, trunkHash, merkleIndex, milestoneHash, address); | ||||
} | ||||
|
||||
public static boolean validateMerkleSignature(List<TransactionViewModel> bundleTransactionViewModels, SpongeFactory.Mode mode, Hash validationAddress, int securityLevel, int depth) { | ||||
|
||||
//System.out.println("Validate Merkle Signature"); | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were some concerns from Oliver about using
NULL_HASH
. Is it kosher here or we should use another filler?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding NULL_HASH it can be replaced with other hash (but should exists in tangle), here it means that no tips are found, so their merkle root is NULL_HASH, in tangle it will look like this milestone is directly connected to genesis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why I don't think it is kosher.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it has to be a valid tx_hash, not sure how, without implementing a(nother) genesis tx.