The initial impl didn't capture the following edge case:
- Tree has 3 (or more) layers + 1 leaf layeri
- Leaf layer last chunk IS full
- Layer 0 last chunk is NOT full
- Layer 1 last chunk is NOT full
- Layer 2 last chunk IS NOT full
In this case, when updating layer 1, we need to use layer 0's old
last hash to update layer 1's old last hash. Same for Layer 2.
The solution is to use logic that checks the *prev* layer when
updating a layer to determine if the old last hash from the prev
layer is needed.
This commit restructures the grow_tree impl to account for this
and simplifies the approach as follows:
1. Read the tree to get num leaf tuples + last hashes in each layer
2. Get the tree extension using the above values + new leaf tuples
2a. Prior to updating the leaf layer, call the function
get_update_leaf_layer_metadata. This function uses existing totals
in the leaf layer, the new total of leaf tuples, and tree params
to calculate how the layer after the leaf layer should be updated.
2b. For each subsequent layer, call the function
get_update_layer_metadata. This function uses the existing totals
in the *prev* layer, the new total of children in the *prev* layer,
and tree params to calculate how the layer should be updated.
3. Grow the tree using the tree extension.
This approach isolates update logic and actual hashing into neat
structured functions, rather than mix the two. This makes the code
easier to follow without needing to keep so much in your head at
one time.