-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add Balance Field Trie #9793
Conversation
@@ -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) { |
There was a problem hiding this comment.
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/types/types.go
Outdated
@@ -82,6 +87,17 @@ func (f FieldIndex) String(stateVersion int) string { | |||
} | |||
} | |||
|
|||
// CompressedLength returns the compressed length of the type(number of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// CompressedLength returns the compressed length of the type(number of | |
// CompressedLength returns the compressed length of the type (number of |
There was a problem hiding this 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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function needs tests.
beacon-chain/state/v1/state_trie.go
Outdated
if balLimit == 0 { | ||
if len(b.state.Balances) == 0 { | ||
balLimit = 1 | ||
} else { | ||
balLimit = uint64(len(b.state.Balances)) | ||
} | ||
} |
There was a problem hiding this comment.
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.
// We only select from a field value, if the | ||
// index exists in our element list. If it doesn't | ||
// we assume a zero value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 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. |
You also need to run |
@@ -49,15 +51,17 @@ func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements inte | |||
reference: stateutil.NewRef(1), | |||
RWMutex: new(sync.RWMutex), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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++ { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 | ||
} |
There was a problem hiding this comment.
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...) | ||
} |
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
What type of PR is this?
Feature
What does this PR do? Why is it needed?
it from recomputing everything for each block.
Which issues(s) does this PR fix?
N.A
Other notes for review