-
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?
Conversation
Take changes from upstream repo
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.
Major things that we should discuss perhaps:
-- Filler for the empty leaves (if NULL_HASH
is good)
-- List<List<Hash>>
vs List<Hash>
for the Merkle tree representation (and the depth issue)
@@ -55,6 +60,44 @@ | |||
} | |||
|
|||
|
|||
public static List<byte[]> buildMerkleTransactionTree(List<Hash> leaves, Hash milestoneHash, Hash address){ | |||
if (leaves.isEmpty()) { | |||
leaves.add(Hash.NULL_HASH); |
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.
in tangle it will look like this milestone is directly connected to genesis
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.
can be replaced with other hash (but should exists in tangle)
yes it has to be a valid tx_hash, not sure how, without implementing a(nother) genesis tx.
private void createAndBroadcastVirtualTransactions(byte[] tipsBytes, List<String> milestoneBundle) { | ||
TransactionViewModel milestone = getFirstTransactionFromBundle(milestoneBundle); | ||
if (milestone != null) { | ||
List<Hash> tips = extractTipsFromData(tipsBytes); |
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.
Do we have guarantees here that the tips are solid?
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.
No, milestone for current node is not created yet, so tips are not solid (probably the other milestones from other nodes are in the same place).
I thought that we should broadcast virtual transaction before broadcasting the milestone.
Also here all virtual transaction are broadcasted (should be limitted)
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 comment
The reason will be displayed to describe this comment to others. Learn more.
What are the pros and cons of having List<List<Hash>>
for the tree representation against a flat array (ArrayList<Hash>
) ?
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.
It depends on what we want to use it:
List<List> is faster if you need to access hashes for a certain level of Merkle tree, but it's slower on traverse all nodes (without any order), or traverse from leves to root.
List is faster if all nodes should be traversed and if merkle level is not relevant
In most cases I saw that only root is used, so maybe we can change the calling methods to call getMerkleRoot instead of the entire tree.
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.
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 15 = (2*8) - 1
in the following way:
- leaves have indices 7, 8, ..., 14
- next level indices 3,4,5,6
- next level 1,2
- root 0
The level i
has indices from 2^i-1
to 2^{i+1}-2
The benefit of having this representation is that you initially allocate the full size and fill evertyhing with null
and as the data becomes available, you can quickly navigate to parents and children by simple bit shifts of the indices, at the same time preserving the order at all levels.
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.
To be more precise: children of a[i]
are a[2*i+1]
and a[2*i+2]
, parent of a[i]
is a[(i-1) >> 1]
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.
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 null
with NULL_HASH
at all levels, it should be used only for padding the leaves list so that it's size is an integer of the form 2^i
for some i
.
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 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.
List<Hash> nextKeys = Arrays.asList(new Hash[(leaves.size() / 2)]); |
No description provided.