github.com/amazechain/amc@v0.1.3/internal/txspool/txs_fetcher.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 txspool
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"google.golang.org/protobuf/proto"
    23  	"math/rand"
    24  	"time"
    25  
    26  	"github.com/amazechain/amc/api/protocol/sync_proto"
    27  	"github.com/amazechain/amc/api/protocol/types_pb"
    28  	"github.com/amazechain/amc/common"
    29  	"github.com/amazechain/amc/common/message"
    30  	"github.com/amazechain/amc/common/transaction"
    31  	"github.com/amazechain/amc/common/types"
    32  	"github.com/amazechain/amc/log"
    33  	"github.com/libp2p/go-libp2p/core/peer"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  const (
    38  	BloomFetcherMaxTime      = 10 * time.Minute
    39  	BloomSendTransactionTime = 10 * time.Second
    40  	BloomSendMaxTransactions = 100
    41  )
    42  
    43  var (
    44  	ErrBadPeer = fmt.Errorf("bad peer error")
    45  )
    46  
    47  // txsRequest
    48  type txsRequest struct {
    49  	hashes hashes       //
    50  	bloom  *types.Bloom //
    51  	peer   peer.ID      //
    52  }
    53  
    54  type hashes []types.Hash
    55  
    56  func (h hashes) pop() types.Hash {
    57  	old := h
    58  	n := len(old)
    59  	x := old[n-1]
    60  	h = old[0 : n-1]
    61  	return x
    62  }
    63  
    64  type TxsFetcher struct {
    65  	quit chan struct{}
    66  
    67  	finished  bool
    68  	peers     common.PeerMap
    69  	p2pServer common.INetwork
    70  
    71  	peerRequests map[peer.ID]*txsRequest // other peer requests
    72  
    73  	bloom   *types.Bloom
    74  	fetched map[types.Hash]bool //
    75  
    76  	getTx      func(hash types.Hash) *transaction.Transaction
    77  	addTxs     func([]*transaction.Transaction) []error
    78  	pendingTxs func(enforceTips bool) map[types.Address][]*transaction.Transaction
    79  
    80  	// fetchTxs
    81  	//fetchTxs func(string, []types.Hash)
    82  	// ch
    83  	peerJoinCh chan *common.PeerJoinEvent
    84  	peerDropCh chan *common.PeerDropEvent
    85  	//
    86  
    87  	ctx    context.Context
    88  	cancel context.CancelFunc
    89  }
    90  
    91  func NewTxsFetcher(ctx context.Context, getTx func(hash types.Hash) *transaction.Transaction, addTxs func([]*transaction.Transaction) []error, pendingTxs func(enforceTips bool) map[types.Address][]*transaction.Transaction, p2pServer common.INetwork, peers common.PeerMap, bloom *types.Bloom) *TxsFetcher {
    92  
    93  	c, cancel := context.WithCancel(ctx)
    94  	f := &TxsFetcher{
    95  		quit:         make(chan struct{}),
    96  		peers:        peers,
    97  		peerRequests: make(map[peer.ID]*txsRequest),
    98  		fetched:      make(map[types.Hash]bool),
    99  		p2pServer:    p2pServer,
   100  		addTxs:       addTxs,
   101  		getTx:        getTx,
   102  		pendingTxs:   pendingTxs,
   103  		bloom:        bloom,
   104  		peerJoinCh:   make(chan *common.PeerJoinEvent, 10),
   105  		peerDropCh:   make(chan *common.PeerDropEvent, 10),
   106  		finished:     false,
   107  
   108  		ctx:    c,
   109  		cancel: cancel,
   110  	}
   111  
   112  	return f
   113  }
   114  
   115  func (f TxsFetcher) Start() error {
   116  	go f.sendBloomTransactionLoop()
   117  	go f.bloomBroadcastLoop()
   118  	return nil
   119  }
   120  
   121  // sendBloomTransactionLoop
   122  func (f TxsFetcher) sendBloomTransactionLoop() {
   123  	tick := time.NewTicker(BloomSendTransactionTime)
   124  
   125  	select {
   126  	case <-tick.C:
   127  		for ID, req := range f.peerRequests {
   128  			if p, ok := f.peers[ID]; ok == true {
   129  
   130  				var txs []*types_pb.Transaction
   131  
   132  				for i := 0; i < BloomSendMaxTransactions; i++ {
   133  					hash := req.hashes.pop()
   134  					tx := f.getTx(hash)
   135  					txs = append(txs, tx.ToProtoMessage().(*types_pb.Transaction))
   136  				}
   137  				msg := &sync_proto.SyncTask{
   138  					Id:       rand.Uint64(),
   139  					Ok:       true,
   140  					SyncType: sync_proto.SyncType_TransactionRes,
   141  					Payload: &sync_proto.SyncTask_SyncTransactionResponse{
   142  						SyncTransactionResponse: &sync_proto.SyncTransactionResponse{
   143  							Transactions: txs,
   144  						},
   145  					},
   146  				}
   147  				data, _ := proto.Marshal(msg)
   148  				p.WriteMsg(message.MsgTransaction, data)
   149  			}
   150  		}
   151  	case <-f.ctx.Done():
   152  		return
   153  	}
   154  }
   155  
   156  func (f TxsFetcher) bloomBroadcastLoop() {
   157  	if f.bloom == nil {
   158  		return
   159  	}
   160  	tick := time.NewTicker(BloomFetcherMaxTime)
   161  	bloom, err := f.bloom.Marshal()
   162  	if err != nil {
   163  		log.Warn("txs fetcher bloom Marshal err", zap.Error(err))
   164  		return
   165  	}
   166  
   167  	msg := &sync_proto.SyncTask{
   168  		Id:       rand.Uint64(),
   169  		Ok:       true,
   170  		SyncType: sync_proto.SyncType_TransactionReq,
   171  		Payload: &sync_proto.SyncTask_SyncTransactionRequest{
   172  			SyncTransactionRequest: &sync_proto.SyncTransactionRequest{
   173  				Bloom: bloom,
   174  			},
   175  		},
   176  	}
   177  	request, _ := proto.Marshal(msg)
   178  	select {
   179  	case peerId := <-f.peerJoinCh:
   180  		f.peers[peerId.Peer].WriteMsg(message.MsgTransaction, request)
   181  	case <-tick.C:
   182  	case <-f.ctx.Done():
   183  		return
   184  	}
   185  }
   186  
   187  // ConnHandler handler peer message
   188  func (f TxsFetcher) ConnHandler(data []byte, ID peer.ID) error {
   189  	_, ok := f.peers[ID]
   190  	if !ok {
   191  		return ErrBadPeer
   192  	}
   193  
   194  	syncTask := sync_proto.SyncTask{}
   195  	if err := proto.Unmarshal(data, &syncTask); err != nil {
   196  		log.Errorf("receive sync task(headersResponse) msg err: %v", err)
   197  		return err
   198  	}
   199  
   200  	//taskID := syncTask.Id
   201  	log.Debugf("receive synctask msg from :%v, task type: %v, ok:%v", ID, syncTask.SyncType, syncTask.Ok)
   202  
   203  	switch syncTask.SyncType {
   204  
   205  	case sync_proto.SyncType_TransactionReq:
   206  		request := syncTask.Payload.(*sync_proto.SyncTask_SyncTransactionRequest).SyncTransactionRequest
   207  		if _, ok := f.peerRequests[ID]; ok == false {
   208  
   209  			bloom := new(types.Bloom)
   210  			err := bloom.UnMarshalBloom(request.Bloom)
   211  			if err == nil {
   212  				var txs []*transaction.Transaction
   213  				var hashes []types.Hash
   214  				pending := f.pendingTxs(false)
   215  				for _, batch := range pending {
   216  					txs = append(txs, batch...)
   217  				}
   218  				for _, tx := range txs {
   219  					hash := tx.Hash()
   220  					if !bloom.Contain(hash.Bytes()) {
   221  						hashes = append(hashes, hash)
   222  					}
   223  				}
   224  				f.peerRequests[ID] = &txsRequest{
   225  					bloom:  bloom,
   226  					hashes: hashes,
   227  					peer:   ID,
   228  				}
   229  			}
   230  		}
   231  
   232  	case sync_proto.SyncType_TransactionRes:
   233  		//response := syncTask.Payload.(*sync_proto.SyncTask_SyncTransactionResponse).SyncTransactionResponse
   234  		//var txs []*transaction.Transaction
   235  		//for _, tranPb := range response.Transactions {
   236  		//if _, ok := f.fetched[utils.ConvertH256ToHash(tranPb.Hash)]; ok == false {
   237  		//	f.fetched[utils.ConvertH256ToHash(tranPb.Hash)] = true
   238  		//	tx, err := transaction.FromProtoMessage(tranPb)
   239  		//	if err == nil {
   240  		//		txs = append(txs, tx)
   241  		//	}
   242  		//}
   243  		//}
   244  		//if len(txs) > 0 {
   245  		//	f.addTxs(txs)
   246  		//}
   247  	}
   248  	return nil
   249  }