mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-24 12:27:18 +00:00
44973b0bb3
* in progress... * in progress... * remove log * log root * Revert "Auxiliary commit to revert individual files from f12a609ea2a2f1e87e97321f3a717cd324b5ae97" This reverts commit 5ae35edb6477d8d0ea4e94b273efc6590484da85. * cleanup * remove log * remove whitespace * remove logs * more stuff * copy * always rebuild trie * revert * add state * init state * fix all * uintptr * move slice to new package * lock in `Detach` * remove constraint * reorder * blockroots and stateroots * fill roots in empty() * fix hasher * implement slice for balances and inactivity scores * detach in setters * Revert "implement slice for balances and inactivity scores" This reverts commit 59eb9df8d766cb1c44a7eb5b3f5e3c042249943d. # Conflicts: # beacon-chain/state/state-native/setters_validator.go * use counter to track states * typos * rename interface * balances * gauge * some improvements * first try with map * fix * inactivity scores in progress * fix test # Conflicts: # beacon-chain/state/state-native/helpers_test.go * test fixes * ToProto fix * copy roots * validators * build fixes * fix bug in `ToProto` * fix fuzz test * fix bug in slice getters * fix state equality checks * make tests pass * make tests pass * more test updates * Revert "Auxiliary commit to revert individual files from 34e7344bff08a589e6341bb1829e3cb74159e878" This reverts commit ecd64efa8917f37ca41460e0356ff007fe55dd9d. * Revert "make tests pass" This reverts commit 0cf00f19eecf4678cd2b866dd107f3179d0426ef. * Revert "make tests pass" This reverts commit 521b65e1d2e13be3d720f333008b6838a8e78878. * pass tests * deepequal identifiable types * Deflake `cloners_test.go` * feature flag for block roots * feature flag * remove recursive locks * reduce complexity of rootSelector * fix randao mixes root * some fixes * revisit tests * revert change to field trie helpers * initialize field map for tests * remove whitespace * initialize roots with proper length * more fixes * out of bounds message fix * optimize length calculation * remove call to Len in PubkeyAtIndex * don't log deposits * unit tests * unit tests * fix * comments * test fixes * id * remove Enumerator interface * review feedback * simplify field trie * bring back fieldtrie package * fix bazel file * use handle32ByteArrays for root computation * fix locks * metrics * bzl * simplify some things * use htr in state test * remove code from require package * gzl * more htr * Fuzzing of the multi-value slice * assert values * getter optimizations * use At when reading from validators * Nishant's review * restore safe copy * remove empty line * build fix * restore how we get root at index for deafult mode * more review comments * optimize default behavior * simplify Slice calls * test fix * remove unnecessary package * remove unused setter * make fieldMap unexported * some improvements in state package * call `Slice` instead of manually copying * unlock in ReadFromEveryValidator * Potuz's comments * lock the state when reading from all validators # Conflicts: # beacon-chain/state/state-native/getters_validator.go * add back preston's changes * add index --------- Co-authored-by: Potuz <potuz@prysmaticlabs.com> Co-authored-by: nisdas <nishdas93@gmail.com> Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
1047 lines
38 KiB
Go
1047 lines
38 KiB
Go
package state_native
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"testing"
|
|
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
|
)
|
|
|
|
func TestStateReferenceSharing_Finalizer_Phase0(t *testing.T) {
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
|
|
|
func() {
|
|
// Create object in a different scope for GC
|
|
b := a.Copy()
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
|
_ = b
|
|
}()
|
|
|
|
runtime.GC() // Should run finalizer on object b
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
|
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
|
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar"))))
|
|
if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 {
|
|
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
|
}
|
|
}
|
|
|
|
func TestStateReferenceSharing_Finalizer_Altair(t *testing.T) {
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
|
|
|
func() {
|
|
// Create object in a different scope for GC
|
|
b := a.Copy()
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
|
_ = b
|
|
}()
|
|
|
|
runtime.GC() // Should run finalizer on object b
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
|
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
|
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar"))))
|
|
if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 {
|
|
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
|
}
|
|
}
|
|
|
|
func TestStateReferenceSharing_Finalizer_Bellatrix(t *testing.T) {
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
|
|
|
func() {
|
|
// Create object in a different scope for GC
|
|
b := a.Copy()
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
|
_ = b
|
|
}()
|
|
|
|
runtime.GC() // Should run finalizer on object b
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
|
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
|
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar"))))
|
|
if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 {
|
|
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
|
}
|
|
}
|
|
|
|
func TestStateReferenceSharing_Finalizer_Capella(t *testing.T) {
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
|
|
|
func() {
|
|
// Create object in a different scope for GC
|
|
b := a.Copy()
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
|
_ = b
|
|
}()
|
|
|
|
runtime.GC() // Should run finalizer on object b
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
|
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
|
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar"))))
|
|
if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 {
|
|
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
|
}
|
|
}
|
|
|
|
func TestStateReferenceSharing_Finalizer_Deneb(t *testing.T) {
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
s, err := InitializeFromProtoUnsafeDeneb(ðpb.BeaconStateDeneb{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected a single reference for RANDAO mixes")
|
|
|
|
func() {
|
|
// Create object in a different scope for GC
|
|
b := a.Copy()
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 references to RANDAO mixes")
|
|
_ = b
|
|
}()
|
|
|
|
runtime.GC() // Should run finalizer on object b
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 1 shared reference to RANDAO mixes!")
|
|
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes")
|
|
require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar"))))
|
|
if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 {
|
|
t.Error("Expected 1 shared reference to RANDAO mix for both a and b")
|
|
}
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRootsMutation_Phase0(t *testing.T) {
|
|
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
BlockRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
StateRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.BlockRoots, 2)
|
|
assertRefCount(t, a, types.StateRoots, 2)
|
|
assertRefCount(t, b, types.BlockRoots, 2)
|
|
assertRefCount(t, b, types.StateRoots, 2)
|
|
|
|
// Assert shared state.
|
|
blockRootsA := a.BlockRoots()
|
|
stateRootsA := a.StateRoots()
|
|
blockRootsB := b.BlockRoots()
|
|
stateRootsB := b.StateRoots()
|
|
assertValFound(t, blockRootsA, root1[:])
|
|
assertValFound(t, blockRootsB, root1[:])
|
|
assertValFound(t, stateRootsA, root1[:])
|
|
assertValFound(t, stateRootsB, root1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
|
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValNotFound(t, a.BlockRoots(), root1[:])
|
|
assertValNotFound(t, a.StateRoots(), root1[:])
|
|
assertValFound(t, a.BlockRoots(), root2[:])
|
|
assertValFound(t, a.StateRoots(), root2[:])
|
|
assertValFound(t, b.BlockRoots(), root1[:])
|
|
assertValFound(t, b.StateRoots(), root1[:])
|
|
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
|
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
assertRefCount(t, b, types.BlockRoots, 1)
|
|
assertRefCount(t, b, types.StateRoots, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRootsMutation_Altair(t *testing.T) {
|
|
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{
|
|
BlockRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
StateRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.BlockRoots, 2)
|
|
assertRefCount(t, a, types.StateRoots, 2)
|
|
assertRefCount(t, b, types.BlockRoots, 2)
|
|
assertRefCount(t, b, types.StateRoots, 2)
|
|
|
|
// Assert shared state.
|
|
blockRootsA := a.BlockRoots()
|
|
stateRootsA := a.StateRoots()
|
|
blockRootsB := b.BlockRoots()
|
|
stateRootsB := b.StateRoots()
|
|
assertValFound(t, blockRootsA, root1[:])
|
|
assertValFound(t, blockRootsB, root1[:])
|
|
assertValFound(t, stateRootsA, root1[:])
|
|
assertValFound(t, stateRootsB, root1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
|
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValNotFound(t, a.BlockRoots(), root1[:])
|
|
assertValNotFound(t, a.StateRoots(), root1[:])
|
|
assertValFound(t, a.BlockRoots(), root2[:])
|
|
assertValFound(t, a.StateRoots(), root2[:])
|
|
assertValFound(t, b.BlockRoots(), root1[:])
|
|
assertValFound(t, b.StateRoots(), root1[:])
|
|
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
|
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
assertRefCount(t, b, types.BlockRoots, 1)
|
|
assertRefCount(t, b, types.StateRoots, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRootsMutation_Bellatrix(t *testing.T) {
|
|
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{
|
|
BlockRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
StateRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.BlockRoots, 2)
|
|
assertRefCount(t, a, types.StateRoots, 2)
|
|
assertRefCount(t, b, types.BlockRoots, 2)
|
|
assertRefCount(t, b, types.StateRoots, 2)
|
|
|
|
// Assert shared state.
|
|
blockRootsA := a.BlockRoots()
|
|
stateRootsA := a.StateRoots()
|
|
blockRootsB := b.BlockRoots()
|
|
stateRootsB := b.StateRoots()
|
|
assertValFound(t, blockRootsA, root1[:])
|
|
assertValFound(t, blockRootsB, root1[:])
|
|
assertValFound(t, stateRootsA, root1[:])
|
|
assertValFound(t, stateRootsB, root1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
|
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValNotFound(t, a.BlockRoots(), root1[:])
|
|
assertValNotFound(t, a.StateRoots(), root1[:])
|
|
assertValFound(t, a.BlockRoots(), root2[:])
|
|
assertValFound(t, a.StateRoots(), root2[:])
|
|
assertValFound(t, b.BlockRoots(), root1[:])
|
|
assertValFound(t, b.StateRoots(), root1[:])
|
|
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
|
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
assertRefCount(t, b, types.BlockRoots, 1)
|
|
assertRefCount(t, b, types.StateRoots, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRootsMutation_Capella(t *testing.T) {
|
|
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{
|
|
BlockRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
StateRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.BlockRoots, 2)
|
|
assertRefCount(t, a, types.StateRoots, 2)
|
|
assertRefCount(t, b, types.BlockRoots, 2)
|
|
assertRefCount(t, b, types.StateRoots, 2)
|
|
|
|
// Assert shared state.
|
|
blockRootsA := a.BlockRoots()
|
|
stateRootsA := a.StateRoots()
|
|
blockRootsB := b.BlockRoots()
|
|
stateRootsB := b.StateRoots()
|
|
assertValFound(t, blockRootsA, root1[:])
|
|
assertValFound(t, blockRootsB, root1[:])
|
|
assertValFound(t, stateRootsA, root1[:])
|
|
assertValFound(t, stateRootsB, root1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
|
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValNotFound(t, a.BlockRoots(), root1[:])
|
|
assertValNotFound(t, a.StateRoots(), root1[:])
|
|
assertValFound(t, a.BlockRoots(), root2[:])
|
|
assertValFound(t, a.StateRoots(), root2[:])
|
|
assertValFound(t, b.BlockRoots(), root1[:])
|
|
assertValFound(t, b.StateRoots(), root1[:])
|
|
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
|
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
assertRefCount(t, b, types.BlockRoots, 1)
|
|
assertRefCount(t, b, types.StateRoots, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRootsMutation_Deneb(t *testing.T) {
|
|
root1, root2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeDeneb(ðpb.BeaconStateDeneb{
|
|
BlockRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
StateRoots: [][]byte{
|
|
root1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.BlockRoots, 2)
|
|
assertRefCount(t, a, types.StateRoots, 2)
|
|
assertRefCount(t, b, types.BlockRoots, 2)
|
|
assertRefCount(t, b, types.StateRoots, 2)
|
|
|
|
// Assert shared state.
|
|
blockRootsA := a.BlockRoots()
|
|
stateRootsA := a.StateRoots()
|
|
blockRootsB := b.BlockRoots()
|
|
stateRootsB := b.StateRoots()
|
|
assertValFound(t, blockRootsA, root1[:])
|
|
assertValFound(t, blockRootsB, root1[:])
|
|
assertValFound(t, stateRootsA, root1[:])
|
|
assertValFound(t, stateRootsB, root1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateBlockRootAtIndex(0, root2))
|
|
require.NoError(t, a.UpdateStateRootAtIndex(0, root2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValNotFound(t, a.BlockRoots(), root1[:])
|
|
assertValNotFound(t, a.StateRoots(), root1[:])
|
|
assertValFound(t, a.BlockRoots(), root2[:])
|
|
assertValFound(t, a.StateRoots(), root2[:])
|
|
assertValFound(t, b.BlockRoots(), root1[:])
|
|
assertValFound(t, b.StateRoots(), root1[:])
|
|
assert.DeepEqual(t, root2[:], a.BlockRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root2[:], a.StateRoots()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, root1[:], blockRootsB[0], "Unexpected mutation found")
|
|
assert.DeepEqual(t, root1[:], stateRootsB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.BlockRoots, 1)
|
|
assertRefCount(t, a, types.StateRoots, 1)
|
|
assertRefCount(t, b, types.BlockRoots, 1)
|
|
assertRefCount(t, b, types.StateRoots, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Phase0(t *testing.T) {
|
|
val1, val2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
RandaoMixes: [][]byte{
|
|
val1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.RandaoMixes, 2)
|
|
assertRefCount(t, b, types.RandaoMixes, 2)
|
|
|
|
// Assert shared state.
|
|
mixesA := a.RandaoMixes()
|
|
mixesB := b.RandaoMixes()
|
|
assertValFound(t, mixesA, val1[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValFound(t, a.RandaoMixes(), val2[:])
|
|
assertValNotFound(t, a.RandaoMixes(), val1[:])
|
|
assertValFound(t, b.RandaoMixes(), val1[:])
|
|
assertValNotFound(t, b.RandaoMixes(), val2[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
assertValNotFound(t, mixesB, val2[:])
|
|
assert.DeepEqual(t, val2[:], a.RandaoMixes()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, val1[:], mixesB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
assertRefCount(t, b, types.RandaoMixes, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Altair(t *testing.T) {
|
|
val1, val2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{
|
|
RandaoMixes: [][]byte{
|
|
val1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.RandaoMixes, 2)
|
|
assertRefCount(t, b, types.RandaoMixes, 2)
|
|
|
|
// Assert shared state.
|
|
mixesA := a.RandaoMixes()
|
|
mixesB := b.RandaoMixes()
|
|
assertValFound(t, mixesA, val1[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValFound(t, a.RandaoMixes(), val2[:])
|
|
assertValNotFound(t, a.RandaoMixes(), val1[:])
|
|
assertValFound(t, b.RandaoMixes(), val1[:])
|
|
assertValNotFound(t, b.RandaoMixes(), val2[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
assertValNotFound(t, mixesB, val2[:])
|
|
assert.DeepEqual(t, val2[:], a.RandaoMixes()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, val1[:], mixesB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
assertRefCount(t, b, types.RandaoMixes, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Bellatrix(t *testing.T) {
|
|
val1, val2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{
|
|
RandaoMixes: [][]byte{
|
|
val1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.RandaoMixes, 2)
|
|
assertRefCount(t, b, types.RandaoMixes, 2)
|
|
|
|
// Assert shared state.
|
|
mixesA := a.RandaoMixes()
|
|
mixesB := b.RandaoMixes()
|
|
assertValFound(t, mixesA, val1[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValFound(t, a.RandaoMixes(), val2[:])
|
|
assertValNotFound(t, a.RandaoMixes(), val1[:])
|
|
assertValFound(t, b.RandaoMixes(), val1[:])
|
|
assertValNotFound(t, b.RandaoMixes(), val2[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
assertValNotFound(t, mixesB, val2[:])
|
|
assert.DeepEqual(t, val2[:], a.RandaoMixes()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, val1[:], mixesB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
assertRefCount(t, b, types.RandaoMixes, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Capella(t *testing.T) {
|
|
val1, val2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{
|
|
RandaoMixes: [][]byte{
|
|
val1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.RandaoMixes, 2)
|
|
assertRefCount(t, b, types.RandaoMixes, 2)
|
|
|
|
// Assert shared state.
|
|
mixesA := a.RandaoMixes()
|
|
mixesB := b.RandaoMixes()
|
|
assertValFound(t, mixesA, val1[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValFound(t, a.RandaoMixes(), val2[:])
|
|
assertValNotFound(t, a.RandaoMixes(), val1[:])
|
|
assertValFound(t, b.RandaoMixes(), val1[:])
|
|
assertValNotFound(t, b.RandaoMixes(), val2[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
assertValNotFound(t, mixesB, val2[:])
|
|
assert.DeepEqual(t, val2[:], a.RandaoMixes()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, val1[:], mixesB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
assertRefCount(t, b, types.RandaoMixes, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Deneb(t *testing.T) {
|
|
val1, val2 := bytesutil.ToBytes32([]byte("foo")), bytesutil.ToBytes32([]byte("bar"))
|
|
s, err := InitializeFromProtoUnsafeDeneb(ðpb.BeaconStateDeneb{
|
|
RandaoMixes: [][]byte{
|
|
val1[:],
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
require.NoError(t, err)
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.RandaoMixes, 2)
|
|
assertRefCount(t, b, types.RandaoMixes, 2)
|
|
|
|
// Assert shared state.
|
|
mixesA := a.RandaoMixes()
|
|
mixesB := b.RandaoMixes()
|
|
assertValFound(t, mixesA, val1[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
|
|
// Mutator should only affect calling state: a.
|
|
require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2))
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
assertValFound(t, a.RandaoMixes(), val2[:])
|
|
assertValNotFound(t, a.RandaoMixes(), val1[:])
|
|
assertValFound(t, b.RandaoMixes(), val1[:])
|
|
assertValNotFound(t, b.RandaoMixes(), val2[:])
|
|
assertValFound(t, mixesB, val1[:])
|
|
assertValNotFound(t, mixesB, val2[:])
|
|
assert.DeepEqual(t, val2[:], a.RandaoMixes()[0], "Expected mutation not found")
|
|
assert.DeepEqual(t, val1[:], mixesB[0], "Unexpected mutation found")
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.RandaoMixes, 1)
|
|
assertRefCount(t, b, types.RandaoMixes, 1)
|
|
}
|
|
|
|
func TestStateReferenceCopy_NoUnexpectedAttestationsMutation(t *testing.T) {
|
|
assertAttFound := func(vals []*ethpb.PendingAttestation, val uint64) {
|
|
for i := range vals {
|
|
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
|
|
return
|
|
}
|
|
}
|
|
t.Log(string(debug.Stack()))
|
|
t.Fatalf("Expected attestation not found (%v), want: %v", vals, val)
|
|
}
|
|
assertAttNotFound := func(vals []*ethpb.PendingAttestation, val uint64) {
|
|
for i := range vals {
|
|
if reflect.DeepEqual(vals[i].AggregationBits, bitfield.NewBitlist(val)) {
|
|
t.Log(string(debug.Stack()))
|
|
t.Fatalf("Unexpected attestation found (%v): %v", vals, val)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.PreviousEpochAttestations, 1)
|
|
assertRefCount(t, a, types.CurrentEpochAttestations, 1)
|
|
|
|
// Update initial state.
|
|
atts := []*ethpb.PendingAttestation{
|
|
{AggregationBits: bitfield.NewBitlist(1)},
|
|
{AggregationBits: bitfield.NewBitlist(2)},
|
|
}
|
|
a.setPreviousEpochAttestations(atts[:1])
|
|
a.setCurrentEpochAttestations(atts[:1])
|
|
curAtt, err := a.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(curAtt), "Unexpected number of attestations")
|
|
preAtt, err := a.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(preAtt), "Unexpected number of attestations")
|
|
|
|
// Copy, increases reference count.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
assertRefCount(t, a, types.PreviousEpochAttestations, 2)
|
|
assertRefCount(t, a, types.CurrentEpochAttestations, 2)
|
|
assertRefCount(t, b, types.PreviousEpochAttestations, 2)
|
|
assertRefCount(t, b, types.CurrentEpochAttestations, 2)
|
|
prevAtts, err := b.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(prevAtts), "Unexpected number of attestations")
|
|
currAtts, err := b.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(currAtts), "Unexpected number of attestations")
|
|
|
|
// Assert shared state.
|
|
currAttsA, err := a.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsA, err := a.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
currAttsB, err := b.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsB, err := b.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
if len(currAttsA) != len(currAttsB) || len(currAttsA) < 1 {
|
|
t.Errorf("Unexpected number of attestations, want: %v", 1)
|
|
}
|
|
if len(prevAttsA) != len(prevAttsB) || len(prevAttsA) < 1 {
|
|
t.Errorf("Unexpected number of attestations, want: %v", 1)
|
|
}
|
|
assertAttFound(currAttsA, 1)
|
|
assertAttFound(prevAttsA, 1)
|
|
assertAttFound(currAttsB, 1)
|
|
assertAttFound(prevAttsB, 1)
|
|
|
|
// Extend state attestations.
|
|
require.NoError(t, a.AppendCurrentEpochAttestations(atts[1]))
|
|
require.NoError(t, a.AppendPreviousEpochAttestations(atts[1]))
|
|
curAtt, err = a.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(curAtt), "Unexpected number of attestations")
|
|
preAtt, err = a.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(preAtt), "Unexpected number of attestations")
|
|
currAttsA, err = a.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsA, err = a.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
currAttsB, err = b.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsB, err = b.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assertAttFound(currAttsA, 1)
|
|
assertAttFound(prevAttsA, 1)
|
|
assertAttFound(currAttsA, 2)
|
|
assertAttFound(prevAttsA, 2)
|
|
assertAttFound(currAttsB, 1)
|
|
assertAttFound(prevAttsB, 1)
|
|
assertAttNotFound(currAttsB, 2)
|
|
assertAttNotFound(prevAttsB, 2)
|
|
|
|
// Mutator should only affect calling state: a.
|
|
applyToEveryAttestation := func(state *BeaconState) {
|
|
// One MUST copy on write.
|
|
currEpochAtts, err := state.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
atts = make([]*ethpb.PendingAttestation, len(currEpochAtts))
|
|
copy(atts, currEpochAtts)
|
|
state.setCurrentEpochAttestations(atts)
|
|
currEpochAtts, err = state.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
for i := range currEpochAtts {
|
|
att := ethpb.CopyPendingAttestation(currEpochAtts[i])
|
|
att.AggregationBits = bitfield.NewBitlist(3)
|
|
currEpochAtts[i] = att
|
|
}
|
|
state.setCurrentEpochAttestations(currEpochAtts)
|
|
|
|
prevEpochAtts, err := state.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
atts = make([]*ethpb.PendingAttestation, len(prevEpochAtts))
|
|
copy(atts, prevEpochAtts)
|
|
state.setPreviousEpochAttestations(atts)
|
|
prevEpochAtts, err = state.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
for i := range prevEpochAtts {
|
|
att := ethpb.CopyPendingAttestation(prevEpochAtts[i])
|
|
att.AggregationBits = bitfield.NewBitlist(3)
|
|
prevEpochAtts[i] = att
|
|
}
|
|
state.setPreviousEpochAttestations(prevEpochAtts)
|
|
}
|
|
applyToEveryAttestation(a)
|
|
|
|
// Assert no shared state mutation occurred only on state a (copy on write).
|
|
currAttsA, err = a.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsA, err = a.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assertAttFound(currAttsA, 3)
|
|
assertAttFound(prevAttsA, 3)
|
|
assertAttNotFound(currAttsA, 1)
|
|
assertAttNotFound(prevAttsA, 1)
|
|
assertAttNotFound(currAttsA, 2)
|
|
assertAttNotFound(prevAttsA, 2)
|
|
// State b must be unaffected.
|
|
currAttsB, err = b.CurrentEpochAttestations()
|
|
require.NoError(t, err)
|
|
prevAttsB, err = b.PreviousEpochAttestations()
|
|
require.NoError(t, err)
|
|
assertAttNotFound(currAttsB, 3)
|
|
assertAttNotFound(prevAttsB, 3)
|
|
assertAttFound(currAttsB, 1)
|
|
assertAttFound(prevAttsB, 1)
|
|
assertAttNotFound(currAttsB, 2)
|
|
assertAttNotFound(prevAttsB, 2)
|
|
|
|
// Copy on write happened, reference counters are reset.
|
|
assertRefCount(t, a, types.CurrentEpochAttestations, 1)
|
|
assertRefCount(t, b, types.CurrentEpochAttestations, 1)
|
|
assertRefCount(t, a, types.PreviousEpochAttestations, 1)
|
|
assertRefCount(t, b, types.PreviousEpochAttestations, 1)
|
|
}
|
|
|
|
func TestValidatorReferences_RemainsConsistent_Phase0(t *testing.T) {
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Validators: []*ethpb.Validator{
|
|
{PublicKey: []byte{'A'}},
|
|
{PublicKey: []byte{'B'}},
|
|
{PublicKey: []byte{'C'}},
|
|
{PublicKey: []byte{'D'}},
|
|
{PublicKey: []byte{'E'}},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Create a second state.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Update First Validator.
|
|
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
|
|
|
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
|
// Modify all validators from copied state.
|
|
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
|
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
|
}))
|
|
|
|
// Ensure reference is properly accounted for.
|
|
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func TestValidatorReferences_RemainsConsistent_Altair(t *testing.T) {
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{
|
|
Validators: []*ethpb.Validator{
|
|
{PublicKey: []byte{'A'}},
|
|
{PublicKey: []byte{'B'}},
|
|
{PublicKey: []byte{'C'}},
|
|
{PublicKey: []byte{'D'}},
|
|
{PublicKey: []byte{'E'}},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Create a second state.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Update First Validator.
|
|
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
|
|
|
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
|
// Modify all validators from copied state.
|
|
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
|
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
|
}))
|
|
|
|
// Ensure reference is properly accounted for.
|
|
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func TestValidatorReferences_RemainsConsistent_Capella(t *testing.T) {
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{
|
|
Validators: []*ethpb.Validator{
|
|
{PublicKey: []byte{'A'}},
|
|
{PublicKey: []byte{'B'}},
|
|
{PublicKey: []byte{'C'}},
|
|
{PublicKey: []byte{'D'}},
|
|
{PublicKey: []byte{'E'}},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Create a second state.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Update First Validator.
|
|
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
|
|
|
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
|
// Modify all validators from copied state.
|
|
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
|
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
|
}))
|
|
|
|
// Ensure reference is properly accounted for.
|
|
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func TestValidatorReferences_RemainsConsistent_Deneb(t *testing.T) {
|
|
s, err := InitializeFromProtoUnsafeDeneb(ðpb.BeaconStateDeneb{
|
|
Validators: []*ethpb.Validator{
|
|
{PublicKey: []byte{'A'}},
|
|
{PublicKey: []byte{'B'}},
|
|
{PublicKey: []byte{'C'}},
|
|
{PublicKey: []byte{'D'}},
|
|
{PublicKey: []byte{'E'}},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Create a second state.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Update First Validator.
|
|
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
|
|
|
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
|
// Modify all validators from copied state.
|
|
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
|
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
|
}))
|
|
|
|
// Ensure reference is properly accounted for.
|
|
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func TestValidatorReferences_RemainsConsistent_Bellatrix(t *testing.T) {
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{
|
|
Validators: []*ethpb.Validator{
|
|
{PublicKey: []byte{'A'}},
|
|
{PublicKey: []byte{'B'}},
|
|
{PublicKey: []byte{'C'}},
|
|
{PublicKey: []byte{'D'}},
|
|
{PublicKey: []byte{'E'}},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
a, ok := s.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Create a second state.
|
|
copied := a.Copy()
|
|
b, ok := copied.(*BeaconState)
|
|
require.Equal(t, true, ok)
|
|
|
|
// Update First Validator.
|
|
assert.NoError(t, a.UpdateValidatorAtIndex(0, ðpb.Validator{PublicKey: []byte{'Z'}}))
|
|
|
|
assert.DeepNotEqual(t, a.Validators()[0], b.Validators()[0], "validators are equal when they are supposed to be different")
|
|
// Modify all validators from copied state.
|
|
assert.NoError(t, b.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
|
return true, ðpb.Validator{PublicKey: []byte{'V'}}, nil
|
|
}))
|
|
|
|
// Ensure reference is properly accounted for.
|
|
assert.NoError(t, a.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
|
assert.NotEqual(t, bytesutil.ToBytes48([]byte{'V'}), val.PublicKey())
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
// assertRefCount checks whether reference count for a given state
|
|
// at a given index is equal to expected amount.
|
|
func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uint) {
|
|
if cnt := b.sharedFieldReferences[idx].Refs(); cnt != want {
|
|
t.Errorf("Unexpected count of references for index %d, want: %v, got: %v", idx, want, cnt)
|
|
}
|
|
}
|
|
|
|
// assertValFound checks whether item with a given value exists in list.
|
|
func assertValFound(t *testing.T, vals [][]byte, val []byte) {
|
|
for i := range vals {
|
|
if reflect.DeepEqual(vals[i], val) {
|
|
return
|
|
}
|
|
}
|
|
t.Log(string(debug.Stack()))
|
|
t.Fatalf("Expected value not found (%v), want: %v", vals, val)
|
|
}
|
|
|
|
// assertValNotFound checks whether item with a given value doesn't exist in list.
|
|
func assertValNotFound(t *testing.T, vals [][]byte, val []byte) {
|
|
for i := range vals {
|
|
if reflect.DeepEqual(vals[i], val) {
|
|
t.Log(string(debug.Stack()))
|
|
t.Errorf("Unexpected value found (%v),: %v", vals, val)
|
|
return
|
|
}
|
|
}
|
|
}
|