@@ -2,6 +2,7 @@ package store
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "strconv"
78
@@ -24,6 +25,10 @@ const (
2425 blockResponseKey = "br"
2526 // blockIDKey is the key used for storing block IDs
2627 blockIDKey = "bid"
28+ // lastPrunedHeightKey tracks the highest height that has been pruned.
29+ // This makes pruning idempotent and allows incremental pruning across
30+ // multiple calls.
31+ lastPrunedHeightKey = "lph"
2732)
2833
2934// Store wraps a datastore with ABCI-specific functionality
@@ -147,3 +152,54 @@ func (s *Store) GetBlockResponse(ctx context.Context, height uint64) (*abci.Resp
147152
148153 return resp , nil
149154}
155+
156+ // Prune deletes per-height ABCI execution metadata (block IDs and block
157+ // responses) for all heights up to and including the provided target
158+ // height. The current ABCI state (stored under stateKey) is never pruned,
159+ // as it is maintained separately by the application.
160+ //
161+ // Pruning is idempotent: the store tracks the highest pruned height and
162+ // will skip work for already-pruned ranges.
163+ func (s * Store ) Prune (ctx context.Context , height uint64 ) error {
164+ // Load the last pruned height, if any.
165+ data , err := s .prefixedStore .Get (ctx , ds .NewKey (lastPrunedHeightKey ))
166+ if err != nil {
167+ if ! errors .Is (err , ds .ErrNotFound ) {
168+ return fmt .Errorf ("failed to get last pruned height: %w" , err )
169+ }
170+ }
171+
172+ var lastPruned uint64
173+ if len (data ) > 0 {
174+ lastPruned , err = strconv .ParseUint (string (data ), 10 , 64 )
175+ if err != nil {
176+ return fmt .Errorf ("invalid last pruned height value %q: %w" , string (data ), err )
177+ }
178+ }
179+
180+ // Nothing to do if we've already pruned up to at least this height.
181+ if height <= lastPruned {
182+ return nil
183+ }
184+
185+ // Delete per-height ABCI metadata (block IDs and block responses) for
186+ // heights in (lastPruned, height]. Missing keys are ignored.
187+ for h := lastPruned + 1 ; h <= height ; h ++ {
188+ bidKey := ds .NewKey (blockIDKey ).ChildString (strconv .FormatUint (h , 10 ))
189+ if err := s .prefixedStore .Delete (ctx , bidKey ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
190+ return fmt .Errorf ("failed to delete block ID at height %d during pruning: %w" , h , err )
191+ }
192+
193+ brKey := ds .NewKey (blockResponseKey ).ChildString (strconv .FormatUint (h , 10 ))
194+ if err := s .prefixedStore .Delete (ctx , brKey ); err != nil && ! errors .Is (err , ds .ErrNotFound ) {
195+ return fmt .Errorf ("failed to delete block response at height %d during pruning: %w" , h , err )
196+ }
197+ }
198+
199+ // Persist the updated last pruned height.
200+ if err := s .prefixedStore .Put (ctx , ds .NewKey (lastPrunedHeightKey ), []byte (strconv .FormatUint (height , 10 ))); err != nil {
201+ return fmt .Errorf ("failed to update last pruned height: %w" , err )
202+ }
203+
204+ return nil
205+ }
0 commit comments