# Write up - EPF Progress (0625)
## Introduce new function: `ProofByFieldIndex`
> You can see a draft PR in [this link](https://github.com/OffchainLabs/prysm/pull/15443).
As I mentioned at the [last dev update](https://hackmd.io/@junsong/S1gNy3INgx), there are some redundant code that can be simplified (e.g. `CurrentSyncCommitteeProof`, `NextSyncCommitteeProof` and `FinalizedRootProof`). `ProofByFieldIndex` is introduced to 1) make logic common and 2) generalize the proof generation in the level of the fields in `BeaconState`.
```go
// ProofByFieldIndex constructs proofs for given field index with lock acquisition.
func (b *BeaconState) ProofByFieldIndex(ctx context.Context, f types.FieldIndex) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
return b.proofByFieldIndex(ctx, f)
}
// proofByFieldIndex constructs proofs for given field index.
// Important: it is assumed that beacon state mutex is locked when calling this method.
func (b *BeaconState) proofByFieldIndex(ctx context.Context, f types.FieldIndex) ([][]byte, error) {
err := b.validateFieldIndex(f)
if err != nil {
return nil, err
}
if err := b.initializeMerkleLayers(ctx); err != nil {
return nil, err
}
if err := b.recomputeDirtyFields(ctx); err != nil {
return nil, err
}
return trie.ProofFromMerkleLayers(b.merkleLayers, f.RealPosition()), nil
}
```
One notable thing is about the **race condition**. In case of `FinalizedRootProof`, we need to access to `epoch` in `Checkpoint` container. So the critical section protected by the mutex should also cover this access. There's a comment for `proofByFieldIndex`: _it is assumed that beacon state mutex is locked when calling this method_.
## More fine-grained proof generation
Although `ProofByFieldIndex` is introduced, this cannot cover all proof generation. For example, what if we want to prove the 42nd validator in the validator registry? Or `FinalizedRootProof` is also a great example to explain this issue. More generally speaking, we should find a way to prove for an item with generalized index more than `128`. `BeaconState` now has 37 fields, so the first field (which is `genesis_time`) has `64` as the generalized index.
After thinking an hour, I'd like to suggest to divide the proof generation into two subproblems.
1. Proof generation using `ProofByFieldIndex`: This step will cover the field that can **directly** access from the `BeaconState`. For example, in case of `FinalizedRootProof`, this will cover `finalized_checkpoint`.
- This logic relies on `merkleLayers` that `BeaconState` holds during its lifetime. It is quite well-optimized by only computing dirty fields, so we can enjoy this optimization.
2. Proof generation of more deeper item: In this case, we need to reconstruct a merkle tree to generate the proof. I think reconstruction is okay in terms of performance as it can be further optimized by caching if we really need a proof for specific item often.
Here's the pseudocode for this strategy. Some functions like `calculateGeneralizedIndex` should be implemented.
```go
func (b *BeaconState) ProofByPath(ctx context.Context, path []types.PathElement) ([][]byte, error) {
gIndex, err := calculateGeneralizedIndex(path)
if err != nil {
return nil, err
}
return ProofByGeneralizedIndex(ctx, gIndex)
}
func (b *BeaconState) ProofByGeneralizedIndex(ctx context.Context, gIndex types.GeneralizedIndex) ([][]byte, error) {
b.lock.Lock()
defer b.lock.Unlock()
fieldIndex := getFieldIndexFromGeneralizedIndex(gIndex)
branchProof, err := b.proofByFieldIndex(ctx, fieldIndex)
if err != nil {
return nil, err
}
tree, err := reconstructMerkleTree(gIndex)
if err != nil {
return nil, err
}
leafProof, err := tree.MerkleProof(index)
if err != nil {
return nil, err
}
proof := make([][]byte, 0)
proof = append(proof, leafProof...)
proof = append(proof, branchProof...)
return proof, nil
}
```
Let's come back to the first question. What if we want to prove the 42nd validator in the validator registry? User can call `ProofByPath` with the path: `["validators", 42]`.
## Other resources
- [`fastssz`](https://github.com/ferranbt/fastssz) (which Prysm also depends on) provides more general way to generate a proof like following:
```go
// Prove returns a list of sibling values and hashes needed
// to compute the root hash for a given general index.
func (n *Node) Prove(index int) (*Proof, error) {
pathLen := getPathLength(index)
proof := &Proof{Index: index}
hashes := make([][]byte, 0, pathLen)
cur := n
for i := pathLen - 1; i >= 0; i-- {
var siblingHash []byte
if isRight := getPosAtLevel(index, i); isRight {
siblingHash = hashNode(cur.left)
cur = cur.right
} else {
siblingHash = hashNode(cur.right)
cur = cur.left
}
hashes = append([][]byte{siblingHash}, hashes...)
if cur == nil {
return nil, errors.New("Node not found in tree")
}
}
proof.Hashes = hashes
if cur.value == nil {
// This is an intermediate node without a value; add the hash to it so that we're providing a suitable leaf value.
cur.value = hashNode(cur)
}
proof.Leaf = cur.value
return proof, nil
}
```