Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin.dbGet API #2667

Merged
merged 1 commit into from
Jan 25, 2024
Merged
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
23 changes: 23 additions & 0 deletions api/admin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"context"

"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/database/rpcdb"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/rpc"
)
Expand All @@ -28,6 +30,7 @@ type Client interface {
SetLoggerLevel(ctx context.Context, loggerName, logLevel, displayLevel string, options ...rpc.Option) (map[string]LogAndDisplayLevels, error)
GetLoggerLevel(ctx context.Context, loggerName string, options ...rpc.Option) (map[string]LogAndDisplayLevels, error)
GetConfig(ctx context.Context, options ...rpc.Option) (interface{}, error)
DBGet(ctx context.Context, key []byte, options ...rpc.Option) ([]byte, error)
}

// Client implementation for the Avalanche Platform Info API Endpoint
Expand Down Expand Up @@ -140,3 +143,23 @@ func (c *client) GetConfig(ctx context.Context, options ...rpc.Option) (interfac
err := c.requester.SendRequest(ctx, "admin.getConfig", struct{}{}, &res, options...)
return res, err
}

func (c *client) DBGet(ctx context.Context, key []byte, options ...rpc.Option) ([]byte, error) {
keyStr, err := formatting.Encode(formatting.HexNC, key)
if err != nil {
return nil, err
}

res := &DBGetReply{}
err = c.requester.SendRequest(ctx, "admin.dbGet", &DBGetArgs{
Key: keyStr,
}, res, options...)
if err != nil {
return nil, err
}

if err := rpcdb.ErrEnumToError[res.ErrorCode]; err != nil {
return nil, err
}
return formatting.Decode(formatting.HexNC, res.Value)
}
34 changes: 34 additions & 0 deletions api/admin/key_value_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package admin

import (
"context"

"github.com/ava-labs/avalanchego/database"
)

var _ database.KeyValueReader = (*KeyValueReader)(nil)

type KeyValueReader struct {
client Client
}

func NewKeyValueReader(client Client) *KeyValueReader {
return &KeyValueReader{
client: client,
}
}

func (r *KeyValueReader) Has(key []byte) (bool, error) {
_, err := r.client.DBGet(context.Background(), key)
if err == database.ErrNotFound {
return false, nil
}
return err == nil, err
}

func (r *KeyValueReader) Get(key []byte) ([]byte, error) {
return r.client.DBGet(context.Background(), key)
}
38 changes: 38 additions & 0 deletions api/admin/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@ import (
"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/api/server"
"github.com/ava-labs/avalanchego/chains"
"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/database/rpcdb"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/perms"
"github.com/ava-labs/avalanchego/utils/profiler"
"github.com/ava-labs/avalanchego/vms"
"github.com/ava-labs/avalanchego/vms/registry"

rpcdbpb "github.com/ava-labs/avalanchego/proto/pb/rpcdb"
)

