Change:
"application_ratchet_nonce_[L]_[j]" and "application_ratchet_key_[L]_[j]". <del> Concretely, suppose we have the following Secret Tree and ratchet for participant D: </del> <ins> Concretely, consider the Secret Tree shown in secret-tree-example. Client A, B, or C would generate the illustrated values on receiving a message from D with generation equal to 1, having not received a message with generation 0 (e.g., due to out-of-order delivery).  In such a case, the following values would be consumed: The key K1 and nonce N1 used to decrypt the message The application ratchet secrets AR1 and AR0 The tree secrets D, F, G (recall that G is the "encryption_secret" for the epoch) The "epoch_secret", "commit_secret", "psk_secret", and "joiner_secret" for the current epoch Other values may be retained (not consumed): K0 and N0 for decryption of an out-of-order message with generation 0 AR2 for derivation of further message decryption keys and nonces HR0 for protection of handshake messages from D </ins> <del> Then if a client uses key K1 and nonce N1 during epoch n then it must consume (at least) values G, F, D, AR0, AR1, K1, N1 as well as the key schedule secrets used to derive G (the "encryption_secret"), namely "init_secret" of epoch n-1 and "commit_secret", "joiner_secret", "epoch_secret" of epoch n.  The client MAY retain (not consume) the values K0 and N0 to allow for out-of-order delivery, and SHOULD retain AR2 for processing future messages. </del> <ins> E and C for deriving secrets used by senders A, B, and C </ins> 11.

Discussion:
This PR reorganizes the exposition of tree concept to make them read more clearly. A new Paths through the Ratchet Tree section is introduced, which covers direct paths, copaths, and filtered direct paths The Ratchet Tree Nodes section is moved forward, so that the definition of filtered direct paths can refer to the notion of resolutions (see NAME note on ) The path example expanded so that there are blanks in the tree, and so that it shows all three path types (direct, copath, and filtered direct) for every leaf. The section on verifying parent hashes uses the example tree from the path section to illustrate the concepts noted in
Latest commit adds coverage for the last part of . In addition to moving the figure forward, it refactors the explanation to be even more explicit about what must be consumed and what may be kept.
Currently defined only briefly in "RatchetTree Evolution". While that's where it's primarily used, it would fit better in the terminology section, especially if we expand it a little and add an illustration.
The problem here is that the definition of "filtered direct path" depends on the definition of "resolution" which isn't defined until a later section.
Indeed, so proposes moving up the definition of resolution as well.
Add illustration of filtered direct path Add illustration of parent hash validation, including: Leaves with no parent hash Nodes with invalid parent hash Skipping blank nodes Add corresponding derivations to Appendix A Move figure in Section 10.2 up to Section 10 as an overview
NAME Note that I have taken a stab at parts of this in because it came along with some other tree stuff.
Duplicate
Regarding the Tree Hash Including the parentnode in the tree hash which is supposed to be computed from the leaves, makes the definition appear circular to a new reader. Since the situation when you have blank nodes in the middle of a tree but not at the root is a rather complicated, the situation when you could calculate a tree hash using the parentnode is non-obvious. In the description of Tree Hash the document says that the ParentNode object is optional "depending on whether the node is blank or not." This isn't clear specification language. Saying the parentnode object is present if the node is blank or present if the node is not blank would be clear. What if the parent node is also blank? What if the root is blank?

