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 //}