OSDN Git Service

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