github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/contract/trace_service.go (about)

     1  package contract
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  
     7  	log "github.com/sirupsen/logrus"
     8  
     9  	"github.com/bytom/bytom/protocol/bc"
    10  	"github.com/bytom/bytom/protocol/bc/types"
    11  )
    12  
    13  const maxAdvanceTraceBlockNum = 3600
    14  
    15  var (
    16  	errGivenTxTooEarly      = errors.New("given tx exceed the max num of blocks ahead")
    17  	errTxAndBlockIsMismatch = errors.New("given tx hash and block hash is mismatch")
    18  	errTxNotIncludeContract = errors.New("input of tx not include utxo contract")
    19  )
    20  
    21  type TraceService struct {
    22  	sync.RWMutex
    23  	tracer           *tracer
    24  	infra            *Infrastructure
    25  	scheduler        *traceScheduler
    26  	unconfirmedIndex map[bc.Hash]*TreeNode
    27  	endedInstances   map[string]bool
    28  	bestHeight       uint64
    29  	bestHash         bc.Hash
    30  }
    31  
    32  func NewTraceService(infra *Infrastructure) *TraceService {
    33  	allInstances, err := infra.Repository.LoadInstances()
    34  	if err != nil {
    35  		log.WithFields(log.Fields{"module": logModule, "err": err}).Fatal("load instances from db")
    36  	}
    37  
    38  	chainStatus := initChainStatus(infra)
    39  	scheduler := newTraceScheduler(infra)
    40  	inSyncInstances := dispatchInstances(allInstances, scheduler, infra.Chain.FinalizedHeight())
    41  
    42  	service := &TraceService{
    43  		infra:            infra,
    44  		tracer:           newTracer(inSyncInstances),
    45  		scheduler:        scheduler,
    46  		unconfirmedIndex: make(map[bc.Hash]*TreeNode),
    47  		endedInstances:   make(map[string]bool),
    48  		bestHeight:       chainStatus.BlockHeight,
    49  		bestHash:         chainStatus.BlockHash,
    50  	}
    51  	scheduler.start(service)
    52  	return service
    53  }
    54  
    55  func initChainStatus(infra *Infrastructure) *ChainStatus {
    56  	chainStatus := infra.Repository.GetChainStatus()
    57  	if chainStatus == nil {
    58  		bestHeight, bestHash := infra.Chain.BestChain()
    59  		chainStatus = &ChainStatus{BlockHeight: bestHeight, BlockHash: bestHash}
    60  		if err := infra.Repository.SaveChainStatus(chainStatus); err != nil {
    61  			log.WithFields(log.Fields{"module": logModule, "err": err}).Fatal("init chain status for trace service")
    62  		}
    63  	}
    64  	return chainStatus
    65  }
    66  
    67  func dispatchInstances(instances []*Instance, scheduler *traceScheduler, finalizedHeight uint64) []*Instance {
    68  	var result []*Instance
    69  	for _, inst := range instances {
    70  		if inst.Status == InSync {
    71  			result = append(result, inst)
    72  		} else if inst.Status == Ended {
    73  			if inst.EndedHeight < finalizedHeight {
    74  				result = append(result, inst)
    75  			}
    76  		} else if inst.Status == Lagging {
    77  			if err := scheduler.addNewJob(inst); err != nil {
    78  				log.WithFields(log.Fields{"module": logModule, "err": err}).Fatal("add new job when init tracer")
    79  			}
    80  		}
    81  	}
    82  	return result
    83  }
    84  
    85  func (t *TraceService) BestHeight() uint64 {
    86  	t.RLock()
    87  	defer t.RUnlock()
    88  	return t.bestHeight
    89  }
    90  
    91  func (t *TraceService) BestHash() bc.Hash {
    92  	t.RLock()
    93  	defer t.RUnlock()
    94  	return t.bestHash
    95  }
    96  
    97  func (t *TraceService) ApplyBlock(block *types.Block) error {
    98  	t.Lock()
    99  	defer t.Unlock()
   100  
   101  	newInstances := t.tracer.applyBlock(block)
   102  	t.processEndedInstances(newInstances)
   103  	t.bestHeight++
   104  	t.bestHash = block.Hash()
   105  	return t.infra.Repository.SaveInstancesWithStatus(newInstances, t.bestHeight, t.bestHash)
   106  }
   107  
   108  func (t *TraceService) DetachBlock(block *types.Block) error {
   109  	t.Lock()
   110  	defer t.Unlock()
   111  
   112  	newInstances := t.tracer.detachBlock(block)
   113  	t.processEndedInstances(nil)
   114  	t.bestHeight--
   115  	t.bestHash = block.PreviousBlockHash
   116  	return t.infra.Repository.SaveInstancesWithStatus(newInstances, t.bestHeight, t.bestHash)
   117  }
   118  
   119  func (t *TraceService) AddUnconfirmedTx(tx *types.Tx) {
   120  	transfers := parseTransfers(tx)
   121  	for _, transfer := range transfers {
   122  		inUTXOs, outUTXOs := transfer.inUTXOs, transfer.outUTXOs
   123  		if len(inUTXOs) == 0 || len(outUTXOs) == 0 {
   124  			return
   125  		}
   126  
   127  		treeNode := &TreeNode{TxHash: tx.ID, UTXOs: outUTXOs}
   128  		if inst := t.tracer.index.getByUTXO(inUTXOs[0].Hash); inst != nil {
   129  			inst.Unconfirmed = append(inst.Unconfirmed, treeNode)
   130  			t.addToUnconfirmedIndex(treeNode, outUTXOs)
   131  			return
   132  		}
   133  
   134  		if parent, ok := t.unconfirmedIndex[inUTXOs[0].Hash]; ok {
   135  			parent.Children = append(parent.Children, treeNode)
   136  			t.addToUnconfirmedIndex(treeNode, outUTXOs)
   137  		}
   138  	}
   139  }
   140  
   141  func (t *TraceService) CreateInstance(txHash, blockHash bc.Hash) ([]string, error) {
   142  	block, err := t.infra.Chain.GetBlockByHash(&blockHash)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	if bestHeight, _ := t.infra.Chain.BestChain(); bestHeight-block.Height > maxAdvanceTraceBlockNum {
   148  		return nil, errGivenTxTooEarly
   149  	}
   150  
   151  	tx := findTx(block, txHash)
   152  	if tx == nil {
   153  		return nil, errTxAndBlockIsMismatch
   154  	}
   155  
   156  	transfers := parseTransfers(tx)
   157  	if len(transfers) == 0 {
   158  		return nil, errTxNotIncludeContract
   159  	}
   160  
   161  	var traceIDs []string
   162  	for _, transfer := range transfers {
   163  		inst := newInstance(transfer, block)
   164  		traceIDs = append(traceIDs, inst.TraceID)
   165  		if err := t.addNewTraceJob(inst); err != nil {
   166  			return nil, err
   167  		}
   168  	}
   169  	return traceIDs, nil
   170  }
   171  
   172  func (t *TraceService) RemoveInstance(traceID string) error {
   173  	t.Lock()
   174  	defer t.Unlock()
   175  
   176  	t.infra.Repository.RemoveInstance(traceID)
   177  	t.tracer.removeInstance(traceID)
   178  	return nil
   179  }
   180  
   181  func (t *TraceService) GetInstance(traceID string) (*Instance, error) {
   182  	return t.infra.Repository.GetInstance(traceID)
   183  }
   184  
   185  func (t *TraceService) takeOverInstances(instances []*Instance, blockHash bc.Hash) bool {
   186  	t.Lock()
   187  	defer t.Unlock()
   188  
   189  	if blockHash != t.bestHash {
   190  		return false
   191  	}
   192  
   193  	for _, inst := range instances {
   194  		if inst.Status != Ended {
   195  			inst.Status = InSync
   196  		}
   197  	}
   198  
   199  	if err := t.infra.Repository.SaveInstances(instances); err != nil {
   200  		log.WithFields(log.Fields{"module": logModule, "err": err}).Error("save instances when take over instances")
   201  		return false
   202  	}
   203  
   204  	t.tracer.addInstances(instances)
   205  	t.processEndedInstances(instances)
   206  	return true
   207  }
   208  
   209  func (t *TraceService) processEndedInstances(instances []*Instance) {
   210  	for _, inst := range instances {
   211  		if inst.Status == Ended {
   212  			t.endedInstances[inst.TraceID] = true
   213  		}
   214  	}
   215  
   216  	finalizedHeight := t.infra.Chain.FinalizedHeight()
   217  	for traceID := range t.endedInstances {
   218  		inst := t.tracer.getInstance(traceID)
   219  		if inst.Status != Ended {
   220  			delete(t.endedInstances, traceID)
   221  		} else if finalizedHeight >= inst.EndedHeight {
   222  			delete(t.endedInstances, traceID)
   223  			t.tracer.removeInstance(traceID)
   224  		}
   225  	}
   226  }
   227  
   228  func (t *TraceService) addNewTraceJob(inst *Instance) error {
   229  	if err := t.infra.Repository.SaveInstances([]*Instance{inst}); err != nil {
   230  		return err
   231  	}
   232  
   233  	if inst.Status != Ended {
   234  		if err := t.scheduler.addNewJob(inst); err != nil {
   235  			return err
   236  		}
   237  	}
   238  	return nil
   239  }
   240  
   241  func (t *TraceService) addToUnconfirmedIndex(treeNode *TreeNode, utxos []*UTXO) {
   242  	for _, utxo := range utxos {
   243  		t.unconfirmedIndex[utxo.Hash] = treeNode
   244  	}
   245  }
   246  
   247  func findTx(block *types.Block, txHash bc.Hash) *types.Tx {
   248  	for _, tx := range block.Transactions {
   249  		if tx.ID == txHash {
   250  			return tx
   251  		}
   252  	}
   253  	return nil
   254  }