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 }