github.com/ava-labs/avalanchego@v1.11.11/vms/example/xsvm/chain/block.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package chain
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"time"
    10  
    11  	"github.com/ava-labs/avalanchego/database"
    12  	"github.com/ava-labs/avalanchego/database/versiondb"
    13  	"github.com/ava-labs/avalanchego/ids"
    14  	"github.com/ava-labs/avalanchego/snow"
    15  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    16  	"github.com/ava-labs/avalanchego/utils/set"
    17  	"github.com/ava-labs/avalanchego/vms/example/xsvm/execute"
    18  
    19  	smblock "github.com/ava-labs/avalanchego/snow/engine/snowman/block"
    20  	xsblock "github.com/ava-labs/avalanchego/vms/example/xsvm/block"
    21  )
    22  
    23  const maxClockSkew = 10 * time.Second
    24  
    25  var (
    26  	_ Block = (*block)(nil)
    27  
    28  	errMissingParent         = errors.New("missing parent block")
    29  	errMissingChild          = errors.New("missing child block")
    30  	errParentNotVerified     = errors.New("parent block has not been verified")
    31  	errFutureTimestamp       = errors.New("future timestamp")
    32  	errTimestampBeforeParent = errors.New("timestamp before parent")
    33  	errWrongHeight           = errors.New("wrong height")
    34  )
    35  
    36  type Block interface {
    37  	snowman.Block
    38  	smblock.WithVerifyContext
    39  
    40  	// State intends to return the new chain state following this block's
    41  	// acceptance. The new chain state is built (but not persisted) following a
    42  	// block's verification to allow block's descendants verification before
    43  	// being accepted.
    44  	State() (database.Database, error)
    45  }
    46  
    47  type block struct {
    48  	*xsblock.Stateless
    49  
    50  	chain *chain
    51  
    52  	id    ids.ID
    53  	bytes []byte
    54  
    55  	state               *versiondb.Database
    56  	verifiedChildrenIDs set.Set[ids.ID]
    57  }
    58  
    59  func (b *block) ID() ids.ID {
    60  	return b.id
    61  }
    62  
    63  func (b *block) Parent() ids.ID {
    64  	return b.ParentID
    65  }
    66  
    67  func (b *block) Bytes() []byte {
    68  	return b.bytes
    69  }
    70  
    71  func (b *block) Height() uint64 {
    72  	return b.Stateless.Height
    73  }
    74  
    75  func (b *block) Timestamp() time.Time {
    76  	return b.Time()
    77  }
    78  
    79  func (b *block) Verify(ctx context.Context) error {
    80  	return b.VerifyWithContext(ctx, nil)
    81  }
    82  
    83  func (b *block) Accept(context.Context) error {
    84  	if err := b.state.Commit(); err != nil {
    85  		return err
    86  	}
    87  
    88  	// Following this block's acceptance, make sure that it's direct children
    89  	// point to the base state, which now also contains this block's changes.
    90  	for childID := range b.verifiedChildrenIDs {
    91  		child, exists := b.chain.verifiedBlocks[childID]
    92  		if !exists {
    93  			return errMissingChild
    94  		}
    95  		if err := child.state.SetDatabase(b.chain.acceptedState); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	b.chain.lastAcceptedID = b.id
   101  	b.chain.lastAcceptedHeight = b.Height()
   102  	delete(b.chain.verifiedBlocks, b.ParentID)
   103  	b.state = nil
   104  	return nil
   105  }
   106  
   107  func (b *block) Reject(context.Context) error {
   108  	delete(b.chain.verifiedBlocks, b.id)
   109  	b.state = nil
   110  
   111  	// TODO: push transactions back into the mempool
   112  	return nil
   113  }
   114  
   115  func (b *block) ShouldVerifyWithContext(context.Context) (bool, error) {
   116  	return execute.ExpectsContext(b.Stateless)
   117  }
   118  
   119  func (b *block) VerifyWithContext(ctx context.Context, blockContext *smblock.Context) error {
   120  	timestamp := b.Time()
   121  	if time.Until(timestamp) > maxClockSkew {
   122  		return errFutureTimestamp
   123  	}
   124  
   125  	// parent block must be verified or accepted
   126  	parent, exists := b.chain.verifiedBlocks[b.ParentID]
   127  	if !exists {
   128  		return errMissingParent
   129  	}
   130  
   131  	if b.Stateless.Height != parent.Stateless.Height+1 {
   132  		return errWrongHeight
   133  	}
   134  
   135  	parentTimestamp := parent.Time()
   136  	if timestamp.Before(parentTimestamp) {
   137  		return errTimestampBeforeParent
   138  	}
   139  
   140  	parentState, err := parent.State()
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	// This block's state is a versionDB built on top of it's parent state. This
   146  	// block's changes are pushed atomically to the parent state when accepted.
   147  	blkState := versiondb.New(parentState)
   148  	err = execute.Block(
   149  		ctx,
   150  		b.chain.chainContext,
   151  		blkState,
   152  		b.chain.chainState == snow.Bootstrapping,
   153  		blockContext,
   154  		b.Stateless,
   155  	)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	// Make sure to only state the state the first time we verify this block.
   161  	if b.state == nil {
   162  		b.state = blkState
   163  		parent.verifiedChildrenIDs.Add(b.id)
   164  		b.chain.verifiedBlocks[b.id] = b
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  func (b *block) State() (database.Database, error) {
   171  	if b.id == b.chain.lastAcceptedID {
   172  		return b.chain.acceptedState, nil
   173  	}
   174  
   175  	// If this block isn't processing, then the child should never have had
   176  	// verify called on it.
   177  	if b.state == nil {
   178  		return nil, errParentNotVerified
   179  	}
   180  
   181  	return b.state, nil
   182  }