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 }