OSDN Git Service

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