6 "github.com/bytom/bytom/consensus/bcrp"
7 "github.com/bytom/bytom/consensus/segwit"
8 "github.com/bytom/bytom/crypto/sha3pool"
9 "github.com/bytom/bytom/errors"
10 "github.com/bytom/bytom/protocol/bc"
11 "github.com/bytom/bytom/protocol/vm"
14 // NewTxVMContext generates the vm.Context for BVM
15 func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, stateData [][]byte, args [][]byte) *vm.Context {
18 blockHeight = vs.block.BlockHeader.GetHeight()
19 numResults = uint64(len(tx.ResultIds))
20 entryID = bc.EntryID(entry) // TODO(bobg): pass this in, don't recompute it
28 switch e := entry.(type) {
30 a1 := e.Value.AssetId.Bytes()
32 amount = &e.Value.Amount
33 destPos = &e.WitnessDestination.Position
36 spentOutput := tx.Entries[*e.SpentOutputId].(*bc.OriginalOutput)
37 a1 := spentOutput.Source.Value.AssetId.Bytes()
39 amount = &spentOutput.Source.Value.Amount
40 destPos = &e.WitnessDestination.Position
41 s := e.SpentOutputId.Bytes()
46 txSigHashFn := func() []byte {
48 hasher := sha3pool.Get256()
49 defer sha3pool.Put256(hasher)
51 entryID.WriteTo(hasher)
56 hashBytes := hash.Bytes()
57 txSigHash = &hashBytes
67 result := &vm.Context{
68 VMVersion: prog.VmVersion,
69 Code: convertProgram(prog.Code, vs.converter),
73 EntryID: entryID.Bytes(),
75 TxVersion: &tx.Version,
76 BlockHeight: &blockHeight,
78 TxSigHash: txSigHashFn,
79 NumResults: &numResults,
83 SpentOutputID: spentOutputID,
84 CheckOutput: ec.checkOutput,
90 func convertProgram(prog []byte, converter ProgramConverterFunc) []byte {
91 if segwit.IsP2WPKHScript(prog) {
92 if witnessProg, err := segwit.ConvertP2PKHSigProgram([]byte(prog)); err == nil {
95 } else if segwit.IsP2WSHScript(prog) {
96 if witnessProg, err := segwit.ConvertP2SHProgram([]byte(prog)); err == nil {
99 } else if bcrp.IsCallContractScript(prog) {
100 if contractProg, err := converter(prog); err == nil {
107 type entryContext struct {
109 entries map[bc.Hash]bc.Entry
112 func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, state [][]byte, expansion bool) (bool, error) {
113 checkEntry := func(e bc.Entry) (bool, error) {
114 check := func(prog *bc.Program, value *bc.AssetAmount, stateData [][]byte) bool {
115 return (prog.VmVersion == vmVersion &&
116 bytes.Equal(prog.Code, code) &&
117 bytes.Equal(value.AssetId.Bytes(), assetID) &&
118 value.Amount == amount &&
119 bytesEqual(stateData, state))
122 switch e := e.(type) {
123 case *bc.OriginalOutput:
124 return check(e.ControlProgram, e.Source.Value, e.StateData), nil
129 // The spec requires prog.Code to be the empty string only
130 // when !expansion. When expansion is true, we prepopulate
131 // prog.Code to give check() a freebie match.
133 // (The spec always requires prog.VmVersion to be zero.)
136 return check(&prog, e.Source.Value, [][]byte{}), nil
139 return false, vm.ErrContext
142 checkMux := func(m *bc.Mux) (bool, error) {
143 if index >= uint64(len(m.WitnessDestinations)) {
144 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= %d", index, len(m.WitnessDestinations))
146 eID := m.WitnessDestinations[index].Ref
147 e, ok := ec.entries[*eID]
149 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for mux destination %d, id %x, not found", index, eID.Bytes())
154 switch e := ec.entry.(type) {
159 d, ok := ec.entries[*e.WitnessDestination.Ref]
161 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for issuance destination %x not found", e.WitnessDestination.Ref.Bytes())
163 if m, ok := d.(*bc.Mux); ok {
167 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= 1", index)
172 d, ok := ec.entries[*e.WitnessDestination.Ref]
174 return false, errors.Wrapf(bc.ErrMissingEntry, "entry for spend destination %x not found", e.WitnessDestination.Ref.Bytes())
176 if m, ok := d.(*bc.Mux); ok {
180 return false, errors.Wrapf(vm.ErrBadValue, "index %d >= 1", index)
185 return false, vm.ErrContext
188 func bytesEqual(a, b [][]byte) bool {
189 if (a == nil) != (b == nil) {
193 if len(a) != len(b) {
197 for i, v := range a {
198 if !bytes.Equal(v, b[i]) {