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 Balance Field Trie #9793

Merged
merged 32 commits into from
Nov 19, 2021
Merged

Add Balance Field Trie #9793

merged 32 commits into from
Nov 19, 2021

Conversation

nisdas
Copy link
Member

@nisdas nisdas commented Oct 18, 2021

What type of PR is this?

Feature

What does this PR do? Why is it needed?

  • Adds in a balance field trie for out state. This will allow the beacon state to cache the relevant leaves of the trie and prevent
    it from recomputing everything for each block.
  • Add Tests
  • Gate behind feature flag.

Which issues(s) does this PR fix?

N.A

Other notes for review

@nisdas nisdas marked this pull request as ready for review October 21, 2021 14:57
@nisdas nisdas requested a review from a team as a code owner October 21, 2021 14:57
@@ -113,6 +114,51 @@ func Pack(serializedItems [][]byte) ([][]byte, error) {
return chunks, nil
}

// PackByChunk a given byte array's final chunk with zeroes if needed.
func PackByChunk(serializedItems [][]byte) ([][bytesPerChunk]byte, error) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of Pack except that we enforce that [32]byte roots are returned at the end instead of a slice. This copy will be the canonical one once #9792 is merged in.

beacon-chain/state/fieldtrie/field_trie.go Show resolved Hide resolved
@@ -82,6 +87,17 @@ func (f FieldIndex) String(stateVersion int) string {
}
}

// CompressedLength returns the compressed length of the type(number of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// CompressedLength returns the compressed length of the type(number of
// CompressedLength returns the compressed length of the type (number of

Copy link
Contributor

@rkapka rkapka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests to handleBalanceSlice.

@@ -141,3 +150,47 @@ func handlePendingAttestation(val []*ethpb.PendingAttestation, indices []uint64,
}
return roots, nil
}

func handleBalanceSlice(val []uint64, indices []uint64, convertAll bool) ([][32]byte, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs tests.

Comment on lines 328 to 334
if balLimit == 0 {
if len(b.state.Balances) == 0 {
balLimit = 1
} else {
balLimit = uint64(len(b.state.Balances))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? balLimit can be zero only if ValidatorRegistryLimit = 0 OR elemSize = 0, both of which are practically impossible.

Comment on lines 183 to 185
// We only select from a field value, if the
// index exists in our element list. If it doesn't
// we assume a zero value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// We only select from a field value, if the
// index exists in our element list. If it doesn't
// we assume a zero value.
// We are adding chunks in sets of 4, if the set is at the edge of the array
// then you will need to zero out the rest of the chunk.
// Ex : 41 indexes, so 41 % 4 = 1 .
// Therefore there are 3 indexes, which aren't there yet but we have to add in as a root.
// These are zeroed out.

@rkapka
Copy link
Contributor

rkapka commented Oct 29, 2021

You also need to run gofmt somewhere.

rkapka
rkapka previously approved these changes Oct 29, 2021
@@ -49,15 +51,17 @@ func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements inte
reference: stateutil.NewRef(1),
RWMutex: new(sync.RWMutex),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not part of this change set, but why do embed a *sync.RWMutex vs a sync.RWMutex? A RWMutex is usable as its zero value, so does not require initialization, whereas using a pointer type requires explicit initialization and risks nil pointers. So I assume the intent is for copies of the state trie share the same lock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is correct , the tries are shared between different states so the lock must be shared correctly too.

func handleBalanceSlice(val []uint64, indices []uint64, convertAll bool) ([][32]byte, error) {
if convertAll {
balancesMarshaling := make([][]byte, 0)
for i := 0; i < len(val); i++ {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: i feel like we should prefer:

for i := range(val) {
 ...
}

or in this case, since you aren't using the actual index:

for _, value := range(val) {
...
}

just because it "scans" faster when reading code.

return nil, errors.Errorf("Wanted type of %v but got %v",
reflect.TypeOf([]uint64{}).Name(), reflect.TypeOf(elements).Name())
}
return handleBalanceSlice(val, indices, convertAll)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outside the scope of this PR, I'm noticing that the scope of these helpers is really inconsistent. HandleEth1DataSlice is exported for no apparent reason, HandleByteArrays and HandleValidatorSlice are defined in a different package (stateutil) with no usages beyond the fieldtrie package.

@@ -113,6 +114,51 @@ func Pack(serializedItems [][]byte) ([][]byte, error) {
return chunks, nil
}

// PackByChunk a given byte array's final chunk with zeroes if needed.
func PackByChunk(serializedItems [][]byte) ([][bytesPerChunk]byte, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need some unit tests on this one

// we set it as numItems itself.
if j > numItems {
j = numItems
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right padding here is a little subtle and maybe worthy of comment. It works because ToBytes32 allocates a [32]byte (which has a zero value of 32 empty-bytes) and then copies the slice into that value. This condition results in the length of the slice passed to ToBytes32 to be less than 32 bytes for the final iteration over orderedItems when len(orderedItems) % 32 != 0.

var orderedItems []byte
for _, item := range serializedItems {
orderedItems = append(orderedItems, item...)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of doing the extra loop at the top of the function to check if areAllEmpty, could you let that condition get to right after this loop and then add this check?

if len(orderedItems) == 0 {
    return [][bytesPerChunk]byte{emptyChunk}, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example showing that a flattened 2d array consisting of all empty sub-arrays would be len = 0:
https://play.golang.org/p/onJkAnvnqfj

"github.com/prysmaticlabs/prysm/config/features"
)

func TestMain(m *testing.M) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this? I remember @prestonvanloon and I saw something similar back in hf1 branch and decided to delete it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basically runs all tests in that package with those flags pre-loaded. Since this touches HTR, I wanted to run them through spec tests here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason it has to call TestMain than an explicit name like TestEpochProcessWithBalanceTrie ?

Copy link
Member Author

@nisdas nisdas Nov 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this has an explanation for why it is that way: https://pkg.go.dev/testing#hdr-Main

This basically allows for test setup and cleanup rather than doing it per test for the whole package.

"github.com/prysmaticlabs/prysm/config/features"
)

func TestMain(m *testing.M) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

"github.com/prysmaticlabs/prysm/config/features"
)

func TestMain(m *testing.M) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

rkapka
rkapka previously approved these changes Nov 5, 2021
@nisdas nisdas merged commit 0ade1f1 into develop Nov 19, 2021
@delete-merged-branch delete-merged-branch bot deleted the addBalanceFieldTrie branch November 19, 2021 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants