OSDN Git Service

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