github.com/amazechain/amc@v0.1.3/internal/sync/validate_blocks.go (about)

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/amazechain/amc/api/protocol/types_pb"
     7  	"github.com/amazechain/amc/common/block"
     8  	"github.com/amazechain/amc/common/types"
     9  	"github.com/amazechain/amc/log"
    10  	"go.opencensus.io/trace"
    11  	"time"
    12  
    13  	pubsub "github.com/libp2p/go-libp2p-pubsub"
    14  	"github.com/libp2p/go-libp2p/core/peer"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  var (
    19  	ErrOptimisticParent = errors.New("parent of the block is optimistic")
    20  )
    21  
    22  // validateBlockPubSub checks that the incoming block has a valid BLS signature.
    23  // Blocks that have already been seen are ignored. If the BLS signature is any valid signature,
    24  // this method rebroadcasts the message.
    25  func (s *Service) validateBlockPubSub(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
    26  	receivedTime := time.Now()
    27  	// Validation runs on publish (not just subscriptions), so we should approve any message from
    28  	// ourselves.
    29  	if pid == s.cfg.p2p.PeerID() {
    30  		return pubsub.ValidationAccept, nil
    31  	}
    32  
    33  	// We should not attempt to process blocks until fully synced, but propagation is OK.
    34  	if s.cfg.initialSync.Syncing() {
    35  		return pubsub.ValidationIgnore, nil
    36  	}
    37  
    38  	ctx, span := trace.StartSpan(ctx, "sync.validateBlockPubSub")
    39  	defer span.End()
    40  
    41  	m, err := s.decodePubsubMessage(msg)
    42  	if err != nil {
    43  		//tracing.AnnotateError(span, err)
    44  		return pubsub.ValidationReject, errors.Wrap(err, "Could not decode message")
    45  	}
    46  
    47  	s.validateBlockLock.Lock()
    48  	defer s.validateBlockLock.Unlock()
    49  
    50  	blk, ok := m.(*types_pb.Block)
    51  	if !ok {
    52  		return pubsub.ValidationReject, errors.New("msg is not types_pb.Block")
    53  	}
    54  
    55  	iBlock := new(block.Block)
    56  	if err = iBlock.FromProtoMessage(blk); err != nil {
    57  		return pubsub.ValidationReject, errors.New("block.Block is nil")
    58  	}
    59  
    60  	iHeader, iBody := iBlock.Header(), iBlock.Body()
    61  	header, ok := iHeader.(*block.Header)
    62  	if !ok {
    63  		return pubsub.ValidationReject, errors.New("msg.header is not block.Header")
    64  	}
    65  	_, ok = iBody.(*block.Body)
    66  	if !ok {
    67  		return pubsub.ValidationReject, errors.New("msg.body is not types_pb.Block")
    68  	}
    69  
    70  	//todo
    71  	// Broadcast the block on a feed to notify other services in the beacon node
    72  	// of a received block (even if it does not process correctly through a state transition).
    73  
    74  	if s.cfg.chain.HasBlock(header.Root, header.Number.Uint64()) {
    75  		return pubsub.ValidationIgnore, nil
    76  	}
    77  
    78  	// Check if parent is a bad block and then reject the block.
    79  	if s.hasBadBlock(header.ParentHash) {
    80  		s.setBadBlock(ctx, header.Root)
    81  		err := fmt.Errorf("received block with root %#x that has an invalid parent %#x", header.Root, header.ParentHash)
    82  		log.Debug("Received block with an invalid parent", "err", err)
    83  		return pubsub.ValidationReject, err
    84  	}
    85  
    86  	// Be lenient in handling early blocks. Instead of discarding blocks arriving later than
    87  	// MAXIMUM_GOSSIP_CLOCK_DISPARITY in future, we tolerate blocks arriving at max two slots
    88  	// earlier (SECONDS_PER_SLOT * 2 seconds). Queue such blocks and process them at the right slot.
    89  	//genesisTime := uint64(s.cfg.chain.GenesisTime().Unix())
    90  	//if header.Time > time.Now() {
    91  	//	log.Error("Ignored block: could not verify header time")
    92  	//	return pubsub.ValidationIgnore, nil
    93  	//}
    94  
    95  	// Add metrics for block arrival time subtracts slot start time.
    96  	if err := captureArrivalTimeMetric(header.Time); err != nil {
    97  		log.Debug("Ignored block: could not capture arrival time metric", "err", err)
    98  		return pubsub.ValidationIgnore, nil
    99  	}
   100  
   101  	// Handle block when the parent is unknown.
   102  	if !s.cfg.chain.HasBlock(header.ParentHash, header.Number.Uint64()-1) {
   103  		// todo feature?
   104  	}
   105  
   106  	msg.ValidatorData = iBlock.ToProtoMessage() // Used in downstream subscriber
   107  
   108  	log.Debug("Received block")
   109  
   110  	blockVerificationGossipSummary.Observe(float64(time.Since(receivedTime).Milliseconds()))
   111  	return pubsub.ValidationAccept, nil
   112  }
   113  
   114  // Returns true if the block is marked as a bad block.
   115  func (s *Service) hasBadBlock(root types.Hash) bool {
   116  	s.badBlockLock.RLock()
   117  	defer s.badBlockLock.RUnlock()
   118  	_, seen := s.badBlockCache.Get(root)
   119  	return seen
   120  }
   121  
   122  // Set bad block in the cache.
   123  func (s *Service) setBadBlock(ctx context.Context, root types.Hash) {
   124  	s.badBlockLock.Lock()
   125  	defer s.badBlockLock.Unlock()
   126  	if ctx.Err() != nil { // Do not mark block as bad if it was due to context error.
   127  		return
   128  	}
   129  	log.Debug("Inserting in invalid block cache", "root", root)
   130  	s.badBlockCache.Add(root, true)
   131  }
   132  
   133  // This captures metrics for block arrival time.
   134  func captureArrivalTimeMetric(headerTime uint64) error {
   135  	startTime := time.Unix(int64(headerTime), 0)
   136  	//todo future block
   137  	if time.Now().Sub(startTime) < 0 {
   138  		return fmt.Errorf("the block is future block time is %s", startTime.Format(time.RFC3339))
   139  	}
   140  	ms := time.Now().Sub(startTime) / time.Millisecond
   141  	arrivalBlockPropagationHistogram.Observe(float64(ms))
   142  	arrivalBlockPropagationGauge.Set(float64(ms))
   143  
   144  	return nil
   145  }
   146  
   147  // isBlockQueueable checks if the slot_time in the block is greater than
   148  // current_time +  MAXIMUM_GOSSIP_CLOCK_DISPARITY. in short, this function
   149  // returns true if the corresponding block should be queued and false if
   150  // the block should be processed immediately.
   151  //func isBlockQueueable(genesisTime uint64, slot primitives.Slot, receivedTime time.Time) bool {
   152  //	slotTime, err := slots.ToTime(genesisTime, slot)
   153  //	if err != nil {
   154  //		return false
   155  //	}
   156  //
   157  //	currentTimeWithDisparity := receivedTime.Add(params.BeaconNetworkConfig().MaximumGossipClockDisparity)
   158  //	return currentTimeWithDisparity.Unix() < slotTime.Unix()
   159  //}
   160  
   161  //func getBlockFields(b interfaces.ReadOnlySignedBeaconBlock) logrus.Fields {
   162  //	if consensusblocks.BeaconBlockIsNil(b) != nil {
   163  //		return logrus.Fields{}
   164  //	}
   165  //	graffiti := b.Block().Body().Graffiti()
   166  //	return logrus.Fields{
   167  //		"slot":          b.Block().Slot(),
   168  //		"proposerIndex": b.Block().ProposerIndex(),
   169  //		"graffiti":      string(graffiti[:]),
   170  //		"version":       b.Block().Version(),
   171  //	}
   172  //}