From 9833b45b3b766813d36df2811d35d2bd8942897e Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Sat, 23 Mar 2024 22:21:23 -0400 Subject: [PATCH] Revert removal of legacy P-chain block parsing (#2866) --- vms/platformvm/state/state.go | 34 ++++++++- vms/platformvm/state/state_test.go | 111 +++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index 77047389c1c1..45360a13b764 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -21,6 +21,7 @@ import ( "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/uptime" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils" @@ -178,6 +179,16 @@ type State interface { Close() error } +// Prior to https://github.com/ava-labs/avalanchego/pull/1719, blocks were +// stored as a map from blkID to stateBlk. Nodes synced prior to this PR may +// still have blocks partially stored using this legacy format. +// +// TODO: Remove after v1.12.x is activated +type stateBlk struct { + Bytes []byte `serialize:"true"` + Status choices.Status `serialize:"true"` +} + /* * VMDB * |-. validators @@ -1789,7 +1800,7 @@ func (s *state) GetStatelessBlock(blockID ids.ID) (block.Block, error) { return nil, err } - blk, err := block.Parse(block.GenesisCodec, blkBytes) + blk, _, err := parseStoredBlock(blkBytes) if err != nil { return nil, err } @@ -2260,3 +2271,24 @@ func (s *state) writeMetadata() error { } return nil } + +// Returns the block and whether it is a [stateBlk]. +// Invariant: blkBytes is safe to parse with blocks.GenesisCodec +// +// TODO: Remove after v1.12.x is activated +func parseStoredBlock(blkBytes []byte) (block.Block, bool, error) { + // Attempt to parse as blocks.Block + blk, err := block.Parse(block.GenesisCodec, blkBytes) + if err == nil { + return blk, false, nil + } + + // Fallback to [stateBlk] + blkState := stateBlk{} + if _, err := block.GenesisCodec.Unmarshal(blkBytes, &blkState); err != nil { + return nil, false, err + } + + blk, err = block.Parse(block.GenesisCodec, blkState.Bytes) + return blk, true, err +} diff --git a/vms/platformvm/state/state_test.go b/vms/platformvm/state/state_test.go index 17343de5eb8f..b0f6afa596f9 100644 --- a/vms/platformvm/state/state_test.go +++ b/vms/platformvm/state/state_test.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" @@ -1293,6 +1294,116 @@ func requireEqualPublicKeysValidatorSet( } } +func TestParsedStateBlock(t *testing.T) { + require := require.New(t) + + var blks []block.Block + + { + blk, err := block.NewApricotAbortBlock(ids.GenerateTestID(), 1000) + require.NoError(err) + blks = append(blks, blk) + } + + { + blk, err := block.NewApricotAtomicBlock(ids.GenerateTestID(), 1000, &txs.Tx{ + Unsigned: &txs.AdvanceTimeTx{ + Time: 1000, + }, + }) + require.NoError(err) + blks = append(blks, blk) + } + + { + blk, err := block.NewApricotCommitBlock(ids.GenerateTestID(), 1000) + require.NoError(err) + blks = append(blks, blk) + } + + { + tx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: ids.GenerateTestID(), + }, + } + require.NoError(tx.Initialize(txs.Codec)) + blk, err := block.NewApricotProposalBlock(ids.GenerateTestID(), 1000, tx) + require.NoError(err) + blks = append(blks, blk) + } + + { + tx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: ids.GenerateTestID(), + }, + } + require.NoError(tx.Initialize(txs.Codec)) + blk, err := block.NewApricotStandardBlock(ids.GenerateTestID(), 1000, []*txs.Tx{tx}) + require.NoError(err) + blks = append(blks, blk) + } + + { + blk, err := block.NewBanffAbortBlock(time.Now(), ids.GenerateTestID(), 1000) + require.NoError(err) + blks = append(blks, blk) + } + + { + blk, err := block.NewBanffCommitBlock(time.Now(), ids.GenerateTestID(), 1000) + require.NoError(err) + blks = append(blks, blk) + } + + { + tx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: ids.GenerateTestID(), + }, + } + require.NoError(tx.Initialize(txs.Codec)) + + blk, err := block.NewBanffProposalBlock(time.Now(), ids.GenerateTestID(), 1000, tx, []*txs.Tx{}) + require.NoError(err) + blks = append(blks, blk) + } + + { + tx := &txs.Tx{ + Unsigned: &txs.RewardValidatorTx{ + TxID: ids.GenerateTestID(), + }, + } + require.NoError(tx.Initialize(txs.Codec)) + + blk, err := block.NewBanffStandardBlock(time.Now(), ids.GenerateTestID(), 1000, []*txs.Tx{tx}) + require.NoError(err) + blks = append(blks, blk) + } + + for _, blk := range blks { + stBlk := stateBlk{ + Bytes: blk.Bytes(), + Status: choices.Accepted, + } + + stBlkBytes, err := block.GenesisCodec.Marshal(block.CodecVersion, &stBlk) + require.NoError(err) + + gotBlk, isStateBlk, err := parseStoredBlock(stBlkBytes) + require.NoError(err) + require.True(isStateBlk) + require.Equal(blk.ID(), gotBlk.ID()) + + gotBlk, isStateBlk, err = parseStoredBlock(blk.Bytes()) + require.NoError(err) + require.False(isStateBlk) + require.Equal(blk.ID(), gotBlk.ID()) + } +} + func TestStateSubnetOwner(t *testing.T) { require := require.New(t)