I think an example with and without blank nodes would be appropriate. Explaining the reason why the parentnode is included will help the reader establish a better mental model. Regarding the Parent Hash I still find this section super confusing. A few points Using a tree hash (siblingtreehash) inside the parent hash is non-obvious. There should be some explanation about what property this is providing and that it is a tree hash and not a parent hash. (Honestly it was the third time I read the section that I realized that it was really the tree hash and not the parent hash of the sibling.) Using P, V, and S for Parent, target node, and sibling node is introduced here and never used anywhere else in the document. P for parent and S for sibling make sense. I can't tell what V is supposed to stand for, and it unfortunately could cause people to confuse the vector notation used throughout, but shortly after introducing V for the target node. I propose using "N" for node or "X" instead. There is one reference to the filtered direct path of U. Perhaps this is V, or a child of V? The calculations are sufficiently complicated that I think this warrants a complete, non-trivial, worked example (possibly in an appendix). At some point we should consider providing test vectors in a future document, and this would be an ideal candidate.
Regarding the Tree Hash To be clear, the that is included in the tree hash is the node whose subtree is being hashed. The tree hash doesn't include only the leaves, it includes all the nodes in the tree. If you mean , then yes, I agree the connection is a little subtle. I've tried to explain this a little better. Updated this to be clear that the ParentNode is omitted if and only if the node is blank. Not clear what situation you mean here. I think the specific computations are pretty clearly specified for these cases. Something like this was added in : URL I extended the example and put it in an appendix. Assuming you mean here, hopefully this is addressed by the above notes. Added a note to clarify that this is the tree hash and why. Changed to "D" for "direct path", and explained the semantics. Actually it's the leaf, which is now called "L".

I think the example linked above does a pretty good job. Feedback obviously welcome. Agreed that a test vectors document will be a useful follow-on.
In order to use tree hashes to represent siblings in parent hashes, we had to compute a different tree hash on the right edge of the tree, pretending that the tree is full, in the sense of having leaves. Thus we actually have two tree hashes in play, one used for group agreement on the tree and one used for parent hash inputs. We should consider specifying that the logical tree used by MLS is always full. This change to the specification would not change the computations done by the protocol much, if at all. Thanks to the fact that we no longer generate redundant nodes (), there is never anything non-blank to the right of the rightmost non-blank leaf anyway. So the main impact is to insert some blank nodes between a parent node on the right edge of the tree and its right child -- and all of the algorithms in the specification already skip over such nodes. Likewise, implementations' internal represntations of the tree wouldn't have to change much, since the extra nodes could be "virtual". The only time they would be noticed is when computing tree hashes. We would have to update the description of the extension to say that it only covers the tree up to the rightmost non-blank leaf. It would simplify the specification in a few ways, for example: The "original sibling tree hash" would just entail blanking unmerged leaves, not structure changes. The description of extending and truncating the tree would be much simpler. The main cost would be extra hashes when joining, or when updating a member close to the right edge of the tree. If you have a tree with leaves, then you have to compute tree hashes over blank leaves and their blank parents.
Thanks to NAME and NAME for helping inspire and develop this idea in discussions around
Virtual interim 2022-05-19 NAME notes that the current tree hash is quite tricky to implement, so this would be a useful simplification Doubling the hashes on a fresh tree doesn't sound too intimidating Folks would find more concreteness useful here, interested in PR Could we phrase this as only a change to TreeKEM?

Would be premised on the equivalence of the hashed tree an the tree you operate on
Discussed at 19 May interim: NAME to draft a PR.
Holding a PR until after lands. Some notes on what would need to change: {{ratchet-tree-terminology}} would change from "the unique left-balanced tree with N leaves" to "the unique balanced binary tree with depth , where is the smallest number such that ". {{adding-and-removing-leaves}} would either be removed, or updated so that it talks about increasing or decreasing the depth of the tree (equivalently, doubling or halving the number of leaves). {{add}} would talk about increasing depth instead of adding leaves. {{remove}} would talk about decreasing depth instead of removing leaves. {{ratchet-tree-extension}} would be explicit that it only carries nodes up to the rightmost non-blank leaf. The encoding/decoding algorithms would have to be updated. {{array-based-trees}} and {{link-based-trees}} could probably be simplified. We would need to make sure all figures showed full trees.
I went ahead and filed for this, based on top of . Once that merges, we can update the PR to be a little easier to read. The changes are basically as described above. One additional note: We have actually already incurred the cost to a new joiner of hashing all the extra nodes, because they have to do it for parent hash verification anyway. So this approach is actually a net reduction in the amount of hashing that has to be done, since they don't have to also compute the non-full-tree hashes.
In , we introduced the idea of a "filtered direct path". It would be helpful to have an illustration of how a filtered direct path differs from a direct path.