OSDN Git Service

fix conflict
[bytom/vapor.git] / protocol / bc / types / map.go
1 package types
2
3 import (
4         "fmt"
5
6         log "github.com/sirupsen/logrus"
7
8         "github.com/vapor/consensus"
9         "github.com/vapor/protocol/bc"
10         "github.com/vapor/protocol/vm"
11         "github.com/vapor/protocol/vm/vmutil"
12 )
13
14 // MapTx converts a types TxData object into its entries-based
15 // representation.
16 func MapTx(oldTx *TxData) *bc.Tx {
17         fmt.Println("MapTx start...")
18         txID, txHeader, entries := mapTx(oldTx)
19         tx := &bc.Tx{
20                 TxHeader: txHeader,
21                 ID:       txID,
22                 Entries:  entries,
23                 InputIDs: make([]bc.Hash, len(oldTx.Inputs)),
24         }
25
26         spentOutputIDs := make(map[bc.Hash]bool)
27         mainchainOutputIDs := make(map[bc.Hash]bool)
28         for id, e := range entries {
29                 var ord uint64
30                 switch e := e.(type) {
31                 case *bc.CrossChainInput:
32                         ord = e.Ordinal
33                         mainchainOutputIDs[*e.MainchainOutputId] = true
34                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
35                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
36                         }
37
38                 case *bc.Spend:
39                         ord = e.Ordinal
40                         spentOutputIDs[*e.SpentOutputId] = true
41                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
42                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
43                         }
44
45                 case *bc.Coinbase:
46                         ord = 0
47                         tx.GasInputIDs = append(tx.GasInputIDs, id)
48
49                 default:
50                         continue
51                 }
52
53                 if ord >= uint64(len(oldTx.Inputs)) {
54                         continue
55                 }
56                 tx.InputIDs[ord] = id
57         }
58
59         for id := range spentOutputIDs {
60                 tx.SpentOutputIDs = append(tx.SpentOutputIDs, id)
61         }
62         for id := range mainchainOutputIDs {
63                 tx.MainchainOutputIDs = append(tx.MainchainOutputIDs, id)
64         }
65         return tx
66 }
67
68 func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) {
69         entryMap = make(map[bc.Hash]bc.Entry)
70         addEntry := func(e bc.Entry) bc.Hash {
71                 id := bc.EntryID(e)
72                 entryMap[id] = e
73                 return id
74         }
75
76         var (
77                 spends   []*bc.Spend
78                 crossIns []*bc.CrossChainInput
79                 coinbase *bc.Coinbase
80         )
81
82         muxSources := make([]*bc.ValueSource, len(tx.Inputs))
83         for i, input := range tx.Inputs {
84                 switch inp := input.TypedInput.(type) {
85                 case *SpendInput:
86                         // create entry for prevout
87                         prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
88                         src := &bc.ValueSource{
89                                 Ref:      &inp.SourceID,
90                                 Value:    &inp.AssetAmount,
91                                 Position: inp.SourcePosition,
92                         }
93                         prevout := bc.NewIntraChainOutput(src, prog, 0) // ordinal doesn't matter for prevouts, only for result outputs
94                         prevoutID := addEntry(prevout)
95                         // create entry for spend
96                         spend := bc.NewSpend(&prevoutID, uint64(i))
97                         spend.WitnessArguments = inp.Arguments
98                         spendID := addEntry(spend)
99                         // setup mux
100                         muxSources[i] = &bc.ValueSource{
101                                 Ref:   &spendID,
102                                 Value: &inp.AssetAmount,
103                         }
104                         spends = append(spends, spend)
105
106                 case *CoinbaseInput:
107                         coinbase = bc.NewCoinbase(inp.Arbitrary)
108                         coinbaseID := addEntry(coinbase)
109
110                         out := tx.Outputs[0]
111                         value := out.AssetAmount()
112                         muxSources[i] = &bc.ValueSource{
113                                 Ref:   &coinbaseID,
114                                 Value: &value,
115                         }
116
117                 case *UnvoteInput:
118                         prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
119                         src := &bc.ValueSource{
120                                 Ref:      &inp.SourceID,
121                                 Value:    &inp.AssetAmount,
122                                 Position: inp.SourcePosition,
123                         }
124                         prevout := bc.NewVoteOutput(src, prog, 0, inp.Vote) // ordinal doesn't matter for prevouts, only for result outputs
125                         prevoutID := addEntry(prevout)
126                         // create entry for spend
127                         spend := bc.NewSpend(&prevoutID, uint64(i))
128                         spend.WitnessArguments = inp.Arguments
129                         spendID := addEntry(spend)
130                         // setup mux
131                         muxSources[i] = &bc.ValueSource{
132                                 Ref:   &spendID,
133                                 Value: &inp.AssetAmount,
134                         }
135                         spends = append(spends, spend)
136
137                 case *CrossChainInput:
138                         // TODO: fed peg script
139                         prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
140                         src := &bc.ValueSource{
141                                 Ref:      &inp.SourceID,
142                                 Value:    &inp.AssetAmount,
143                                 Position: inp.SourcePosition,
144                         }
145                         prevout := bc.NewIntraChainOutput(src, prog, 0) // ordinal doesn't matter
146                         outputID := bc.EntryID(prevout)
147                         crossIn := bc.NewCrossChainInput(&outputID, &inp.AssetAmount, prog, uint64(i))
148                         crossIn.WitnessArguments = inp.Arguments
149                         crossInID := addEntry(crossIn)
150                         muxSources[i] = &bc.ValueSource{
151                                 Ref:   &crossInID,
152                                 Value: &inp.AssetAmount,
153                         }
154                         crossIns = append(crossIns, crossIn)
155                 }
156         }
157
158         mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
159         muxID := addEntry(mux)
160
161         // connect the inputs to the mux
162         for _, spend := range spends {
163                 switch spentOutput := entryMap[*spend.SpentOutputId].(type) {
164                 case *bc.IntraChainOutput:
165                         spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal)
166
167                 case *bc.VoteOutput:
168                         spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal)
169                 }
170         }
171
172         for _, crossIn := range crossIns {
173                 crossIn.SetDestination(&muxID, crossIn.Value, crossIn.Ordinal)
174         }
175
176         if coinbase != nil {
177                 coinbase.SetDestination(&muxID, mux.Sources[0].Value, 0)
178         }
179
180         // convert types.outputs to the bc.output
181         var resultIDs []*bc.Hash
182         for i, out := range tx.Outputs {
183                 value := out.AssetAmount()
184                 src := &bc.ValueSource{
185                         Ref:      &muxID,
186                         Value:    &value,
187                         Position: uint64(i),
188                 }
189                 var resultID bc.Hash
190                 switch {
191                 // must deal with retirement first due to cases' priorities in the switch statement
192                 case vmutil.IsUnspendable(out.ControlProgram()):
193                         // retirement
194                         r := bc.NewRetirement(src, uint64(i))
195                         resultID = addEntry(r)
196
197                 case out.OutputType() == IntraChainOutputType:
198                         // non-retirement intra-chain tx
199                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
200                         o := bc.NewIntraChainOutput(src, prog, uint64(i))
201                         resultID = addEntry(o)
202
203                 case out.OutputType() == CrossChainOutputType:
204                         // non-retirement cross-chain tx
205                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
206                         o := bc.NewCrossChainOutput(src, prog, uint64(i))
207                         resultID = addEntry(o)
208
209                 case out.OutputType() == VoteOutputType:
210                         // non-retirement vote tx
211                         voteOut, _ := out.TypedOutput.(*VoteTxOutput)
212                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
213                         o := bc.NewVoteOutput(src, prog, uint64(i), voteOut.Vote)
214                         resultID = addEntry(o)
215
216                 default:
217                         log.Warn("unknown outType")
218                 }
219
220                 dest := &bc.ValueDestination{
221                         Value:    src.Value,
222                         Ref:      &resultID,
223                         Position: 0,
224                 }
225                 resultIDs = append(resultIDs, &resultID)
226                 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
227         }
228
229         h := bc.NewTxHeader(tx.Version, tx.SerializedSize, tx.TimeRange, resultIDs)
230         return addEntry(h), h, entryMap
231 }
232
233 func mapBlockHeader(old *BlockHeader) (bc.Hash, *bc.BlockHeader) {
234         bh := bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, old.Timestamp, &old.TransactionsMerkleRoot, &old.TransactionStatusHash, old.Witness)
235         return bc.EntryID(bh), bh
236 }
237
238 // MapBlock converts a types block to bc block
239 func MapBlock(old *Block) *bc.Block {
240         if old == nil {
241                 return nil
242         }
243
244         b := new(bc.Block)
245         b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
246         for _, oldTx := range old.Transactions {
247                 b.Transactions = append(b.Transactions, oldTx.Tx)
248         }
249         return b
250 }