Skip to content

Commit

Permalink
x/sync / x/merkledb -- add SyncableDB interface (ava-labs#1555)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Laine authored May 30, 2023
1 parent b66e25e commit 243e313
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 43 deletions.
43 changes: 25 additions & 18 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"fmt"
"sync"

"github.com/prometheus/client_golang/prometheus"

"go.opentelemetry.io/otel/attribute"

oteltrace "go.opentelemetry.io/otel/trace"
Expand All @@ -27,6 +25,7 @@ import (
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/math"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/prometheus/client_golang/prometheus"
)

const (
Expand All @@ -53,22 +52,7 @@ var (
errSameRoot = errors.New("start and end root are the same")
)

type Config struct {
// The number of changes to the database that we store in memory in order to
// serve change proofs.
HistoryLength int
NodeCacheSize int
// If [Reg] is nil, metrics are collected locally but not exported through
// Prometheus.
// This may be useful for testing.
Reg prometheus.Registerer
Tracer trace.Tracer
}

type MerkleDB interface {
database.Database
Trie

type ChangeProofer interface {
// GetChangeProof returns a proof for a subset of the key/value changes in key range
// [start, end] that occurred between [startRootID] and [endRootID].
// Returns at most [maxLength] key/value pairs.
Expand Down Expand Up @@ -105,7 +89,9 @@ type MerkleDB interface {

// CommitChangeProof commits the key/value pairs within the [proof] to the db.
CommitChangeProof(ctx context.Context, proof *ChangeProof) error
}

type RangeProofer interface {
// GetRangeProofAtRoot returns a proof for the key/value pairs in this trie within the range
// [start, end] when the root of the trie was [rootID].
GetRangeProofAtRoot(
Expand All @@ -121,6 +107,27 @@ type MerkleDB interface {
CommitRangeProof(ctx context.Context, start []byte, proof *RangeProof) error
}

type MerkleDB interface {
database.Database
Trie
MerkleRootGetter
ProofGetter
ChangeProofer
RangeProofer
}

type Config struct {
// The number of changes to the database that we store in memory in order to
// serve change proofs.
HistoryLength int
NodeCacheSize int
// If [Reg] is nil, metrics are collected locally but not exported through
// Prometheus.
// This may be useful for testing.
Reg prometheus.Registerer
Tracer trace.Tracer
}

// merkleDB can only be edited by committing changes from a trieView.
type merkleDB struct {
// Must be held when reading/writing fields.
Expand Down
20 changes: 14 additions & 6 deletions x/merkledb/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,21 @@ import (

var errNoNewRoot = errors.New("there was no updated root in change list")

type MerkleRootGetter interface {
// GetMerkleRoot returns the merkle root of the Trie
GetMerkleRoot(ctx context.Context) (ids.ID, error)
}

type ProofGetter interface {
// GetProof generates a proof of the value associated with a particular key,
// or a proof of its absence from the trie
GetProof(ctx context.Context, bytesPath []byte) (*Proof, error)
}

type ReadOnlyTrie interface {
MerkleRootGetter
ProofGetter

// GetValue gets the value associated with the specified key
// database.ErrNotFound if the key is not present
GetValue(ctx context.Context, key []byte) ([]byte, error)
Expand All @@ -26,15 +40,9 @@ type ReadOnlyTrie interface {
// database.ErrNotFound if the key is not present
getValue(key path, lock bool) ([]byte, error)

// GetMerkleRoot returns the merkle root of the Trie
GetMerkleRoot(ctx context.Context) (ids.ID, error)

// get an editable copy of the node with the given key path
getEditableNode(key path) (*node, error)

// GetProof generates a proof of the value associated with a particular key, or a proof of its absence from the trie
GetProof(ctx context.Context, bytesPath []byte) (*Proof, error)

// GetRangeProof generates a proof of up to maxLength smallest key/values with keys between start and end
GetRangeProof(ctx context.Context, start, end []byte, maxLength int) (*RangeProof, error)

Expand Down
4 changes: 2 additions & 2 deletions x/sync/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type Client interface {
// GetChangeProof synchronously sends the given request, returning a parsed ChangesResponse or error
// [verificationDB] is the local db that has all key/values in it for the proof's startroot within the proof's key range
// Note: this verifies the response including the change proof.
GetChangeProof(ctx context.Context, request *syncpb.ChangeProofRequest, verificationDB merkledb.MerkleDB) (*merkledb.ChangeProof, error)
GetChangeProof(ctx context.Context, request *syncpb.ChangeProofRequest, verificationDB SyncableDB) (*merkledb.ChangeProof, error)
}

type client struct {
Expand Down Expand Up @@ -79,7 +79,7 @@ func NewClient(config *ClientConfig) Client {
// GetChangeProof synchronously retrieves the change proof given by [req].
// Upon failure, retries until the context is expired.
// The returned change proof is verified.
func (c *client) GetChangeProof(ctx context.Context, req *syncpb.ChangeProofRequest, db merkledb.MerkleDB) (*merkledb.ChangeProof, error) {
func (c *client) GetChangeProof(ctx context.Context, req *syncpb.ChangeProofRequest, db SyncableDB) (*merkledb.ChangeProof, error) {
parseFn := func(ctx context.Context, responseBytes []byte) (*merkledb.ChangeProof, error) {
if len(responseBytes) > int(req.BytesLimit) {
return nil, fmt.Errorf("%w: (%d) > %d)", errTooManyBytes, len(responseBytes), req.BytesLimit)
Expand Down
18 changes: 9 additions & 9 deletions x/sync/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (

func sendRangeRequest(
t *testing.T,
db merkledb.MerkleDB,
db SyncableDB,
request *syncpb.RangeProofRequest,
maxAttempts uint32,
modifyResponse func(*merkledb.RangeProof),
Expand Down Expand Up @@ -123,7 +123,7 @@ func TestGetRangeProof(t *testing.T) {
require.NoError(t, err)

tests := map[string]struct {
db merkledb.MerkleDB
db SyncableDB
request *syncpb.RangeProofRequest
modifyResponse func(*merkledb.RangeProof)
expectedErr error
Expand Down Expand Up @@ -209,10 +209,10 @@ func TestGetRangeProof(t *testing.T) {
},
modifyResponse: func(response *merkledb.RangeProof) {
start := response.KeyValues[1].Key
proof, err := largeTrieDB.GetRangeProof(context.Background(), start, nil, defaultRequestKeyLimit)
if err != nil {
panic(err)
}
rootID, err := largeTrieDB.GetMerkleRoot(context.Background())
require.NoError(t, err)
proof, err := largeTrieDB.GetRangeProofAtRoot(context.Background(), rootID, start, nil, defaultRequestKeyLimit)
require.NoError(t, err)
response.KeyValues = proof.KeyValues
response.StartProof = proof.StartProof
response.EndProof = proof.EndProof
Expand Down Expand Up @@ -279,8 +279,8 @@ func TestGetRangeProof(t *testing.T) {

func sendChangeRequest(
t *testing.T,
db merkledb.MerkleDB,
verificationDB merkledb.MerkleDB,
db SyncableDB,
verificationDB SyncableDB,
request *syncpb.ChangeProofRequest,
maxAttempts uint32,
modifyResponse func(*merkledb.ChangeProof),
Expand Down Expand Up @@ -423,7 +423,7 @@ func TestGetChangeProof(t *testing.T) {
require.NoError(t, err)

tests := map[string]struct {
db merkledb.MerkleDB
db SyncableDB
request *syncpb.ChangeProofRequest
modifyResponse func(*merkledb.ChangeProof)
expectedErr error
Expand Down
2 changes: 1 addition & 1 deletion x/sync/mock_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions x/sync/network_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ var ErrMinProofSizeIsTooLarge = errors.New("cannot generate any proof within the

type NetworkServer struct {
appSender common.AppSender // Used to respond to peer requests via AppResponse.
db merkledb.MerkleDB
db SyncableDB
log logging.Logger
}

func NewNetworkServer(appSender common.AppSender, db merkledb.MerkleDB, log logging.Logger) *NetworkServer {
func NewNetworkServer(appSender common.AppSender, db SyncableDB, log logging.Logger) *NetworkServer {
return &NetworkServer{
appSender: appSender,
db: db,
Expand Down
8 changes: 4 additions & 4 deletions x/sync/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ func newNoopTracer() trace.Tracer {
}

type mockClient struct {
db merkledb.MerkleDB
db SyncableDB
}

func (client *mockClient) GetChangeProof(ctx context.Context, request *syncpb.ChangeProofRequest, _ merkledb.MerkleDB) (*merkledb.ChangeProof, error) {
func (client *mockClient) GetChangeProof(ctx context.Context, request *syncpb.ChangeProofRequest, _ SyncableDB) (*merkledb.ChangeProof, error) {
startRoot, err := ids.ToID(request.StartRootHash)
if err != nil {
return nil, err
Expand Down Expand Up @@ -871,7 +871,7 @@ func Test_Sync_Error_During_Sync(t *testing.T) {
},
).AnyTimes()
client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
func(ctx context.Context, request *syncpb.ChangeProofRequest, _ merkledb.MerkleDB) (*merkledb.ChangeProof, error) {
func(ctx context.Context, request *syncpb.ChangeProofRequest, _ SyncableDB) (*merkledb.ChangeProof, error) {
startRoot, err := ids.ToID(request.StartRootHash)
require.NoError(err)
endRoot, err := ids.ToID(request.EndRootHash)
Expand Down Expand Up @@ -961,7 +961,7 @@ func Test_Sync_Result_Correct_Root_Update_Root_During(t *testing.T) {
},
).AnyTimes()
client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
func(ctx context.Context, request *syncpb.ChangeProofRequest, _ merkledb.MerkleDB) (*merkledb.ChangeProof, error) {
func(ctx context.Context, request *syncpb.ChangeProofRequest, _ SyncableDB) (*merkledb.ChangeProof, error) {
<-updatedRootChan
startRoot, err := ids.ToID(request.StartRootHash)
require.NoError(err)
Expand Down
13 changes: 13 additions & 0 deletions x/sync/syncable_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package sync

import "github.com/ava-labs/avalanchego/x/merkledb"

type SyncableDB interface {
merkledb.MerkleRootGetter
merkledb.ProofGetter
merkledb.ChangeProofer
merkledb.RangeProofer
}
2 changes: 1 addition & 1 deletion x/sync/syncmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ type StateSyncManager struct {
}

type StateSyncConfig struct {
SyncDB merkledb.MerkleDB
SyncDB SyncableDB
Client Client
SimultaneousWorkLimit int
Log logging.Logger
Expand Down

0 comments on commit 243e313

Please sign in to comment.