OSDN Git Service

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