const (
Expand All @@ -44,6 +49,7 @@ type Config struct {
ProfileDir string
LogFactory logging.Factory
NodeConfig interface{}
DB database.Database
ChainManager chains.Manager
HTTPServer server.PathAdderWithReadLock
VMRegistry registry.VMRegistry
Expand Down Expand Up @@ -376,3 +382,35 @@ func (a *Admin) getLogLevels(loggerNames []string) (map[string]LogAndDisplayLeve
}
return loggerLevels, nil
}

type DBGetArgs struct {
Key string `json:"key"`
}

type DBGetReply struct {
Value string `json:"value"`
ErrorCode rpcdbpb.Error `json:"errorCode"`
}

//nolint:stylecheck // renaming this method to DBGet would change the API method from "dbGet" to "dBGet"
func (a *Admin) DbGet(_ *http.Request, args *DBGetArgs, reply *DBGetReply) error {
a.Log.Debug("API called",
zap.String("service", "admin"),
zap.String("method", "dbGet"),
logging.UserString("key", args.Key),
)

key, err := formatting.Decode(formatting.HexNC, args.Key)
if err != nil {
return err
}

value, err := a.DB.Get(key)
if err != nil {
reply.ErrorCode = rpcdb.ErrorToErrEnum[err]
return rpcdb.ErrorToRPCError(err)
}

reply.Value, err = formatting.Encode(formatting.HexNC, value)
return err
}
57 changes: 57 additions & 0 deletions api/admin/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import (

"go.uber.org/mock/gomock"

"github.com/ava-labs/avalanchego/database/memdb"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/vms"
"github.com/ava-labs/avalanchego/vms/registry"

rpcdbpb "github.com/ava-labs/avalanchego/proto/pb/rpcdb"
)

type loadVMsTest struct {
Expand Down Expand Up @@ -111,3 +115,56 @@ func TestLoadVMsGetAliasesFails(t *testing.T) {
err := resources.admin.LoadVMs(&http.Request{}, nil, &reply)
require.ErrorIs(err, errTest)
}

func TestServiceDBGet(t *testing.T) {
a := &Admin{Config: Config{
Log: logging.NoLog{},
DB: memdb.New(),
}}

helloBytes := []byte("hello")
helloHex, err := formatting.Encode(formatting.HexNC, helloBytes)
require.NoError(t, err)

worldBytes := []byte("world")
worldHex, err := formatting.Encode(formatting.HexNC, worldBytes)
require.NoError(t, err)

require.NoError(t, a.DB.Put(helloBytes, worldBytes))

tests := []struct {
name string
key string
expectedValue string
expectedErrorCode rpcdbpb.Error
}{
{
name: "key exists",
key: helloHex,
expectedValue: worldHex,
expectedErrorCode: rpcdbpb.Error_ERROR_UNSPECIFIED,
},
{
name: "key doesn't exist",
key: "",
expectedValue: "",
expectedErrorCode: rpcdbpb.Error_ERROR_NOT_FOUND,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)

reply := &DBGetReply{}
require.NoError(a.DbGet(
nil,
&DBGetArgs{
Key: test.key,
},
reply,
))
require.Equal(test.expectedValue, reply.Value)
require.Equal(test.expectedErrorCode, reply.ErrorCode)
})
}
}
26 changes: 13 additions & 13 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ const (

var (
// Commonly shared VM DB prefix
vmDBPrefix = []byte("vm")
VMDBPrefix = []byte("vm")

// Bootstrapping prefixes for LinearizableVMs
vertexDBPrefix = []byte("vertex")
vertexBootstrappingDBPrefix = []byte("vertex_bs")
txBootstrappingDBPrefix = []byte("tx_bs")
blockBootstrappingDBPrefix = []byte("block_bs")
VertexDBPrefix = []byte("vertex")
VertexBootstrappingDBPrefix = []byte("vertex_bs")
TxBootstrappingDBPrefix = []byte("tx_bs")
BlockBootstrappingDBPrefix = []byte("block_bs")

// Bootstrapping prefixes for ChainVMs
bootstrappingDB = []byte("bs")
ChainBootstrappingDBPrefix = []byte("bs")

errUnknownVMType = errors.New("the vm should have type avalanche.DAGVM or snowman.ChainVM")
errCreatePlatformVM = errors.New("attempted to create a chain running the PlatformVM")
Expand Down Expand Up @@ -596,11 +596,11 @@ func (m *manager) createAvalancheChain(
return nil, err
}
prefixDB := prefixdb.New(ctx.ChainID[:], meterDB)
vmDB := prefixdb.New(vmDBPrefix, prefixDB)
vertexDB := prefixdb.New(vertexDBPrefix, prefixDB)
vertexBootstrappingDB := prefixdb.New(vertexBootstrappingDBPrefix, prefixDB)
txBootstrappingDB := prefixdb.New(txBootstrappingDBPrefix, prefixDB)
blockBootstrappingDB := prefixdb.New(blockBootstrappingDBPrefix, prefixDB)
vmDB := prefixdb.New(VMDBPrefix, prefixDB)
vertexDB := prefixdb.New(VertexDBPrefix, prefixDB)
vertexBootstrappingDB := prefixdb.New(VertexBootstrappingDBPrefix, prefixDB)
txBootstrappingDB := prefixdb.New(TxBootstrappingDBPrefix, prefixDB)
blockBootstrappingDB := prefixdb.New(BlockBootstrappingDBPrefix, prefixDB)

vtxBlocker, err := queue.NewWithMissing(vertexBootstrappingDB, "vtx", ctx.AvalancheRegisterer)
if err != nil {
Expand Down Expand Up @@ -999,8 +999,8 @@ func (m *manager) createSnowmanChain(
return nil, err
}
prefixDB := prefixdb.New(ctx.ChainID[:], meterDB)
vmDB := prefixdb.New(vmDBPrefix, prefixDB)
bootstrappingDB := prefixdb.New(bootstrappingDB, prefixDB)
vmDB := prefixdb.New(VMDBPrefix, prefixDB)
bootstrappingDB := prefixdb.New(ChainBootstrappingDBPrefix, prefixDB)

blocked, err := queue.NewWithMissing(bootstrappingDB, "block", ctx.Registerer)
if err != nil {
Expand Down
56 changes: 42 additions & 14 deletions database/prefixdb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,57 @@ type Database struct {
closed bool
}

func newDB(prefix []byte, db database.Database) *Database {
return &Database{
dbPrefix: prefix,
db: db,
bufferPool: sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultBufCap)
},
},
}
}

// New returns a new prefixed database
func New(prefix []byte, db database.Database) *Database {
if prefixDB, ok := db.(*Database); ok {
simplePrefix := make([]byte, len(prefixDB.dbPrefix)+len(prefix))
copy(simplePrefix, prefixDB.dbPrefix)
copy(simplePrefix[len(prefixDB.dbPrefix):], prefix)
return NewNested(simplePrefix, prefixDB.db)
return newDB(
JoinPrefixes(prefixDB.dbPrefix, prefix),
prefixDB.db,
)
}
return NewNested(prefix, db)
return newDB(
MakePrefix(prefix),
db,
)
}

// NewNested returns a new prefixed database without attempting to compress
// prefixes.
func NewNested(prefix []byte, db database.Database) *Database {
return &Database{
dbPrefix: hashing.ComputeHash256(prefix),
db: db,
bufferPool: sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultBufCap)
},
},
}
return newDB(
MakePrefix(prefix),
db,
)
}

func MakePrefix(prefix []byte) []byte {
return hashing.ComputeHash256(prefix)
}

func JoinPrefixes(firstPrefix, secondPrefix []byte) []byte {
simplePrefix := make([]byte, len(firstPrefix)+len(secondPrefix))
copy(simplePrefix, firstPrefix)
copy(simplePrefix[len(firstPrefix):], secondPrefix)
return MakePrefix(simplePrefix)
}

func PrefixKey(prefix, key []byte) []byte {
prefixedKey := make([]byte, len(prefix)+len(key))
copy(prefixedKey, prefix)
copy(prefixedKey[len(prefix):], key)
return prefixedKey
}

// Assumes that it is OK for the argument to db.db.Has
Expand Down
Loading
Loading