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

     1  // Copyright 2022 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The AmazeChain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package download
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"github.com/amazechain/amc/utils"
    23  	"github.com/holiman/uint256"
    24  	"google.golang.org/protobuf/proto"
    25  	"hash"
    26  	"sync"
    27  	"sync/atomic"
    28  	"time"
    29  
    30  	"github.com/amazechain/amc/api/protocol/sync_proto"
    31  	"github.com/amazechain/amc/api/protocol/types_pb"
    32  	"github.com/amazechain/amc/common"
    33  	"github.com/amazechain/amc/log"
    34  	event "github.com/amazechain/amc/modules/event/v2"
    35  	"github.com/libp2p/go-libp2p/core/peer"
    36  	"go.uber.org/zap"
    37  )
    38  
    39  var (
    40  	ErrBusy          = fmt.Errorf("busy")
    41  	ErrCanceled      = fmt.Errorf("syncing canceled (requested)")
    42  	ErrSyncBlock     = fmt.Errorf("err sync block")
    43  	ErrTimeout       = fmt.Errorf("timeout")
    44  	ErrBadPeer       = fmt.Errorf("bad peer error")
    45  	ErrNoPeers       = fmt.Errorf("no peers to download")
    46  	ErrInvalidPubSub = fmt.Errorf("PubSub is nil")
    47  )
    48  
    49  const (
    50  	maxHeaderFetch          = 192             //Get the number of headers at a time
    51  	maxBodiesFetch          = 128             // Get the number of bodies at a time
    52  	maxResultsProcess       = 2048            // Number of content download results to import at once into the chain
    53  	headerDownloadInterval  = 3 * time.Second // header download interval
    54  	syncPeerCount           = 6
    55  	syncTimeTick            = time.Duration(10 * time.Second)
    56  	syncCheckTimes          = 1
    57  	syncTimeOutPerRequest   = time.Duration(1 * time.Minute)
    58  	syncPeerIntervalRequest = time.Duration(3 * time.Second)
    59  	syncPeerInfoTimeTick    = time.Duration(10 * time.Second)
    60  	maxDifferenceNumber     = 2
    61  )
    62  
    63  type headerResponse struct {
    64  	taskID  uint64
    65  	ok      bool
    66  	headers []*types_pb.Header
    67  }
    68  
    69  type bodyResponse struct {
    70  	taskID uint64
    71  	ok     bool
    72  	bodies []*types_pb.Block
    73  }
    74  
    75  type blockTask struct {
    76  	taskID uint64
    77  	ok     bool
    78  	number []uint256.Int
    79  }
    80  
    81  type Task struct {
    82  	taskID     uint64
    83  	Id         peer.ID
    84  	H          hash.Hash
    85  	TimeBegin  time.Time
    86  	IsSync     bool
    87  	IndexBegin uint256.Int
    88  	IndexEnd   uint256.Int
    89  }
    90  
    91  type Downloader struct {
    92  	mode uint32 // sync mode , use d.getMode() to get the SyncMode
    93  
    94  	bc            common.IBlockChain
    95  	network       common.INetwork
    96  	isDownloading int32
    97  
    98  	highestNumber uint256.Int
    99  
   100  	ctx        context.Context
   101  	cancel     context.CancelFunc
   102  	cancelLock sync.RWMutex
   103  	cancelWg   sync.WaitGroup //
   104  	once       sync.Once
   105  
   106  	errorCh chan error
   107  
   108  	pubsub    common.IPubSub
   109  	peersInfo *peersInfo
   110  
   111  	headerTasks           []Task
   112  	headerProcessingTasks map[uint64]Task
   113  	headerResultStore     map[uint256.Int]*types_pb.Header
   114  	headerTaskLock        sync.Mutex
   115  	//
   116  	headerProcCh chan *headerResponse
   117  
   118  	//
   119  	//bodyTaskCh  chan *blockTask
   120  	blockProcCh chan *bodyResponse
   121  
   122  	bodyTaskPoolLock    sync.Mutex
   123  	bodyTaskPool        []*blockTask
   124  	bodyProcessingTasks map[uint64]*blockTask
   125  	bodyResultStore     map[uint256.Int]*types_pb.Block
   126  }
   127  
   128  func NewDownloader(ctx context.Context, bc common.IBlockChain, network common.INetwork, pubsub common.IPubSub, peers common.PeerMap) common.IDownloader {
   129  	c, cancel := context.WithCancel(ctx)
   130  
   131  	highestNumber := bc.CurrentBlock().Number64().Clone()
   132  	for _, peer := range peers {
   133  		if highestNumber.Uint64() < peer.CurrentHeight.Uint64() {
   134  			highestNumber = peer.CurrentHeight.Clone()
   135  		}
   136  	}
   137  
   138  	return &Downloader{
   139  		mode:                  uint32(FullSync),
   140  		bc:                    bc,
   141  		network:               network,
   142  		ctx:                   c,
   143  		cancel:                cancel,
   144  		isDownloading:         0,
   145  		pubsub:                pubsub,
   146  		errorCh:               make(chan error, 10),
   147  		headerTasks:           make([]Task, 0),
   148  		headerProcessingTasks: make(map[uint64]Task),
   149  		headerResultStore:     make(map[uint256.Int]*types_pb.Header),
   150  		headerProcCh:          make(chan *headerResponse, 10),
   151  		blockProcCh:           make(chan *bodyResponse, 10),
   152  		bodyTaskPool:          make([]*blockTask, 0),
   153  		bodyProcessingTasks:   make(map[uint64]*blockTask),
   154  		bodyResultStore:       make(map[uint256.Int]*types_pb.Block),
   155  		highestNumber:         *highestNumber,
   156  		peersInfo:             newPeersInfo(c, peers),
   157  	}
   158  }
   159  
   160  func (d *Downloader) getMode() SyncMode {
   161  	return SyncMode(atomic.LoadUint32(&d.mode))
   162  }
   163  
   164  func (d *Downloader) FindBlock(number uint64, peerID peer.ID) (uint64, error) {
   165  	return 0, nil
   166  }
   167  
   168  func (d *Downloader) waitAvailablePeer() {
   169  	timer := time.NewTicker(1 * time.Second)
   170  	defer timer.Stop()
   171  
   172  	timeOutTimer := time.NewTicker(60 * time.Second)
   173  	defer timeOutTimer.Stop()
   174  
   175  	for {
   176  		select {
   177  		case <-d.ctx.Done():
   178  			return
   179  		case <-timer.C:
   180  			peers := d.peersInfo.findPeers(new(uint256.Int).AddUint64(d.bc.CurrentBlock().Number64(), 1), 10)
   181  			if len(peers) > 0 {
   182  				return
   183  			}
   184  		case <-timeOutTimer.C:
   185  			log.Warn("Can not find Peers")
   186  		}
   187  	}
   188  }
   189  
   190  // Start Downloader
   191  func (d *Downloader) Start() error {
   192  	//
   193  	go d.pubSubLoop()
   194  	//
   195  	if d.network.Bootstrapped() {
   196  		//todo
   197  		//log.Debugf("boot node")
   198  		event.GlobalEvent.Send(common.DownloaderFinishEvent{})
   199  		return nil
   200  	}
   201  
   202  	go d.synchronise()
   203  	return nil
   204  }
   205  
   206  // Start
   207  func (d *Downloader) doSync(mode SyncMode) error {
   208  
   209  	log.Info("do sync", zap.Int("SyncMode", int(mode)))
   210  	if !atomic.CompareAndSwapInt32(&d.isDownloading, 0, 1) {
   211  		return ErrBusy
   212  	}
   213  	defer atomic.StoreInt32(&d.isDownloading, 0)
   214  
   215  	// blockChain current block height
   216  	origin, err := d.findAncestor()
   217  	if err != nil {
   218  		return err
   219  	}
   220  	// downloader current height
   221  	latest, err := d.findHead()
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	var fetchers []func() error
   227  
   228  	switch mode {
   229  	case HeaderSync:
   230  	default:
   231  		fetchers = append(fetchers, func() error { return d.fetchHeaders(origin, latest) })
   232  		fetchers = append(fetchers, func() error { return d.fetchBodies(latest) })
   233  		fetchers = append(fetchers, func() error { return d.processHeaders() })
   234  	}
   235  
   236  	// assemble
   237  	fetchers = append(fetchers, func() error { return d.processBodies() })
   238  	fetchers = append(fetchers, func() error { return d.processChain() })
   239  
   240  	return d.spawnSync(fetchers)
   241  }
   242  
   243  // spawnSync
   244  func (d *Downloader) spawnSync(fetchers []func() error) error {
   245  	errc := make(chan error, len(fetchers))
   246  	d.cancelWg.Add(len(fetchers))
   247  	for _, fn := range fetchers {
   248  		fn := fn
   249  		go func() { defer d.cancelWg.Done(); errc <- fn() }()
   250  	}
   251  	var err error
   252  	for i := 0; i < len(fetchers); i++ {
   253  		if i == len(fetchers)-1 {
   254  		}
   255  		if err = <-errc; err != nil {
   256  			break
   257  		}
   258  	}
   259  	d.Close()
   260  	return err
   261  }
   262  
   263  func (d *Downloader) SyncHeader() error {
   264  	return d.doSync(HeaderSync)
   265  }
   266  
   267  func (d *Downloader) SyncBody() error {
   268  	return nil
   269  }
   270  
   271  func (d *Downloader) SyncTx() error {
   272  	return nil
   273  }
   274  
   275  func (d *Downloader) IsDownloading() bool {
   276  	ok := atomic.LoadInt32(&d.isDownloading)
   277  	if ok == 1 {
   278  		return true
   279  	} else if ok == 0 {
   280  		return false
   281  	}
   282  	return true
   283  }
   284  
   285  func (d *Downloader) findAncestor() (uint256.Int, error) {
   286  	return *d.bc.CurrentBlock().Number64(), nil
   287  }
   288  
   289  func (d *Downloader) findHead() (uint256.Int, error) {
   290  	//if d.highestNumber.IsEmpty {
   291  	//	return d.highestNumber, ErrSyncBlock
   292  	//}
   293  	return d.highestNumber, nil
   294  }
   295  
   296  func (d *Downloader) pubSubLoop() {
   297  	defer func() {
   298  		close(d.errorCh)
   299  	}()
   300  	defer d.cancel()
   301  
   302  	highestBlockCh := make(chan common.ChainHighestBlock)
   303  	defer close(highestBlockCh)
   304  	highestSub := event.GlobalEvent.Subscribe(highestBlockCh)
   305  	defer highestSub.Unsubscribe()
   306  
   307  	for {
   308  		select {
   309  		case <-d.ctx.Done():
   310  			return
   311  		case err := <-highestSub.Err():
   312  			log.Debugf("receive a err from highestSub %v", err)
   313  			return
   314  		case highestBlock, ok := <-highestBlockCh:
   315  			if ok && highestBlock.Block.Number64().Uint64() > d.highestNumber.Uint64() {
   316  				log.Debugf("receive a new highestBlock block number: %d", highestBlock.Block.Number64().Uint64())
   317  				d.highestNumber = *highestBlock.Block.Number64()
   318  				if highestBlock.Inserted {
   319  					d.peersInfo.peerInfoBroadcast(highestBlock.Block.Number64())
   320  				}
   321  				//else {
   322  				//	d.bodyResultStore[*highestBlock.Block.Number64()] = highestBlock.Block.ToProtoMessage().(*types_pb.PBlock)
   323  				//}
   324  			}
   325  		}
   326  	}
   327  }
   328  
   329  // runLoop
   330  func (d *Downloader) synchronise() {
   331  	log.Info("start downloader")
   332  	defer log.Info("downloader finished")
   333  	//
   334  	d.waitAvailablePeer()
   335  	//
   336  	event.GlobalEvent.Send(common.DownloaderStartEvent{})
   337  	defer event.GlobalEvent.Send(common.DownloaderFinishEvent{})
   338  
   339  	defer d.cancel()
   340  	tick := time.NewTicker(syncTimeTick)
   341  	defer tick.Stop()
   342  	// checked := 1
   343  
   344  	for {
   345  		select {
   346  		case <-d.ctx.Done():
   347  			return
   348  		case err, ok := <-d.errorCh:
   349  			if ok {
   350  				log.Errorf("failed to running downloader, err:%v", err)
   351  			}
   352  			return
   353  		case <-tick.C:
   354  			difference := new(uint256.Int).Sub(&d.highestNumber, d.bc.CurrentBlock().Number64())
   355  			log.Tracef("highest: %d, current: %d", d.highestNumber.Uint64(), d.bc.CurrentBlock().Number64().Uint64())
   356  			if difference.Uint64() > 1 {
   357  				log.Infof("start downloader Compare Loop remote  highestNumber: %d, current number: %d, difference: %d", d.highestNumber.Uint64(), d.bc.CurrentBlock().Number64().Uint64(), difference.Uint64())
   358  				err := d.doSync(d.getMode())
   359  				if err != nil {
   360  					log.Errorf("failed to running downloader, err:%v", err)
   361  				}
   362  				return
   363  			}
   364  			//if d.highestNumber.Uint64() != 0 && difference.Uint64() ==0 {
   365  			//	return
   366  			//}
   367  			//} else {
   368  			//	if checked >= syncCheckTimes {
   369  			//		return
   370  			//	}
   371  			//	checked++
   372  			//}
   373  			tick.Reset(syncTimeTick)
   374  		}
   375  	}
   376  }
   377  
   378  func (d Downloader) calculateHeight(peer2 common.Peer) error {
   379  	if d.bc.CurrentBlock().Number64().Uint64() == 0 {
   380  
   381  	}
   382  	return nil
   383  }
   384  
   385  func (d *Downloader) ConnHandler(data []byte, ID peer.ID) error {
   386  	p, ok := d.peersInfo.get(ID)
   387  	if !ok {
   388  		return ErrBadPeer
   389  	}
   390  
   391  	syncTask := sync_proto.SyncTask{}
   392  	if err := proto.Unmarshal(data, &syncTask); err != nil {
   393  		log.Errorf("receive sync task(headersResponse) msg err: %v", err)
   394  		return err
   395  	}
   396  
   397  	taskID := syncTask.Id
   398  	params := make([]interface{}, 0)
   399  	params = append(params, "peerID", ID, "taskType", syncTask.SyncType, "taskID", taskID, "isOK", syncTask.Ok)
   400  
   401  	switch syncTask.SyncType {
   402  
   403  	case sync_proto.SyncType_HeaderRes:
   404  		headersResponse := syncTask.Payload.(*sync_proto.SyncTask_SyncHeaderResponse).SyncHeaderResponse
   405  		params = append(params, "headerCount", len(headersResponse.Headers), "headerNumberFrom", utils.ConvertH256ToUint256Int(headersResponse.Headers[0].Number).Uint64(), "headerNumberTo", utils.ConvertH256ToUint256Int(headersResponse.Headers[len(headersResponse.Headers)-1].Number).Uint64())
   406  		d.headerProcCh <- &headerResponse{taskID: taskID, ok: syncTask.Ok, headers: headersResponse.Headers}
   407  
   408  	case sync_proto.SyncType_HeaderReq:
   409  		headerRequest := syncTask.Payload.(*sync_proto.SyncTask_SyncHeaderRequest).SyncHeaderRequest
   410  		params = append(params, "Amount", utils.ConvertH256ToUint256Int(headerRequest.Amount).Uint64(), "headerNumberFrom", utils.ConvertH256ToUint256Int(headerRequest.Number).Uint64())
   411  		go d.responseHeaders(taskID, p, headerRequest)
   412  
   413  	case sync_proto.SyncType_BodyRes:
   414  		bodiesResponse := syncTask.Payload.(*sync_proto.SyncTask_SyncBlockResponse).SyncBlockResponse
   415  		params = append(params, "blocksCount", len(bodiesResponse.Blocks), "bodyNumberFrom", utils.ConvertH256ToUint256Int(bodiesResponse.Blocks[0].Header.Number).Uint64(), "bodyNumberTo", utils.ConvertH256ToUint256Int(bodiesResponse.Blocks[len(bodiesResponse.Blocks)-1].Header.Number).Uint64())
   416  		d.blockProcCh <- &bodyResponse{taskID: taskID, ok: syncTask.Ok, bodies: bodiesResponse.Blocks}
   417  
   418  	case sync_proto.SyncType_BodyReq:
   419  		blockRequest := syncTask.Payload.(*sync_proto.SyncTask_SyncBlockRequest).SyncBlockRequest
   420  		params = append(params, "bodyNumberFrom", utils.ConvertH256ToUint256Int(blockRequest.Number[0]).Uint64(), "bodyNumberTo", utils.ConvertH256ToUint256Int(blockRequest.Number[len(blockRequest.Number)-1]).Uint64())
   421  		go d.responseBlocks(taskID, p, blockRequest)
   422  
   423  	case sync_proto.SyncType_PeerInfoBroadcast:
   424  		peerInfoBroadcast := syncTask.Payload.(*sync_proto.SyncTask_SyncPeerInfoBroadcast).SyncPeerInfoBroadcast
   425  		//
   426  		currentNumber := utils.ConvertH256ToUint256Int(peerInfoBroadcast.Number)
   427  		currentDifficulty := utils.ConvertH256ToUint256Int(peerInfoBroadcast.Difficulty)
   428  		params = append(params, "Number", currentNumber, "Difficulty", currentDifficulty)
   429  		//
   430  		if currentNumber.Uint64() > d.highestNumber.Uint64() {
   431  			d.highestNumber.Set(currentNumber.Clone())
   432  		}
   433  		d.peersInfo.update(p.ID(), currentNumber, currentDifficulty)
   434  	}
   435  
   436  	log.Info("receive sync task msg", params...)
   437  
   438  	return nil
   439  }
   440  
   441  func (d *Downloader) Close() error {
   442  	d.cancelLock.Lock()
   443  	defer d.cancelLock.Unlock()
   444  	d.cancel()
   445  	d.cancelWg.Wait()
   446  	return nil
   447  }