OSDN Git Service

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