OSDN Git Service

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