|
|
|
|
@@ -16,533 +16,121 @@ import (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestStateReferenceSharing_Finalizer_Phase0(t *testing.T) {
|
|
|
|
|
// This test showcases the logic on the RandaoMixes field with the GC finalizer.
|
|
|
|
|
// This test showcases the logic on the slashings field with the GC finalizer.
|
|
|
|
|
|
|
|
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
|
|
|
s, err := InitializeFromProtoUnsafePhase0(ðpb.BeaconState{Slashings: []uint64{123}})
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected a single reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 references to slashings")
|
|
|
|
|
_ = 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!")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 1 shared reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 shared references to slashings")
|
|
|
|
|
require.NoError(t, b.UpdateSlashingsAtIndex(0, 456))
|
|
|
|
|
if b.sharedFieldReferences[types.Slashings].Refs() != 1 || a.sharedFieldReferences[types.Slashings].Refs() != 1 {
|
|
|
|
|
t.Error("Expected 1 shared reference to slashings 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.
|
|
|
|
|
// This test showcases the logic on the slashings field with the GC finalizer.
|
|
|
|
|
|
|
|
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
|
|
|
s, err := InitializeFromProtoUnsafeAltair(ðpb.BeaconStateAltair{Slashings: []uint64{123}})
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected a single reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 references to slashings")
|
|
|
|
|
_ = 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!")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 1 shared reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 shared references to slashings")
|
|
|
|
|
require.NoError(t, b.UpdateSlashingsAtIndex(0, 456))
|
|
|
|
|
if b.sharedFieldReferences[types.Slashings].Refs() != 1 || a.sharedFieldReferences[types.Slashings].Refs() != 1 {
|
|
|
|
|
t.Error("Expected 1 shared reference to slashings 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.
|
|
|
|
|
// This test showcases the logic on the slashings field with the GC finalizer.
|
|
|
|
|
|
|
|
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
|
|
|
s, err := InitializeFromProtoUnsafeBellatrix(ðpb.BeaconStateBellatrix{Slashings: []uint64{123}})
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected a single reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 references to slashings")
|
|
|
|
|
_ = 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!")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 1 shared reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 shared references to slashings")
|
|
|
|
|
require.NoError(t, b.UpdateSlashingsAtIndex(0, 456))
|
|
|
|
|
if b.sharedFieldReferences[types.Slashings].Refs() != 1 || a.sharedFieldReferences[types.Slashings].Refs() != 1 {
|
|
|
|
|
t.Error("Expected 1 shared reference to slashings 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.
|
|
|
|
|
// This test showcases the logic on the slashings field with the GC finalizer.
|
|
|
|
|
|
|
|
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{RandaoMixes: [][]byte{[]byte("foo")}})
|
|
|
|
|
s, err := InitializeFromProtoUnsafeCapella(ðpb.BeaconStateCapella{Slashings: []uint64{123}})
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected a single reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 references to slashings")
|
|
|
|
|
_ = 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!")
|
|
|
|
|
assert.Equal(t, uint(1), a.sharedFieldReferences[types.Slashings].Refs(), "Expected 1 shared reference to slashings")
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
assert.Equal(t, uint(2), b.sharedFieldReferences[types.Slashings].Refs(), "Expected 2 shared references to slashings")
|
|
|
|
|
require.NoError(t, b.UpdateSlashingsAtIndex(0, 456))
|
|
|
|
|
if b.sharedFieldReferences[types.Slashings].Refs() != 1 || a.sharedFieldReferences[types.Slashings].Refs() != 1 {
|
|
|
|
|
t.Error("Expected 1 shared reference to slashings 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_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_NoUnexpectedAttestationsMutation(t *testing.T) {
|
|
|
|
|
assertAttFound := func(vals []*ethpb.PendingAttestation, val uint64) {
|
|
|
|
|
for i := range vals {
|
|
|
|
|
@@ -855,25 +443,3 @@ func assertRefCount(t *testing.T, b *BeaconState, idx types.FieldIndex, want uin
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|