mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-08 18:51:19 +00:00
d55757500f
* Initial spec rewrite * Finish adding merkle tree implementation * Last bits * Move reverse function * Add comments * Add deposit tree snapshot * Add deposit tree * Add comments + cleanup * Fixes * Add missing errors * Small fixes * Add unhandled error * Cleanup * Fix unsafe file.Close * Add missing comments * Small fixes * Address some of deepSource' compaints * Add depositCount check * Add finalizedDeposit check * Replace pointer magic with copy() * Add test for slice reversal * add back bytes method * Add package level description * Remove zerohash gen and add additional checks * Add additional comments * Small lint fixes * Forgot an error * Small fixes * Move Uint64ToBytesLittleEndian32 + test * Fix uint subtraction issue * Move mixInLength below error handling * Fix * Fix deposit root * integrate 4881 * edits * added in deposit tree fetcher * add file * Add remaining fetcher functions * Add new file for inserter functions * Fixes and additional funcs * Cleanup * Add * Graph * pushed up edits * fix up * Updates * Add EIP4881 toggle flag * Add interfaces * Fix tests * More changes * Fix * Remove generated graph * Fix spacing * Changes * Fixes * Changes * Test Fix * gaz * Fix a couple tests * Fix last tests * define protos * proto methods * pushed * regen * Add proto funcs * builds * pushin up * Fix and cleanup * Fix spectest * General cleanup * add 4881 to e2e * Remove debug statements + remove test skip * Implement first set of missing methods * Replace Zerohashes + cleanup * gazelle * fmt * Put back defensive check * Add error logs * InsertFinalizedDeposits: return an error * Remove logging * Radek' Review * Lint fixes * build * Remove cancel * Update beacon-chain/deterministic-genesis/service.go Co-authored-by: Raul Jordan <raul@prysmaticlabs.com> * Update beacon-chain/cache/depositsnapshot/deposit_inserter.go Co-authored-by: Raul Jordan <raul@prysmaticlabs.com> * Cleanup * Fix panic when DepositSnapshot is nil on init * Gofmt * Fix RootEquivalence test * Gofmt * Add missing comments * Nishant' review * Add Insert benchmarks * fix up copy method * Fix deep copy * Fix conflicts * Return error * Fix linter issues * add in migration logic * Cleanup + tests * fix * Fix incorrect index in test * Fix linter * Gofmt * fix it * fixes for off by 1 * gaz * fix cast * fix it * remove ErrZeroIndex * Fix merkle_tree_test * add fallback * add fix for insertion bug * add many fixes * fix empty snapshot * clean up * use feature * remove check * fix failing tests * skip it * fix test * fix test again * fix for the last time * Apply suggestions from code review Co-authored-by: Radosław Kapka <rkapka@wp.pl> * fix it * remove cancel * fix for voting * addressing more comments * fix err * potuz's review * one more test * fix bad test * make 4881 part of dev mode * add workaround for new trie * comment * preston's review * james's review * add comment * james review * preston's review * remove skipped test * gaz --------- Co-authored-by: rauljordan <raul@prysmaticlabs.com> Co-authored-by: nisdas <nishdas93@gmail.com> Co-authored-by: Radosław Kapka <rkapka@wp.pl> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
191 lines
6.1 KiB
Go
191 lines
6.1 KiB
Go
// Package depositsnapshot implements the EIP-4881 standard for minimal sparse Merkle tree.
|
|
// The format proposed by the EIP allows for the pruning of deposits that are no longer needed to participate fully in consensus.
|
|
// Full EIP-4881 specification can be found here: https://eips.ethereum.org/EIPS/eip-4881
|
|
package depositsnapshot
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/v4/math"
|
|
protodb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
)
|
|
|
|
var (
|
|
// ErrEmptyExecutionBlock occurs when the execution block is nil.
|
|
ErrEmptyExecutionBlock = errors.New("empty execution block")
|
|
// ErrInvalidSnapshotRoot occurs when the snapshot root does not match the calculated root.
|
|
ErrInvalidSnapshotRoot = errors.New("snapshot root is invalid")
|
|
// ErrInvalidDepositCount occurs when the value for mix in length is 0.
|
|
ErrInvalidDepositCount = errors.New("deposit count should be greater than 0")
|
|
// ErrInvalidIndex occurs when the index is less than the number of finalized deposits.
|
|
ErrInvalidIndex = errors.New("index should be greater than finalizedDeposits - 1")
|
|
// ErrTooManyDeposits occurs when the number of deposits exceeds the capacity of the tree.
|
|
ErrTooManyDeposits = errors.New("number of deposits should not be greater than the capacity of the tree")
|
|
)
|
|
|
|
// DepositTree is the Merkle tree representation of deposits.
|
|
type DepositTree struct {
|
|
tree MerkleTreeNode
|
|
depositCount uint64 // number of deposits in the tree, reference implementation calls this mix_in_length.
|
|
finalizedExecutionBlock executionBlock
|
|
}
|
|
|
|
type executionBlock struct {
|
|
Hash [32]byte
|
|
Depth uint64
|
|
}
|
|
|
|
// NewDepositTree creates an empty deposit tree.
|
|
func NewDepositTree() *DepositTree {
|
|
var leaves [][32]byte
|
|
merkle := create(leaves, DepositContractDepth)
|
|
return &DepositTree{
|
|
tree: merkle,
|
|
depositCount: 0,
|
|
finalizedExecutionBlock: executionBlock{},
|
|
}
|
|
}
|
|
|
|
// GetSnapshot returns a deposit tree snapshot.
|
|
func (d *DepositTree) GetSnapshot() (DepositTreeSnapshot, error) {
|
|
var finalized [][32]byte
|
|
depositCount, finalized := d.tree.GetFinalized(finalized)
|
|
return fromTreeParts(finalized, depositCount, d.finalizedExecutionBlock)
|
|
}
|
|
|
|
// fromSnapshot returns a deposit tree from a deposit tree snapshot.
|
|
func fromSnapshot(snapshot DepositTreeSnapshot) (*DepositTree, error) {
|
|
root, err := snapshot.CalculateRoot()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if snapshot.depositRoot != root {
|
|
return nil, ErrInvalidSnapshotRoot
|
|
}
|
|
if snapshot.depositCount >= math.PowerOf2(uint64(DepositContractDepth)) {
|
|
return nil, ErrTooManyDeposits
|
|
}
|
|
tree, err := fromSnapshotParts(snapshot.finalized, snapshot.depositCount, DepositContractDepth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DepositTree{
|
|
tree: tree,
|
|
depositCount: snapshot.depositCount,
|
|
finalizedExecutionBlock: snapshot.executionBlock,
|
|
}, nil
|
|
}
|
|
|
|
// Finalize marks a deposit as finalized.
|
|
func (d *DepositTree) Finalize(eth1DepositIndex int64, executionHash common.Hash, executionNumber uint64) error {
|
|
var blockHash [32]byte
|
|
copy(blockHash[:], executionHash[:])
|
|
d.finalizedExecutionBlock = executionBlock{
|
|
Hash: blockHash,
|
|
Depth: executionNumber,
|
|
}
|
|
depositCount := uint64(eth1DepositIndex + 1)
|
|
_, err := d.tree.Finalize(depositCount, DepositContractDepth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// getProof returns the deposit tree proof.
|
|
func (d *DepositTree) getProof(index uint64) ([32]byte, [][32]byte, error) {
|
|
if d.depositCount <= 0 {
|
|
return [32]byte{}, nil, ErrInvalidDepositCount
|
|
}
|
|
finalizedDeposits, _ := d.tree.GetFinalized([][32]byte{})
|
|
if finalizedDeposits != 0 {
|
|
finalizedDeposits = finalizedDeposits - 1
|
|
}
|
|
if index <= finalizedDeposits {
|
|
return [32]byte{}, nil, ErrInvalidIndex
|
|
}
|
|
leaf, proof := generateProof(d.tree, index, DepositContractDepth)
|
|
var mixInLength [32]byte
|
|
copy(mixInLength[:], bytesutil.Uint64ToBytesLittleEndian32(d.depositCount))
|
|
proof = append(proof, mixInLength)
|
|
return leaf, proof, nil
|
|
}
|
|
|
|
// getRoot returns the root of the deposit tree.
|
|
func (d *DepositTree) getRoot() [32]byte {
|
|
var enc [32]byte
|
|
binary.LittleEndian.PutUint64(enc[:], d.depositCount)
|
|
|
|
root := d.tree.GetRoot()
|
|
return hash.Hash(append(root[:], enc[:]...))
|
|
}
|
|
|
|
// pushLeaf adds a new leaf to the tree.
|
|
func (d *DepositTree) pushLeaf(leaf [32]byte) error {
|
|
var err error
|
|
d.tree, err = d.tree.PushLeaf(leaf, DepositContractDepth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.depositCount++
|
|
return nil
|
|
}
|
|
|
|
// Insert is defined as part of MerkleTree interface and adds a new leaf to the tree.
|
|
func (d *DepositTree) Insert(item []byte, _ int) error {
|
|
var leaf [32]byte
|
|
copy(leaf[:], item[:32])
|
|
return d.pushLeaf(leaf)
|
|
}
|
|
|
|
// HashTreeRoot is defined as part of MerkleTree interface and calculates the hash tree root.
|
|
func (d *DepositTree) HashTreeRoot() ([32]byte, error) {
|
|
root := d.getRoot()
|
|
if root == [32]byte{} {
|
|
return [32]byte{}, errors.New("could not retrieve hash tree root")
|
|
}
|
|
return root, nil
|
|
}
|
|
|
|
// NumOfItems is defined as part of MerkleTree interface and returns the number of deposits in the tree.
|
|
func (d *DepositTree) NumOfItems() int {
|
|
return int(d.depositCount)
|
|
}
|
|
|
|
// MerkleProof is defined as part of MerkleTree interface and generates a merkle proof.
|
|
func (d *DepositTree) MerkleProof(index int) ([][]byte, error) {
|
|
_, proof, err := d.getProof(uint64(index))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
byteSlices := make([][]byte, len(proof))
|
|
for i, p := range proof {
|
|
copied := p
|
|
byteSlices[i] = copied[:]
|
|
}
|
|
return byteSlices, nil
|
|
}
|
|
|
|
// Copy performs a deep copy of the tree.
|
|
func (d *DepositTree) Copy() (*DepositTree, error) {
|
|
snapshot, err := d.GetSnapshot()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fromSnapshot(snapshot)
|
|
}
|
|
|
|
// ToProto returns a proto object of the deposit snapshot of
|
|
// the tree.
|
|
func (d *DepositTree) ToProto() (*protodb.DepositSnapshot, error) {
|
|
snapshot, err := d.GetSnapshot()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return snapshot.ToProto(), nil
|
|
}
|