Skip to content

Commit 5381d56

Browse files
Add test for proposervm BuildBlock after bootstrapping (#2876)
Co-authored-by: yacovm <[email protected]>
1 parent 23c5625 commit 5381d56

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

vms/proposervm/vm_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,3 +2660,188 @@ func TestSelectChildPChainHeight(t *testing.T) {
26602660
})
26612661
}
26622662
}
2663+
2664+
// This tests the case where a chain has bootstrapped to a last accepted block
2665+
// which references a P-Chain height that is not locally accepted yet.
2666+
func TestBootstrappingAheadOfPChainBuildBlockRegression(t *testing.T) {
2667+
t.Skip("FIXME")
2668+
2669+
require := require.New(t)
2670+
2671+
// innerVMBlks is appended to throughout the test, which modifies the
2672+
// behavior of coreVM.
2673+
innerVMBlks := []*snowmantest.Block{
2674+
snowmantest.Genesis,
2675+
}
2676+
2677+
var innerToEngine chan<- common.Message
2678+
coreVM := &blocktest.VM{
2679+
VM: enginetest.VM{
2680+
T: t,
2681+
InitializeF: func(_ context.Context, _ *snow.Context, _ database.Database, _ []byte, _ []byte, _ []byte, toEngine chan<- common.Message, _ []*common.Fx, _ common.AppSender) error {
2682+
innerToEngine = toEngine
2683+
return nil
2684+
},
2685+
},
2686+
ParseBlockF: func(_ context.Context, blkBytes []byte) (snowman.Block, error) {
2687+
for _, blk := range innerVMBlks {
2688+
if bytes.Equal(blk.Bytes(), blkBytes) {
2689+
return blk, nil
2690+
}
2691+
}
2692+
return nil, errUnknownBlock
2693+
},
2694+
GetBlockF: func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
2695+
for _, blk := range innerVMBlks {
2696+
if blk.Status == snowtest.Accepted && blk.ID() == blkID {
2697+
return blk, nil
2698+
}
2699+
}
2700+
return nil, database.ErrNotFound
2701+
},
2702+
LastAcceptedF: func(context.Context) (ids.ID, error) {
2703+
var (
2704+
lastAcceptedID ids.ID
2705+
lastAcceptedHeight uint64
2706+
)
2707+
for _, blk := range innerVMBlks {
2708+
if blk.Status == snowtest.Accepted && blk.Height() >= lastAcceptedHeight {
2709+
lastAcceptedID = blk.ID()
2710+
lastAcceptedHeight = blk.Height()
2711+
}
2712+
}
2713+
return lastAcceptedID, nil
2714+
},
2715+
}
2716+
2717+
proVM := New(
2718+
coreVM,
2719+
Config{
2720+
Upgrades: upgrade.Config{
2721+
ApricotPhase4Time: snowmantest.GenesisTimestamp,
2722+
ApricotPhase4MinPChainHeight: 0,
2723+
DurangoTime: snowmantest.GenesisTimestamp,
2724+
},
2725+
MinBlkDelay: DefaultMinBlockDelay,
2726+
NumHistoricalBlocks: DefaultNumHistoricalBlocks,
2727+
StakingLeafSigner: pTestSigner,
2728+
StakingCertLeaf: pTestCert,
2729+
Registerer: prometheus.NewRegistry(),
2730+
},
2731+
)
2732+
proVM.Set(snowmantest.GenesisTimestamp)
2733+
2734+
// We mark the P-chain as having synced to height=1.
2735+
const currentPChainHeight = 1
2736+
valState := &validatorstest.State{
2737+
T: t,
2738+
GetMinimumHeightF: func(context.Context) (uint64, error) {
2739+
return currentPChainHeight, nil
2740+
},
2741+
GetCurrentHeightF: func(context.Context) (uint64, error) {
2742+
return currentPChainHeight, nil
2743+
},
2744+
GetValidatorSetF: func(_ context.Context, height uint64, _ ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
2745+
if height > currentPChainHeight {
2746+
return nil, fmt.Errorf("requested height (%d) > current P-chain height (%d)", height, currentPChainHeight)
2747+
}
2748+
return map[ids.NodeID]*validators.GetValidatorOutput{
2749+
proVM.ctx.NodeID: {
2750+
NodeID: proVM.ctx.NodeID,
2751+
Weight: 10,
2752+
},
2753+
}, nil
2754+
},
2755+
}
2756+
2757+
ctx := snowtest.Context(t, ids.ID{1})
2758+
ctx.NodeID = ids.NodeIDFromCert(pTestCert)
2759+
ctx.ValidatorState = valState
2760+
2761+
db := prefixdb.New([]byte{0}, memdb.New())
2762+
2763+
toEngine := make(chan common.Message, 1)
2764+
require.NoError(proVM.Initialize(
2765+
context.Background(),
2766+
ctx,
2767+
db,
2768+
nil,
2769+
nil,
2770+
nil,
2771+
toEngine,
2772+
nil,
2773+
nil,
2774+
))
2775+
defer func() {
2776+
require.NoError(proVM.Shutdown(context.Background()))
2777+
}()
2778+
2779+
require.NoError(proVM.SetState(context.Background(), snow.Bootstrapping))
2780+
2781+
// During bootstrapping, the first post-fork block is verified against the
2782+
// P-chain height, so we provide a valid height.
2783+
innerBlock1 := snowmantest.BuildChild(snowmantest.Genesis)
2784+
innerVMBlks = append(innerVMBlks, innerBlock1)
2785+
statelessBlock1, err := statelessblock.BuildUnsigned(
2786+
snowmantest.GenesisID,
2787+
snowmantest.GenesisTimestamp,
2788+
currentPChainHeight,
2789+
innerBlock1.Bytes(),
2790+
)
2791+
require.NoError(err)
2792+
2793+
block1, err := proVM.ParseBlock(context.Background(), statelessBlock1.Bytes())
2794+
require.NoError(err)
2795+
2796+
require.NoError(block1.Verify(context.Background()))
2797+
require.NoError(block1.Accept(context.Background()))
2798+
2799+
// During bootstrapping, the additional post-fork blocks are not verified
2800+
// against the local P-chain height, so even if we provide a height higher
2801+
// than our P-chain height, verification will succeed.
2802+
innerBlock2 := snowmantest.BuildChild(innerBlock1)
2803+
innerVMBlks = append(innerVMBlks, innerBlock2)
2804+
statelessBlock2, err := statelessblock.Build(
2805+
statelessBlock1.ID(),
2806+
statelessBlock1.Timestamp(),
2807+
currentPChainHeight+1,
2808+
pTestCert,
2809+
innerBlock2.Bytes(),
2810+
ctx.ChainID,
2811+
pTestSigner,
2812+
)
2813+
require.NoError(err)
2814+
2815+
block2, err := proVM.ParseBlock(context.Background(), statelessBlock2.Bytes())
2816+
require.NoError(err)
2817+
2818+
require.NoError(block2.Verify(context.Background()))
2819+
require.NoError(block2.Accept(context.Background()))
2820+
2821+
require.NoError(proVM.SetPreference(context.Background(), statelessBlock2.ID()))
2822+
2823+
// At this point, the VM has a last accepted block with a P-chain height
2824+
// greater than our locally accepted P-chain.
2825+
require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
2826+
2827+
// If the inner VM requests building a block, the proposervm passes that
2828+
// message to the consensus engine. This is really the source of the issue,
2829+
// as the proposervm is not currently in a state where it can correctly
2830+
// build any blocks.
2831+
innerToEngine <- common.PendingTxs
2832+
require.Equal(common.PendingTxs, <-toEngine)
2833+
2834+
innerBlock3 := snowmantest.BuildChild(innerBlock2)
2835+
innerVMBlks = append(innerVMBlks, innerBlock3)
2836+
2837+
coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
2838+
return innerBlock3, nil
2839+
}
2840+
2841+
// Attempting to build a block now errors with an unexpected error. This
2842+
// results in dropping the build block request, which breaks the invariant
2843+
// that BuildBlock will be called at least once after sending a PendingTxs
2844+
// message on the ToEngine channel.
2845+
_, err = proVM.BuildBlock(context.Background())
2846+
require.NoError(err)
2847+
}

0 commit comments

Comments
 (0)