Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions x/blockdb/memdb/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package memdb

import (
"sync"

"github.com/ava-labs/avalanchego/x/blockdb"
)

// Database is an in-memory implementation of BlockDB
type Database struct {
mu sync.RWMutex
blocks map[blockdb.BlockHeight]blockdb.BlockData
closed bool
}

// WriteBlock stores a block in memory
func (m *Database) WriteBlock(height blockdb.BlockHeight, block blockdb.BlockData) error {
m.mu.Lock()
defer m.mu.Unlock()

if m.closed {
return blockdb.ErrDatabaseClosed
}

if m.blocks == nil {
m.blocks = make(map[blockdb.BlockHeight]blockdb.BlockData)
}

if len(block) == 0 {
return blockdb.ErrBlockEmpty
}

blockCopy := make([]byte, len(block))
copy(blockCopy, block)
m.blocks[height] = blockCopy

return nil
}

// ReadBlock retrieves the full block data for the given height
func (m *Database) ReadBlock(height blockdb.BlockHeight) (blockdb.BlockData, error) {
m.mu.RLock()
defer m.mu.RUnlock()

if m.closed {
return nil, blockdb.ErrDatabaseClosed
}

block, ok := m.blocks[height]
if !ok {
return nil, blockdb.ErrBlockNotFound
}

blockCopy := make([]byte, len(block))
copy(blockCopy, block)
return blockCopy, nil
}

// HasBlock checks if a block exists at the given height
func (m *Database) HasBlock(height blockdb.BlockHeight) (bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()

if m.closed {
return false, blockdb.ErrDatabaseClosed
}

_, ok := m.blocks[height]
return ok, nil
}

// Close closes the in-memory database
func (m *Database) Close() error {
m.mu.Lock()
defer m.mu.Unlock()

m.closed = true
m.blocks = nil
return nil
}
77 changes: 77 additions & 0 deletions x/blockdb/memdb/database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package memdb

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/x/blockdb"
)

func TestOperationsAfterCloseReturnError(t *testing.T) {
db := &Database{}

// Close database
require.NoError(t, db.Close())

height := blockdb.BlockHeight(1)
blockData := blockdb.BlockData("test block data")

// All operations should fail after close
err := db.WriteBlock(height, blockData)
require.ErrorIs(t, err, blockdb.ErrDatabaseClosed)

_, err = db.ReadBlock(height)
require.ErrorIs(t, err, blockdb.ErrDatabaseClosed)

_, err = db.HasBlock(height)
require.ErrorIs(t, err, blockdb.ErrDatabaseClosed)
}

func TestWriteReadAndHasBlock(t *testing.T) {
db := &Database{}

height := blockdb.BlockHeight(1)
blockData := blockdb.BlockData("test block data")

// Write block
require.NoError(t, db.WriteBlock(height, blockData))

// Verify HasBlock returns true
exists, err := db.HasBlock(height)
require.NoError(t, err)
require.True(t, exists)

// Read block back
retrievedBlock, err := db.ReadBlock(height)
require.NoError(t, err)
require.Equal(t, blockData, retrievedBlock)

// Verify non-existent block
nonExistentHeight := blockdb.BlockHeight(999)
exists, err = db.HasBlock(nonExistentHeight)
require.NoError(t, err)
require.False(t, exists)
}

func TestOverwritingBlockUpdatesData(t *testing.T) {
db := &Database{}

height := blockdb.BlockHeight(1)
originalData := blockdb.BlockData("original data")
updatedData := blockdb.BlockData("updated data")

// Write original block
require.NoError(t, db.WriteBlock(height, originalData))

// Overwrite with new data
require.NoError(t, db.WriteBlock(height, updatedData))

// Verify updated data
retrievedBlock, err := db.ReadBlock(height)
require.NoError(t, err)
require.Equal(t, updatedData, retrievedBlock)
}
Loading