Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
85 changes: 85 additions & 0 deletions database/heightindexdb/memdb/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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/database"
)

var _ database.HeightIndex = (*Database)(nil)

// Database is an in-memory implementation of database.HeightIndex
type Database struct {
mu sync.RWMutex
data map[uint64][]byte
closed bool
}

// Put stores data in memory at the given height
func (d *Database) Put(height uint64, data []byte) error {
d.mu.Lock()
defer d.mu.Unlock()

if d.closed {
return database.ErrClosed
}

if d.data == nil {
d.data = make(map[uint64][]byte)
}

if len(data) == 0 {
return database.ErrNotFound
}

dataCopy := make([]byte, len(data))
copy(dataCopy, data)
d.data[height] = dataCopy

return nil
}

// Get retrieves data at the given height
func (d *Database) Get(height uint64) ([]byte, error) {
d.mu.RLock()
defer d.mu.RUnlock()

if d.closed {
return nil, database.ErrClosed
}

data, ok := d.data[height]
if !ok {
return nil, database.ErrNotFound
}

dataCopy := make([]byte, len(data))
copy(dataCopy, data)
return dataCopy, nil
}

// Has checks if data exists at the given height
func (d *Database) Has(height uint64) (bool, error) {
d.mu.RLock()
defer d.mu.RUnlock()

if d.closed {
return false, database.ErrClosed
}

_, ok := d.data[height]
return ok, nil
}

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

d.closed = true
d.data = nil
return nil
}
113 changes: 113 additions & 0 deletions database/heightindexdb/memdb/database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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/database"
)

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

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

height := uint64(1)
blockData := []byte("test block data")

tests := []struct {
name string
fn func() error
}{
{
name: "Put",
fn: func() error {
return db.Put(height, blockData)
},
},
{
name: "Get",
fn: func() error {
_, err := db.Get(height)
return err
},
},
{
name: "Has",
fn: func() error {
_, err := db.Has(height)
return err
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.fn()
require.ErrorIs(t, err, database.ErrClosed)
})
}
}

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

height := uint64(1)
blockData := []byte("test block data")
require.NoError(t, db.Put(height, blockData))
}

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

height := uint64(1)
blockData := []byte("test block data")
require.NoError(t, db.Put(height, blockData))

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

func TestHas(t *testing.T) {
t.Run("non-existent block", func(t *testing.T) {
db := &Database{}
exists, err := db.Has(uint64(1))
require.NoError(t, err)
require.False(t, exists)
})

t.Run("existing block", func(t *testing.T) {
db := &Database{}
blockData := []byte("test block data")
require.NoError(t, db.Put(uint64(1), blockData))
exists, err := db.Has(uint64(1))
require.NoError(t, err)
require.True(t, exists)
})
}

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

height := uint64(1)
originalData := []byte("original data")
updatedData := []byte("updated data")

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

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

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