-
Notifications
You must be signed in to change notification settings - Fork 671
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use native DB range queries for applying validator diffs (#1752)
- Loading branch information
1 parent
c7ec186
commit 1de061a
Showing
13 changed files
with
1,227 additions
and
1,201 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package state | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
|
||
"github.com/ava-labs/avalanchego/database" | ||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
const ( | ||
// startDiffKey = [subnetID] + [inverseHeight] | ||
startDiffKeyLength = ids.IDLen + database.Uint64Size | ||
// diffKey = [subnetID] + [inverseHeight] + [nodeID] | ||
diffKeyLength = startDiffKeyLength + ids.NodeIDLen | ||
// diffKeyNodeIDOffset = [subnetIDLen] + [inverseHeightLen] | ||
diffKeyNodeIDOffset = ids.IDLen + database.Uint64Size | ||
|
||
// weightValue = [isNegative] + [weight] | ||
weightValueLength = database.BoolSize + database.Uint64Size | ||
) | ||
|
||
var ( | ||
errUnexpectedDiffKeyLength = fmt.Errorf("expected diff key length %d", diffKeyLength) | ||
errUnexpectedWeightValueLength = fmt.Errorf("expected weight value length %d", weightValueLength) | ||
) | ||
|
||
// marshalStartDiffKey is used to determine the starting key when iterating. | ||
// | ||
// Invariant: the result is a prefix of [marshalDiffKey] when called with the | ||
// same arguments. | ||
func marshalStartDiffKey(subnetID ids.ID, height uint64) []byte { | ||
key := make([]byte, startDiffKeyLength) | ||
copy(key, subnetID[:]) | ||
packIterableHeight(key[ids.IDLen:], height) | ||
return key | ||
} | ||
|
||
func marshalDiffKey(subnetID ids.ID, height uint64, nodeID ids.NodeID) []byte { | ||
key := make([]byte, diffKeyLength) | ||
copy(key, subnetID[:]) | ||
packIterableHeight(key[ids.IDLen:], height) | ||
copy(key[diffKeyNodeIDOffset:], nodeID[:]) | ||
return key | ||
} | ||
|
||
func unmarshalDiffKey(key []byte) (ids.ID, uint64, ids.NodeID, error) { | ||
if len(key) != diffKeyLength { | ||
return ids.Empty, 0, ids.EmptyNodeID, errUnexpectedDiffKeyLength | ||
} | ||
var ( | ||
subnetID ids.ID | ||
nodeID ids.NodeID | ||
) | ||
copy(subnetID[:], key) | ||
height := unpackIterableHeight(key[ids.IDLen:]) | ||
copy(nodeID[:], key[diffKeyNodeIDOffset:]) | ||
return subnetID, height, nodeID, nil | ||
} | ||
|
||
func marshalWeightDiff(diff *ValidatorWeightDiff) []byte { | ||
value := make([]byte, weightValueLength) | ||
if diff.Decrease { | ||
value[0] = database.BoolTrue | ||
} | ||
binary.BigEndian.PutUint64(value[database.BoolSize:], diff.Amount) | ||
return value | ||
} | ||
|
||
func unmarshalWeightDiff(value []byte) (*ValidatorWeightDiff, error) { | ||
if len(value) != weightValueLength { | ||
return nil, errUnexpectedWeightValueLength | ||
} | ||
return &ValidatorWeightDiff{ | ||
Decrease: value[0] == database.BoolTrue, | ||
Amount: binary.BigEndian.Uint64(value[database.BoolSize:]), | ||
}, nil | ||
} | ||
|
||
// Note: [height] is encoded as a bit flipped big endian number so that | ||
// iterating lexicographically results in iterating in decreasing heights. | ||
// | ||
// Invariant: [key] has sufficient length | ||
func packIterableHeight(key []byte, height uint64) { | ||
binary.BigEndian.PutUint64(key, ^height) | ||
} | ||
|
||
// Because we bit flip the height when constructing the key, we must remember to | ||
// bip flip again here. | ||
// | ||
// Invariant: [key] has sufficient length | ||
func unpackIterableHeight(key []byte) uint64 { | ||
return ^binary.BigEndian.Uint64(key) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package state | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/thepudds/fzgen/fuzzer" | ||
|
||
"github.com/ava-labs/avalanchego/database/memdb" | ||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
func FuzzMarshalDiffKey(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
require := require.New(t) | ||
|
||
var ( | ||
subnetID ids.ID | ||
height uint64 | ||
nodeID ids.NodeID | ||
) | ||
fz := fuzzer.NewFuzzer(data) | ||
fz.Fill(&subnetID, &height, &nodeID) | ||
|
||
key := marshalDiffKey(subnetID, height, nodeID) | ||
parsedSubnetID, parsedHeight, parsedNodeID, err := unmarshalDiffKey(key) | ||
require.NoError(err) | ||
require.Equal(subnetID, parsedSubnetID) | ||
require.Equal(height, parsedHeight) | ||
require.Equal(nodeID, parsedNodeID) | ||
}) | ||
} | ||
|
||
func FuzzUnmarshalDiffKey(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, key []byte) { | ||
require := require.New(t) | ||
|
||
subnetID, height, nodeID, err := unmarshalDiffKey(key) | ||
if err != nil { | ||
require.ErrorIs(err, errUnexpectedDiffKeyLength) | ||
return | ||
} | ||
|
||
formattedKey := marshalDiffKey(subnetID, height, nodeID) | ||
require.Equal(key, formattedKey) | ||
}) | ||
} | ||
|
||
func TestDiffIteration(t *testing.T) { | ||
require := require.New(t) | ||
|
||
db := memdb.New() | ||
|
||
subnetID0 := ids.GenerateTestID() | ||
subnetID1 := ids.GenerateTestID() | ||
|
||
nodeID0 := ids.NodeID{0x00} | ||
nodeID1 := ids.NodeID{0x01} | ||
|
||
subnetID0Height0NodeID0 := marshalDiffKey(subnetID0, 0, nodeID0) | ||
subnetID0Height1NodeID0 := marshalDiffKey(subnetID0, 1, nodeID0) | ||
subnetID0Height1NodeID1 := marshalDiffKey(subnetID0, 1, nodeID1) | ||
|
||
subnetID1Height0NodeID0 := marshalDiffKey(subnetID1, 0, nodeID0) | ||
subnetID1Height1NodeID0 := marshalDiffKey(subnetID1, 1, nodeID0) | ||
subnetID1Height1NodeID1 := marshalDiffKey(subnetID1, 1, nodeID1) | ||
|
||
require.NoError(db.Put(subnetID0Height0NodeID0, nil)) | ||
require.NoError(db.Put(subnetID0Height1NodeID0, nil)) | ||
require.NoError(db.Put(subnetID0Height1NodeID1, nil)) | ||
require.NoError(db.Put(subnetID1Height0NodeID0, nil)) | ||
require.NoError(db.Put(subnetID1Height1NodeID0, nil)) | ||
require.NoError(db.Put(subnetID1Height1NodeID1, nil)) | ||
|
||
{ | ||
it := db.NewIteratorWithStartAndPrefix(marshalStartDiffKey(subnetID0, 0), subnetID0[:]) | ||
defer it.Release() | ||
|
||
expectedKeys := [][]byte{ | ||
subnetID0Height0NodeID0, | ||
} | ||
for _, expectedKey := range expectedKeys { | ||
require.True(it.Next()) | ||
require.Equal(expectedKey, it.Key()) | ||
} | ||
require.False(it.Next()) | ||
require.NoError(it.Error()) | ||
} | ||
|
||
{ | ||
it := db.NewIteratorWithStartAndPrefix(marshalStartDiffKey(subnetID0, 1), subnetID0[:]) | ||
defer it.Release() | ||
|
||
expectedKeys := [][]byte{ | ||
subnetID0Height1NodeID0, | ||
subnetID0Height1NodeID1, | ||
subnetID0Height0NodeID0, | ||
} | ||
for _, expectedKey := range expectedKeys { | ||
require.True(it.Next()) | ||
require.Equal(expectedKey, it.Key()) | ||
} | ||
require.False(it.Next()) | ||
require.NoError(it.Error()) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.