Skip to content
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

Minimize signature verification when bootstrapping #3255

Merged
merged 6 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,9 +787,7 @@ func (m *manager) createAvalancheChain(
return nil, err
}

// Note: vmWrappingProposerVM is the VM that the Snowman engines should be
// using.
var vmWrappingProposerVM block.ChainVM = proposervm.New(
proposerVM := proposervm.New(
vmWrappedInsideProposerVM,
proposervm.Config{
Upgrades: m.Upgrades,
Expand All @@ -801,6 +799,10 @@ func (m *manager) createAvalancheChain(
},
)

// Note: vmWrappingProposerVM is the VM that the Snowman engines should be
// using.
var vmWrappingProposerVM block.ChainVM = proposerVM

if m.MeterVMEnabled {
meterchainvmReg, err := metrics.MakeAndRegister(
m.meterChainVMGatherer,
Expand Down Expand Up @@ -948,6 +950,7 @@ func (m *manager) createAvalancheChain(

// create bootstrap gear
bootstrapCfg := smbootstrap.Config{
NonVerifyingParse: block.ParseFunc(proposerVM.ParseLocalBlock),
AllGetsServer: snowGetHandler,
Ctx: ctx,
Beacons: vdrs,
Expand Down Expand Up @@ -1184,7 +1187,7 @@ func (m *manager) createSnowmanChain(
return nil, err
}

vm = proposervm.New(
proposerVM := proposervm.New(
marun marked this conversation as resolved.
Show resolved Hide resolved
vm,
proposervm.Config{
Upgrades: m.Upgrades,
Expand All @@ -1196,6 +1199,8 @@ func (m *manager) createSnowmanChain(
},
)

vm = proposerVM

if m.MeterVMEnabled {
meterchainvmReg, err := metrics.MakeAndRegister(
m.meterChainVMGatherer,
Expand Down Expand Up @@ -1345,6 +1350,7 @@ func (m *manager) createSnowmanChain(

// create bootstrap gear
bootstrapCfg := smbootstrap.Config{
NonVerifyingParse: block.ParseFunc(proposerVM.ParseLocalBlock),
AllGetsServer: snowGetHandler,
Ctx: ctx,
Beacons: beacons,
Expand Down
8 changes: 8 additions & 0 deletions snow/engine/snowman/block/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,11 @@ type Parser interface {
// It is expected for all historical blocks to be parseable.
ParseBlock(ctx context.Context, blockBytes []byte) (snowman.Block, error)
}

// ParseFunc defines a function that parses raw bytes into a block.
type ParseFunc func(context.Context, []byte) (snowman.Block, error)

// ParseBlock wraps a ParseFunc into a ParseBlock function, to be used by a Parser interface
func (f ParseFunc) ParseBlock(ctx context.Context, blockBytes []byte) (snowman.Block, error) {
return f(ctx, blockBytes)
}
7 changes: 5 additions & 2 deletions snow/engine/snowman/bootstrap/bootstrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,14 @@ type Bootstrapper struct {

// Called when bootstrapping is done on a specific chain
onFinished func(ctx context.Context, lastReqID uint32) error

nonVerifyingParser block.Parser
}

func New(config Config, onFinished func(ctx context.Context, lastReqID uint32) error) (*Bootstrapper, error) {
metrics, err := newMetrics(config.Ctx.Registerer)
return &Bootstrapper{
nonVerifyingParser: config.NonVerifyingParse,
StephenButtolph marked this conversation as resolved.
Show resolved Hide resolved
Config: config,
metrics: metrics,
StateSummaryFrontierHandler: common.NewNoOpStateSummaryFrontierHandler(config.Ctx.Log),
Expand Down Expand Up @@ -178,7 +181,7 @@ func (b *Bootstrapper) Start(ctx context.Context, startReqID uint32) error {
return fmt.Errorf("failed to initialize interval tree: %w", err)
}

b.missingBlockIDs, err = getMissingBlockIDs(ctx, b.DB, b.VM, b.tree, b.startingHeight)
b.missingBlockIDs, err = getMissingBlockIDs(ctx, b.DB, b.nonVerifyingParser, b.tree, b.startingHeight)
if err != nil {
return fmt.Errorf("failed to initialize missing block IDs: %w", err)
}
Expand Down Expand Up @@ -650,7 +653,7 @@ func (b *Bootstrapper) tryStartExecuting(ctx context.Context) error {
log,
b.DB,
&parseAcceptor{
parser: b.VM,
parser: b.nonVerifyingParser,
ctx: b.Ctx,
numAccepted: b.numAccepted,
},
Expand Down
1 change: 1 addition & 0 deletions snow/engine/snowman/bootstrap/bootstrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func newConfig(t *testing.T) (Config, ids.NodeID, *common.SenderTest, *block.Tes
peerTracker.Connected(peer, version.CurrentApp)

return Config{
NonVerifyingParse: vm.ParseBlock,
AllGetsServer: snowGetHandler,
Ctx: ctx,
Beacons: vdrs,
Expand Down
3 changes: 3 additions & 0 deletions snow/engine/snowman/bootstrap/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ type Config struct {

VM block.ChainVM

// NonVerifyingParse parses blocks without verifying them.
NonVerifyingParse block.ParseFunc

Bootstrapped func()
}
8 changes: 4 additions & 4 deletions snow/engine/snowman/bootstrap/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const (
func getMissingBlockIDs(
ctx context.Context,
db database.KeyValueReader,
parser block.Parser,
nonVerifyingParser block.Parser,
tree *interval.Tree,
lastAcceptedHeight uint64,
) (set.Set[ids.ID], error) {
Expand All @@ -57,7 +57,7 @@ func getMissingBlockIDs(
return nil, err
}

blk, err := parser.ParseBlock(ctx, blkBytes)
blk, err := nonVerifyingParser.ParseBlock(ctx, blkBytes)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -130,7 +130,7 @@ func execute(
haltable common.Haltable,
log logging.Func,
db database.Database,
parser block.Parser,
nonVerifyingParser block.Parser,
tree *interval.Tree,
lastAcceptedHeight uint64,
) error {
Expand Down Expand Up @@ -198,7 +198,7 @@ func execute(

for !haltable.Halted() && iterator.Next() {
blkBytes := iterator.Value()
blk, err := parser.ParseBlock(ctx, blkBytes)
blk, err := nonVerifyingParser.ParseBlock(ctx, blkBytes)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions vms/platformvm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,7 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
require.NoError(err)

bootstrapConfig := bootstrap.Config{
NonVerifyingParse: vm.ParseBlock,
AllGetsServer: snowGetHandler,
Ctx: consensusCtx,
Beacons: beacons,
Expand Down
2 changes: 1 addition & 1 deletion vms/proposervm/state_syncable_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (vm *VM) ParseStateSummary(ctx context.Context, summaryBytes []byte) (block
if err != nil {
return nil, fmt.Errorf("could not parse inner summary due to: %w", err)
}
block, err := vm.parsePostForkBlock(ctx, statelessSummary.BlockBytes())
block, err := vm.parsePostForkBlock(ctx, statelessSummary.BlockBytes(), true)
if err != nil {
return nil, fmt.Errorf("could not parse proposervm block bytes from summary due to: %w", err)
}
Expand Down
22 changes: 19 additions & 3 deletions vms/proposervm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,14 @@ func (vm *VM) BuildBlock(ctx context.Context) (snowman.Block, error) {
}

func (vm *VM) ParseBlock(ctx context.Context, b []byte) (snowman.Block, error) {
marun marked this conversation as resolved.
Show resolved Hide resolved
if blk, err := vm.parsePostForkBlock(ctx, b); err == nil {
if blk, err := vm.parsePostForkBlock(ctx, b, true); err == nil {
return blk, nil
}
return vm.parsePreForkBlock(ctx, b)
}

func (vm *VM) ParseLocalBlock(ctx context.Context, b []byte) (snowman.Block, error) {
if blk, err := vm.parsePostForkBlock(ctx, b, false); err == nil {
return blk, nil
}
return vm.parsePreForkBlock(ctx, b)
Expand Down Expand Up @@ -524,8 +531,17 @@ func (vm *VM) setLastAcceptedMetadata(ctx context.Context) error {
return nil
}

func (vm *VM) parsePostForkBlock(ctx context.Context, b []byte) (PostForkBlock, error) {
statelessBlock, err := statelessblock.Parse(b, vm.ctx.ChainID)
func (vm *VM) parsePostForkBlock(ctx context.Context, b []byte, verifySignature bool) (PostForkBlock, error) {
var (
statelessBlock statelessblock.Block
err error
)

if verifySignature {
statelessBlock, err = statelessblock.Parse(b, vm.ctx.ChainID)
} else {
statelessBlock, err = statelessblock.ParseWithoutVerification(b)
}
if err != nil {
return nil, err
}
Expand Down
88 changes: 88 additions & 0 deletions vms/proposervm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/ava-labs/avalanchego/staking"
"github.com/ava-labs/avalanchego/upgrade"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/timer/mockable"
"github.com/ava-labs/avalanchego/vms/proposervm/proposer"

Expand Down Expand Up @@ -2476,3 +2477,90 @@ func TestGetPostDurangoSlotTimeWithNoValidators(t *testing.T) {
require.NoError(err)
require.Equal(parentTimestamp.Add(proVM.MinBlkDelay), slotTime)
}

func TestLocalParse(t *testing.T) {
innerVM := &block.TestVM{
ParseBlockF: func(_ context.Context, rawBlock []byte) (snowman.Block, error) {
return &snowmantest.Block{BytesV: rawBlock}, nil
},
}

chainID := ids.GenerateTestID()

tlsCert, err := staking.NewTLSCert()
require.NoError(t, err)

cert, err := staking.ParseCertificate(tlsCert.Leaf.Raw)
require.NoError(t, err)
key := tlsCert.PrivateKey.(crypto.Signer)

signedBlock, err := statelessblock.Build(
ids.ID{1},
time.Unix(123, 0),
uint64(42),
cert,
[]byte{1, 2, 3},
chainID,
key,
)
require.NoError(t, err)

properlySignedBlock := signedBlock.Bytes()

improperlySignedBlock := make([]byte, len(properlySignedBlock))
copy(improperlySignedBlock, properlySignedBlock)
improperlySignedBlock[len(improperlySignedBlock)-1] = ^improperlySignedBlock[len(improperlySignedBlock)-1]

conf := Config{
MinBlkDelay: DefaultMinBlockDelay,
NumHistoricalBlocks: DefaultNumHistoricalBlocks,
StakingLeafSigner: pTestSigner,
StakingCertLeaf: pTestCert,
Registerer: prometheus.NewRegistry(),
}

vm := New(innerVM, conf)
defer func() {
require.NoError(t, vm.Shutdown(context.Background()))
}()

db := prefixdb.New([]byte{}, memdb.New())

_ = vm.Initialize(context.Background(), &snow.Context{
Log: logging.NoLog{},
ChainID: chainID,
}, db, nil, nil, nil, nil, nil, nil)

tests := []struct {
name string
f block.ParseFunc
block []byte
resultingBlock interface{}
}{
{
name: "local parse as post-fork",
f: vm.ParseLocalBlock,
block: improperlySignedBlock,
resultingBlock: &postForkBlock{},
},
{
name: "parse as pre-fork",
f: vm.ParseBlock,
block: improperlySignedBlock,
resultingBlock: &preForkBlock{},
},
{
name: "parse as post-fork",
f: vm.ParseBlock,
block: properlySignedBlock,
resultingBlock: &postForkBlock{},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
block, err := test.f(context.Background(), test.block)
require.NoError(t, err)
require.IsType(t, test.resultingBlock, block)
})
}
}
Loading