diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 3a603096d..b283136e2 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -125,7 +125,17 @@ func (s *Service) onBlock(ctx context.Context, signed *ethpb.SignedBeaconBlock, if err != nil { return errors.Wrap(err, "could not fetch finalized state") } - s.depositCache.InsertFinalizedDeposits(ctx, int64(finalizedState.Eth1Data().DepositCount-1)) + // We update the cache up to the last deposit index in the finalized block's state. + // We can be confident that these deposits will be included in some block + // because the Eth1 follow distance makes such long-range reorgs extremely unlikely. + eth1DepositIndex := int64(finalizedState.Eth1Data().DepositCount - 1) + s.depositCache.InsertFinalizedDeposits(ctx, eth1DepositIndex) + if featureconfig.Get().EnablePruningDepositProofs { + // Deposit proofs are only used during state transition and can be safely removed to save space. + if err = s.depositCache.PruneProofs(ctx, eth1DepositIndex); err != nil { + return errors.Wrap(err, "could not prune deposit proofs") + } + } } defer reportAttestationInclusion(b) diff --git a/beacon-chain/cache/depositcache/deposits_cache.go b/beacon-chain/cache/depositcache/deposits_cache.go index a5e715a91..410b8c61b 100644 --- a/beacon-chain/cache/depositcache/deposits_cache.go +++ b/beacon-chain/cache/depositcache/deposits_cache.go @@ -258,3 +258,28 @@ func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big. return deposits } + +// PruneProofs removes proofs from all deposits whose index is equal or less than untilDepositIndex. +func (dc *DepositCache) PruneProofs(ctx context.Context, untilDepositIndex int64) error { + ctx, span := trace.StartSpan(ctx, "DepositsCache.PruneProofs") + defer span.End() + dc.depositsLock.Lock() + defer dc.depositsLock.Unlock() + + if untilDepositIndex > int64(len(dc.deposits)) { + untilDepositIndex = int64(len(dc.deposits) - 1) + } + + for i := untilDepositIndex; i >= 0; i-- { + if ctx.Err() != nil { + return ctx.Err() + } + // Finding a nil proof means that all proofs up to this deposit have been already pruned. + if dc.deposits[i].Deposit.Proof == nil { + break + } + dc.deposits[i].Deposit.Proof = nil + } + + return nil +} diff --git a/beacon-chain/cache/depositcache/deposits_cache_test.go b/beacon-chain/cache/depositcache/deposits_cache_test.go index a863ed43f..2b37050c4 100644 --- a/beacon-chain/cache/depositcache/deposits_cache_test.go +++ b/beacon-chain/cache/depositcache/deposits_cache_test.go @@ -573,3 +573,137 @@ func TestNonFinalizedDeposits_ReturnsNonFinalizedDepositsUpToBlockNumber(t *test deps := dc.NonFinalizedDeposits(context.Background(), big.NewInt(10)) assert.Equal(t, 1, len(deps)) } + +func TestPruneProofs_Ok(t *testing.T) { + dc, err := New() + require.NoError(t, err) + + deposits := []struct { + blkNum uint64 + deposit *ethpb.Deposit + index int64 + }{ + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 0, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 1, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 2, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 3, + }, + } + + for _, ins := range deposits { + dc.InsertDeposit(context.Background(), ins.deposit, ins.blkNum, ins.index, [32]byte{}) + } + + require.NoError(t, dc.PruneProofs(context.Background(), 1)) + + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[0].Deposit.Proof) + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[1].Deposit.Proof) + assert.NotNil(t, dc.deposits[2].Deposit.Proof) + assert.NotNil(t, dc.deposits[3].Deposit.Proof) +} + +func TestPruneProofs_SomeAlreadyPruned(t *testing.T) { + dc, err := New() + require.NoError(t, err) + + deposits := []struct { + blkNum uint64 + deposit *ethpb.Deposit + index int64 + }{ + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: nil}, + index: 0, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: nil}, + index: 1, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 2, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 3, + }, + } + + for _, ins := range deposits { + dc.InsertDeposit(context.Background(), ins.deposit, ins.blkNum, ins.index, [32]byte{}) + } + + require.NoError(t, dc.PruneProofs(context.Background(), 2)) + + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[2].Deposit.Proof) +} + +func TestPruneProofs_PruneAllWhenDepositIndexTooBig(t *testing.T) { + dc, err := New() + require.NoError(t, err) + + deposits := []struct { + blkNum uint64 + deposit *ethpb.Deposit + index int64 + }{ + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 0, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 1, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 2, + }, + { + blkNum: 0, + deposit: ðpb.Deposit{Proof: makeDepositProof()}, + index: 3, + }, + } + + for _, ins := range deposits { + dc.InsertDeposit(context.Background(), ins.deposit, ins.blkNum, ins.index, [32]byte{}) + } + + require.NoError(t, dc.PruneProofs(context.Background(), 99)) + + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[0].Deposit.Proof) + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[1].Deposit.Proof) + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[2].Deposit.Proof) + assert.DeepEqual(t, ([][]byte)(nil), dc.deposits[3].Deposit.Proof) +} + +func makeDepositProof() [][]byte { + proof := make([][]byte, int(params.BeaconConfig().DepositContractTreeDepth)+1) + for i := range proof { + proof[i] = make([]byte, 32) + } + return proof +} diff --git a/beacon-chain/cache/depositcache/pending_deposits_test.go b/beacon-chain/cache/depositcache/pending_deposits_test.go index c4bdac26c..907cc5dce 100644 --- a/beacon-chain/cache/depositcache/pending_deposits_test.go +++ b/beacon-chain/cache/depositcache/pending_deposits_test.go @@ -9,7 +9,6 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prysmaticlabs/prysm/shared/bytesutil" - "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil/assert" ) @@ -29,14 +28,6 @@ func TestInsertPendingDeposit_ignoresNilDeposit(t *testing.T) { assert.Equal(t, 0, len(dc.pendingDeposits)) } -func makeDepositProof() [][]byte { - proof := make([][]byte, int(params.BeaconConfig().DepositContractTreeDepth)+1) - for i := range proof { - proof[i] = make([]byte, 32) - } - return proof -} - func TestRemovePendingDeposit_OK(t *testing.T) { db := DepositCache{} proof1 := makeDepositProof() diff --git a/shared/featureconfig/config.go b/shared/featureconfig/config.go index 1db05066c..9043b6d91 100644 --- a/shared/featureconfig/config.go +++ b/shared/featureconfig/config.go @@ -68,6 +68,7 @@ type Flags struct { EnableEth1DataMajorityVote bool // EnableEth1DataMajorityVote uses the Voting With The Majority algorithm to vote for eth1data. EnableAttBroadcastDiscoveryAttempts bool // EnableAttBroadcastDiscoveryAttempts allows the p2p service to attempt to ensure a subnet peer is present before broadcasting an attestation. EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p. + EnablePruningDepositProofs bool // EnablePruningDepositProofs enables pruning deposit proofs which significantly reduces the size of a deposit // DisableForkChoice disables using LMD-GHOST fork choice to update // the head of the chain based on attestations and instead accepts any valid received block @@ -297,6 +298,10 @@ func ConfigureBeaconChain(ctx *cli.Context) { log.Warn("Enabling new BLS library blst") cfg.EnableBlst = true } + if ctx.Bool(enablePruningDepositProofs.Name) { + log.Warn("Enabling pruning deposit proofs") + cfg.EnablePruningDepositProofs = true + } Init(cfg) } diff --git a/shared/featureconfig/flags.go b/shared/featureconfig/flags.go index 8fc4ff810..8d5790fa5 100644 --- a/shared/featureconfig/flags.go +++ b/shared/featureconfig/flags.go @@ -184,6 +184,10 @@ var ( Name: "use-check-point-cache", Usage: "Enables check point info caching", } + enablePruningDepositProofs = &cli.BoolFlag{ + Name: "enable-pruning-deposit-proofs", + Usage: "Enables pruning deposit proofs when they are no longer needed. This significantly reduces deposit size.", + } ) // devModeFlags holds list of flags that are set when development mode is on. @@ -192,6 +196,7 @@ var devModeFlags = []cli.Flag{ enableEth1DataMajorityVote, enableAttBroadcastDiscoveryAttempts, enablePeerScorer, + enablePruningDepositProofs, } // Deprecated flags list. @@ -715,6 +720,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{ enableAttBroadcastDiscoveryAttempts, enablePeerScorer, checkPtInfoCache, + enablePruningDepositProofs, }...) // E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E. @@ -726,4 +732,5 @@ var E2EBeaconChainFlags = []string{ "--dev", "--enable-eth1-data-majority-vote", "--use-check-point-cache", + "--enable-pruning-deposit-proofs", }