Skip to content

Commit 38d9717

Browse files
committed
add ReadonlyDBBucket
1 parent 94d628e commit 38d9717

2 files changed

Lines changed: 35 additions & 14 deletions

File tree

chain/db.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ type DB interface {
7373
Snapshot() ReadonlyDB
7474
}
7575

76-
// A ReadonlyDB is a read-only, point-in-time view of a DB. Because it exposes
77-
// no mutating methods, the underlying database cannot be modified through it.
76+
// A ReadonlyDB is a read-only, point-in-time view of a DB. It exposes
77+
// no mutating methods. The underlying database cannot be modified through it.
7878
type ReadonlyDB interface {
79-
Bucket(name []byte) DBBucket
79+
Bucket(name []byte) ReadonlyDBBucket
8080
// Close releases the resources held by the snapshot.
8181
Close() error
8282
}
@@ -90,15 +90,21 @@ type noSnapshotDB struct {
9090
}
9191
}
9292

93-
func (s noSnapshotDB) Bucket(name []byte) DBBucket { return s.db.Bucket(name) }
94-
func (noSnapshotDB) Close() error { return nil }
93+
func (s noSnapshotDB) Bucket(name []byte) ReadonlyDBBucket { return s.db.Bucket(name) }
94+
func (noSnapshotDB) Close() error { return nil }
95+
96+
// A ReadonlyDBBucket is a read-only view of a DBBucket; it exposes no mutating
97+
// methods, so a bucket obtained from a ReadonlyDB cannot be written to.
98+
type ReadonlyDBBucket interface {
99+
Get(key []byte) []byte
100+
Iter() iter.Seq2[[]byte, []byte]
101+
}
95102

96103
// A DBBucket is a set of key-value pairs.
97104
type DBBucket interface {
98-
Get(key []byte) []byte
105+
ReadonlyDBBucket
99106
Put(key, value []byte) error
100107
Delete(key []byte) error
101-
Iter() iter.Seq2[[]byte, []byte]
102108
}
103109

104110
// MemDB implements DB with an in-memory map.
@@ -395,10 +401,17 @@ func check(err error) {
395401

396402
// dbBucket is a helper type for implementing Store.
397403
type dbBucket struct {
398-
b DBBucket
404+
b ReadonlyDBBucket
399405
db *DBStore
400406
}
401407

408+
// writable returns the bucket as a DBBucket for mutation. It is only reached
409+
// from write paths, which only run on a writable DBStore; such a store is
410+
// always backed by a full DB, so its buckets always implement DBBucket.
411+
func (b *dbBucket) writable() DBBucket {
412+
return b.b.(DBBucket)
413+
}
414+
402415
func (b *dbBucket) getRaw(key []byte) []byte {
403416
if b.b == nil {
404417
return nil
@@ -421,7 +434,7 @@ func (b *dbBucket) get(key []byte, v types.DecoderFrom) bool {
421434
}
422435

423436
func (b *dbBucket) putRaw(key, value []byte) {
424-
check(b.b.Put(key, value))
437+
check(b.writable().Put(key, value))
425438
b.db.unflushed += len(value)
426439
}
427440

@@ -434,7 +447,7 @@ func (b *dbBucket) put(key []byte, v types.EncoderTo) {
434447
}
435448

436449
func (b *dbBucket) delete(key []byte) {
437-
check(b.b.Delete(key))
450+
check(b.writable().Delete(key))
438451
b.db.unflushed += len(key)
439452
}
440453

@@ -456,11 +469,19 @@ var (
456469
// snapshot can satisfy it (see roDB), letting a snapshot reuse DBStore's
457470
// accessors without exposing any write methods.
458471
type rwDB interface {
459-
Bucket(name []byte) DBBucket
472+
Bucket(name []byte) ReadonlyDBBucket
460473
Flush() error
461474
Snapshot() ReadonlyDB
462475
}
463476

477+
// rwView adapts a full DB to the rwDB interface, narrowing Bucket's return type
478+
// to ReadonlyDBBucket so that DBStore's shared read path cannot mutate through
479+
// it. Write paths recover the full DBBucket via dbBucket.writable, which is
480+
// sound because a writable DBStore is always backed by a full DB.
481+
type rwView struct{ DB }
482+
483+
func (v rwView) Bucket(name []byte) ReadonlyDBBucket { return v.DB.Bucket(name) }
484+
464485
// DBStore implements Store using a key-value database.
465486
type DBStore struct {
466487
db rwDB
@@ -1053,7 +1074,7 @@ func NewDBStore(db DB, n *consensus.Network, genesisBlock types.Block, logger Mi
10531074
}
10541075

10551076
dbs := &DBStore{
1056-
db: db,
1077+
db: rwView{db},
10571078
n: n,
10581079
}
10591080

@@ -1129,7 +1150,7 @@ func NewDBStoreAtCheckpoint(db DB, cs consensus.State, b types.Block, logger Mig
11291150
}
11301151

11311152
dbs := &DBStore{
1132-
db: db,
1153+
db: rwView{db},
11331154
n: cs.Network,
11341155
}
11351156

db.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ type boltSnapshot struct {
9696
}
9797

9898
// Bucket implements chain.ReadonlyDB.
99-
func (s *boltSnapshot) Bucket(name []byte) chain.DBBucket {
99+
func (s *boltSnapshot) Bucket(name []byte) chain.ReadonlyDBBucket {
100100
b := s.tx.Bucket(name)
101101
if b == nil {
102102
return nil

0 commit comments

Comments
 (0)