OSDN Git Service

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