OSDN Git Service

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