OSDN Git Service

Mov (#518)
[bytom/vapor.git] / protocol / bc / types / map.go
1 package types
2
3 import (
4         log "github.com/sirupsen/logrus"
5         "golang.org/x/crypto/sha3"
6
7         "github.com/bytom/vapor/consensus"
8         "github.com/bytom/vapor/protocol/bc"
9         "github.com/bytom/vapor/protocol/vm"
10         "github.com/bytom/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         mainchainOutputIDs := make(map[bc.Hash]bool)
26         for id, e := range entries {
27                 var ord uint64
28                 switch e := e.(type) {
29                 case *bc.CrossChainInput:
30                         ord = e.Ordinal
31                         mainchainOutputIDs[*e.MainchainOutputId] = true
32                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
33                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
34                         }
35
36                 case *bc.Spend:
37                         ord = e.Ordinal
38                         spentOutputIDs[*e.SpentOutputId] = true
39                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
40                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
41                         }
42
43                 case *bc.VetoInput:
44                         ord = e.Ordinal
45                         spentOutputIDs[*e.SpentOutputId] = true
46                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
47                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
48                         }
49
50                 case *bc.Coinbase:
51                         ord = 0
52                         tx.GasInputIDs = append(tx.GasInputIDs, id)
53
54                 default:
55                         continue
56                 }
57
58                 if ord < uint64(len(oldTx.Inputs)) {
59                         tx.InputIDs[ord] = id
60                 }
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                         totalAmount := uint64(0)
115                         for _, out := range tx.Outputs {
116                                 totalAmount += out.AssetAmount().Amount
117                         }
118                         muxSources[i] = &bc.ValueSource{
119                                 Ref: &coinbaseID,
120                                 Value: &bc.AssetAmount{
121                                         AssetId: consensus.BTMAssetID,
122                                         Amount:  totalAmount,
123                                 },
124                         }
125
126                 case *VetoInput:
127                         prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
128                         src := &bc.ValueSource{
129                                 Ref:      &inp.SourceID,
130                                 Value:    &inp.AssetAmount,
131                                 Position: inp.SourcePosition,
132                         }
133                         prevout := bc.NewVoteOutput(src, prog, 0, inp.Vote) // ordinal doesn't matter for prevouts, only for result outputs
134                         prevoutID := addEntry(prevout)
135                         // create entry for VetoInput
136                         vetoInput := bc.NewVetoInput(&prevoutID, uint64(i))
137                         vetoInput.WitnessArguments = inp.Arguments
138                         vetoVoteID := addEntry(vetoInput)
139                         // setup mux
140                         muxSources[i] = &bc.ValueSource{
141                                 Ref:   &vetoVoteID,
142                                 Value: &inp.AssetAmount,
143                         }
144                         vetoInputs = append(vetoInputs, vetoInput)
145
146                 case *CrossChainInput:
147                         prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
148                         src := &bc.ValueSource{
149                                 Ref:      &inp.SourceID,
150                                 Value:    &inp.AssetAmount,
151                                 Position: inp.SourcePosition,
152                         }
153
154                         prevout := bc.NewIntraChainOutput(src, prog, 0) // ordinal doesn't matter
155                         mainchainOutputID := addEntry(prevout)
156
157                         assetDefHash := bc.NewHash(sha3.Sum256(inp.AssetDefinition))
158                         assetDef := &bc.AssetDefinition{
159                                 Data: &assetDefHash,
160                                 IssuanceProgram: &bc.Program{
161                                         VmVersion: inp.IssuanceVMVersion,
162                                         Code:      inp.IssuanceProgram,
163                                 },
164                         }
165
166                         crossIn := bc.NewCrossChainInput(&mainchainOutputID, prog, uint64(i), assetDef, inp.AssetDefinition)
167                         crossIn.WitnessArguments = inp.Arguments
168                         crossInID := addEntry(crossIn)
169                         muxSources[i] = &bc.ValueSource{
170                                 Ref:   &crossInID,
171                                 Value: &inp.AssetAmount,
172                         }
173                         crossIns = append(crossIns, crossIn)
174                 }
175         }
176
177         mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
178         muxID := addEntry(mux)
179
180         // connect the inputs to the mux
181         for _, spend := range spends {
182                 spentOutput := entryMap[*spend.SpentOutputId].(*bc.IntraChainOutput)
183                 spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal)
184         }
185
186         for _, vetoInput := range vetoInputs {
187                 voteOutput := entryMap[*vetoInput.SpentOutputId].(*bc.VoteOutput)
188                 vetoInput.SetDestination(&muxID, voteOutput.Source.Value, vetoInput.Ordinal)
189         }
190
191         for _, crossIn := range crossIns {
192                 mainchainOutput := entryMap[*crossIn.MainchainOutputId].(*bc.IntraChainOutput)
193                 crossIn.SetDestination(&muxID, mainchainOutput.Source.Value, crossIn.Ordinal)
194         }
195
196         if coinbase != nil {
197                 coinbase.SetDestination(&muxID, mux.Sources[0].Value, 0)
198         }
199
200         // convert types.outputs to the bc.output
201         var resultIDs []*bc.Hash
202         for i, out := range tx.Outputs {
203                 value := out.AssetAmount()
204                 src := &bc.ValueSource{
205                         Ref:      &muxID,
206                         Value:    &value,
207                         Position: uint64(i),
208                 }
209                 var resultID bc.Hash
210                 switch {
211                 // must deal with retirement first due to cases' priorities in the switch statement
212                 case vmutil.IsUnspendable(out.ControlProgram()):
213                         // retirement
214                         r := bc.NewRetirement(src, uint64(i))
215                         resultID = addEntry(r)
216
217                 case out.OutputType() == IntraChainOutputType:
218                         // non-retirement intra-chain tx
219                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
220                         o := bc.NewIntraChainOutput(src, prog, uint64(i))
221                         resultID = addEntry(o)
222
223                 case out.OutputType() == CrossChainOutputType:
224                         // non-retirement cross-chain tx
225                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
226                         o := bc.NewCrossChainOutput(src, prog, uint64(i))
227                         resultID = addEntry(o)
228
229                 case out.OutputType() == VoteOutputType:
230                         // non-retirement vote tx
231                         voteOut, _ := out.TypedOutput.(*VoteOutput)
232                         prog := &bc.Program{out.VMVersion(), out.ControlProgram()}
233                         o := bc.NewVoteOutput(src, prog, uint64(i), voteOut.Vote)
234                         resultID = addEntry(o)
235
236                 default:
237                         log.Warn("unknown outType")
238                 }
239
240                 dest := &bc.ValueDestination{
241                         Value:    src.Value,
242                         Ref:      &resultID,
243                         Position: 0,
244                 }
245                 resultIDs = append(resultIDs, &resultID)
246                 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
247         }
248
249         h := bc.NewTxHeader(tx.Version, tx.SerializedSize, tx.TimeRange, resultIDs)
250         return addEntry(h), h, entryMap
251 }
252
253 func mapBlockHeader(old *BlockHeader) (bc.Hash, *bc.BlockHeader) {
254         bh := bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, old.Timestamp, &old.TransactionsMerkleRoot, &old.TransactionStatusHash, old.Witness)
255         return bc.EntryID(bh), bh
256 }
257
258 // MapBlock converts a types block to bc block
259 func MapBlock(old *Block) *bc.Block {
260         if old == nil {
261                 return nil
262         }
263
264         b := new(bc.Block)
265         b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
266         for _, oldTx := range old.Transactions {
267                 b.Transactions = append(b.Transactions, oldTx.Tx)
268         }
269         return b
270 }