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

     1  package contract
     2  
     3  import (
     4  	"encoding/hex"
     5  
     6  	"github.com/bytom/bytom/consensus/segwit"
     7  	"github.com/bytom/bytom/protocol/bc"
     8  	"github.com/bytom/bytom/protocol/bc/types"
     9  	"github.com/bytom/bytom/protocol/vm/vmutil"
    10  )
    11  
    12  type tracer struct {
    13  	index *instanceIndex
    14  }
    15  
    16  func newTracer(instances []*Instance) *tracer {
    17  	index := newInstanceIndex()
    18  	for _, inst := range instances {
    19  		index.save(inst)
    20  	}
    21  	return &tracer{index: index}
    22  }
    23  
    24  func (t *tracer) getInstance(traceID string) *Instance {
    25  	return t.index.getByID(traceID)
    26  }
    27  
    28  func (t *tracer) allInstances() []*Instance {
    29  	return t.index.getAll()
    30  }
    31  
    32  func (t *tracer) addInstances(instances []*Instance) {
    33  	for _, inst := range instances {
    34  		t.index.save(inst)
    35  	}
    36  }
    37  
    38  func (t *tracer) removeInstance(traceID string) {
    39  	t.index.remove(traceID)
    40  }
    41  
    42  func (t *tracer) applyBlock(block *types.Block) []*Instance {
    43  	var newInstances []*Instance
    44  	for _, tx := range block.Transactions {
    45  		transfers := parseTransfers(tx)
    46  		for _, transfer := range transfers {
    47  			if len(transfer.inUTXOs) == 0 {
    48  				continue
    49  			}
    50  
    51  			if inst := t.index.getByUTXO(transfer.inUTXOs[0].Hash); inst != nil {
    52  				newInst := inst.transferTo(transfer, block.Height)
    53  				newInstances = append(newInstances, newInst)
    54  			}
    55  		}
    56  	}
    57  	t.saveInstances(newInstances)
    58  	return newInstances
    59  }
    60  
    61  func (t *tracer) detachBlock(block *types.Block) []*Instance {
    62  	var newInstances []*Instance
    63  	for i := len(block.Transactions) - 1; i >= 0; i-- {
    64  		tx := block.Transactions[i]
    65  		transfers := parseTransfers(tx)
    66  		for _, transfer := range transfers {
    67  			utxos := append(transfer.outUTXOs, transfer.inUTXOs...)
    68  			if inst := t.index.getByUTXO(utxos[0].Hash); inst != nil {
    69  				newInst := inst.rollbackTo(transfer)
    70  				newInstances = append(newInstances, newInst)
    71  			}
    72  		}
    73  	}
    74  	t.saveInstances(newInstances)
    75  	return newInstances
    76  }
    77  
    78  type transfer struct {
    79  	txHash   bc.Hash
    80  	inUTXOs  []*UTXO
    81  	outUTXOs []*UTXO
    82  }
    83  
    84  func parseTransfers(tx *types.Tx) []*transfer {
    85  	inUTXOs, outUTXOs := parseContractUTXOs(tx)
    86  	groupInUTXOs := groupUTXOs(inUTXOs)
    87  	groupOutUTXOs := groupUTXOs(outUTXOs)
    88  
    89  	var transfers []*transfer
    90  	for program, utxos := range groupInUTXOs {
    91  		outUTXOs := groupOutUTXOs[program]
    92  		transfers = append(transfers, &transfer{txHash: tx.ID, inUTXOs: utxos, outUTXOs: outUTXOs})
    93  	}
    94  	for program, utxos := range groupOutUTXOs {
    95  		if _, ok := groupInUTXOs[program]; !ok {
    96  			transfers = append(transfers, &transfer{txHash: tx.ID, outUTXOs: utxos})
    97  		}
    98  	}
    99  	return transfers
   100  }
   101  
   102  func groupUTXOs(utxos []*UTXO) map[string][]*UTXO {
   103  	groupUTXOs := make(map[string][]*UTXO)
   104  	for _, utxo := range utxos {
   105  		program := hex.EncodeToString(utxo.Program)
   106  		groupUTXOs[program] = append(groupUTXOs[program], utxo)
   107  	}
   108  	return groupUTXOs
   109  }
   110  
   111  func parseContractUTXOs(tx *types.Tx) ([]*UTXO, []*UTXO) {
   112  	var inUTXOs, outUTXOs []*UTXO
   113  	for i, input := range tx.Inputs {
   114  		if isContract(input.ControlProgram()) && input.InputType() == types.SpendInputType {
   115  			inUTXOs = append(inUTXOs, inputToUTXO(tx, i))
   116  		}
   117  	}
   118  
   119  	for i, output := range tx.Outputs {
   120  		if isContract(output.ControlProgram) && output.OutputType() == types.OriginalOutputType {
   121  			outUTXOs = append(outUTXOs, outputToUTXO(tx, i))
   122  		}
   123  	}
   124  	return inUTXOs, outUTXOs
   125  }
   126  
   127  func isContract(program []byte) bool {
   128  	return !(segwit.IsP2WScript(program) || vmutil.IsUnspendable(program))
   129  }
   130  
   131  func (t *tracer) saveInstances(instances []*Instance) {
   132  	for _, inst := range instances {
   133  		t.index.save(inst)
   134  	}
   135  }