github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/validation/vmcontext.go (about) 1 package validation 2 3 import ( 4 "bytes" 5 6 "github.com/bytom/bytom/consensus/segwit" 7 "github.com/bytom/bytom/crypto/sha3pool" 8 "github.com/bytom/bytom/errors" 9 "github.com/bytom/bytom/protocol/bc" 10 "github.com/bytom/bytom/protocol/vm" 11 ) 12 13 // NewTxVMContext generates the vm.Context for BVM 14 func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args [][]byte) *vm.Context { 15 var ( 16 tx = vs.tx 17 blockHeight = vs.block.BlockHeader.GetHeight() 18 numResults = uint64(len(tx.ResultIds)) 19 entryID = bc.EntryID(entry) // TODO(bobg): pass this in, don't recompute it 20 21 assetID *[]byte 22 amount *uint64 23 destPos *uint64 24 spentOutputID *[]byte 25 ) 26 27 switch e := entry.(type) { 28 case *bc.Issuance: 29 a1 := e.Value.AssetId.Bytes() 30 assetID = &a1 31 amount = &e.Value.Amount 32 destPos = &e.WitnessDestination.Position 33 34 case *bc.Spend: 35 spentOutput := tx.Entries[*e.SpentOutputId].(*bc.Output) 36 a1 := spentOutput.Source.Value.AssetId.Bytes() 37 assetID = &a1 38 amount = &spentOutput.Source.Value.Amount 39 destPos = &e.WitnessDestination.Position 40 s := e.SpentOutputId.Bytes() 41 spentOutputID = &s 42 } 43 44 var txSigHash *[]byte 45 txSigHashFn := func() []byte { 46 if txSigHash == nil { 47 hasher := sha3pool.Get256() 48 defer sha3pool.Put256(hasher) 49 50 entryID.WriteTo(hasher) 51 tx.ID.WriteTo(hasher) 52 53 var hash bc.Hash 54 hash.ReadFrom(hasher) 55 hashBytes := hash.Bytes() 56 txSigHash = &hashBytes 57 } 58 return *txSigHash 59 } 60 61 ec := &entryContext{ 62 entry: entry, 63 entries: tx.Entries, 64 } 65 66 result := &vm.Context{ 67 VMVersion: prog.VmVersion, 68 Code: witnessProgram(prog.Code), 69 Arguments: args, 70 71 EntryID: entryID.Bytes(), 72 73 TxVersion: &tx.Version, 74 BlockHeight: &blockHeight, 75 76 TxSigHash: txSigHashFn, 77 NumResults: &numResults, 78 AssetID: assetID, 79 Amount: amount, 80 DestPos: destPos, 81 SpentOutputID: spentOutputID, 82 CheckOutput: ec.checkOutput, 83 } 84 85 return result 86 } 87 88 func witnessProgram(prog []byte) []byte { 89 if segwit.IsP2WPKHScript(prog) { 90 if witnessProg, err := segwit.ConvertP2PKHSigProgram([]byte(prog)); err == nil { 91 return witnessProg 92 } 93 } else if segwit.IsP2WSHScript(prog) { 94 if witnessProg, err := segwit.ConvertP2SHProgram([]byte(prog)); err == nil { 95 return witnessProg 96 } 97 } 98 return prog 99 } 100 101 type entryContext struct { 102 entry bc.Entry 103 entries map[bc.Hash]bc.Entry 104 } 105 106 func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, expansion bool) (bool, error) { 107 checkEntry := func(e bc.Entry) (bool, error) { 108 check := func(prog *bc.Program, value *bc.AssetAmount) bool { 109 return (prog.VmVersion == vmVersion && 110 bytes.Equal(prog.Code, code) && 111 bytes.Equal(value.AssetId.Bytes(), assetID) && 112 value.Amount == amount) 113 } 114 115 switch e := e.(type) { 116 case *bc.Output: 117 return check(e.ControlProgram, e.Source.Value), nil 118 119 case *bc.Retirement: 120 var prog bc.Program 121 if expansion { 122 // The spec requires prog.Code to be the empty string only 123 // when !expansion. When expansion is true, we prepopulate 124 // prog.Code to give check() a freebie match. 125 // 126 // (The spec always requires prog.VmVersion to be zero.) 127 prog.Code = code 128 } 129 return check(&prog, e.Source.Value), nil 130 } 131 132 return false, vm.ErrContext 133 } 134 135 checkMux := func(m *bc.Mux) (bool, error) { 136 if index >= uint64(len(m.WitnessDestinations)) { 137 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= %d", index, len(m.WitnessDestinations)) 138 } 139 eID := m.WitnessDestinations[index].Ref 140 e, ok := ec.entries[*eID] 141 if !ok { 142 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for mux destination %d, id %x, not found", index, eID.Bytes()) 143 } 144 return checkEntry(e) 145 } 146 147 switch e := ec.entry.(type) { 148 case *bc.Mux: 149 return checkMux(e) 150 151 case *bc.Issuance: 152 d, ok := ec.entries[*e.WitnessDestination.Ref] 153 if !ok { 154 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for issuance destination %x not found", e.WitnessDestination.Ref.Bytes()) 155 } 156 if m, ok := d.(*bc.Mux); ok { 157 return checkMux(m) 158 } 159 if index != 0 { 160 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= 1", index) 161 } 162 return checkEntry(d) 163 164 case *bc.Spend: 165 d, ok := ec.entries[*e.WitnessDestination.Ref] 166 if !ok { 167 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for spend destination %x not found", e.WitnessDestination.Ref.Bytes()) 168 } 169 if m, ok := d.(*bc.Mux); ok { 170 return checkMux(m) 171 } 172 if index != 0 { 173 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= 1", index) 174 } 175 return checkEntry(d) 176 } 177 178 return false, vm.ErrContext 179 }