OSDN Git Service

Revert "update master (#487)" (#518)
[bytom/bytom.git] / protocol / bc / legacy / map.go
1 package legacy
2
3 import (
4         "github.com/bytom/consensus"
5         "github.com/bytom/crypto/sha3pool"
6         "github.com/bytom/protocol/bc"
7         "github.com/bytom/protocol/vm"
8         "github.com/bytom/protocol/vm/vmutil"
9 )
10
11 // MapTx converts a legacy TxData object into its entries-based
12 // representation.
13 func MapTx(oldTx *TxData) *bc.Tx {
14         txid, header, entries := mapTx(oldTx)
15
16         tx := &bc.Tx{
17                 TxHeader: header,
18                 ID:       txid,
19                 Entries:  entries,
20                 InputIDs: make([]bc.Hash, len(oldTx.Inputs)),
21         }
22
23         var (
24                 nonceIDs       = make(map[bc.Hash]bool)
25                 spentOutputIDs = make(map[bc.Hash]bool)
26         )
27         for id, e := range entries {
28                 var ord uint64
29                 switch e := e.(type) {
30                 case *bc.Issuance:
31                         anchor, ok := entries[*e.AnchorId]
32                         if !ok {
33                                 // this tx will be invalid because this issuance is
34                                 // missing an anchor
35                                 continue
36                         }
37                         if _, ok := anchor.(*bc.Nonce); ok {
38                                 nonceIDs[*e.AnchorId] = true
39                         }
40                         ord = e.Ordinal
41                         // resume below after the switch
42
43                 case *bc.Spend:
44                         spentOutputIDs[*e.SpentOutputId] = true
45                         ord = e.Ordinal
46                         // resume below after the switch
47                         if *e.WitnessDestination.Value.AssetId == *consensus.BTMAssetID {
48                                 tx.GasInputIDs = append(tx.GasInputIDs, id)
49                         }
50
51                 case *bc.Coinbase:
52                 default:
53                         continue
54                 }
55                 if ord >= uint64(len(oldTx.Inputs)) {
56                         continue // poorly-formed transaction
57                 }
58                 tx.InputIDs[ord] = id
59         }
60
61         for id := range nonceIDs {
62                 tx.NonceIDs = append(tx.NonceIDs, id)
63         }
64         for id := range spentOutputIDs {
65                 tx.SpentOutputIDs = append(tx.SpentOutputIDs, id)
66         }
67         return tx
68 }
69
70 func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash]bc.Entry) {
71         entryMap = make(map[bc.Hash]bc.Entry)
72
73         addEntry := func(e bc.Entry) bc.Hash {
74                 id := bc.EntryID(e)
75                 entryMap[id] = e
76                 return id
77         }
78
79         // Loop twice over tx.Inputs, once for spends and once for
80         // issuances.  Do spends first so the entry ID of the first spend is
81         // available in case an issuance needs it for its anchor.
82
83         var (
84                 firstSpend   *bc.Spend
85                 firstSpendID bc.Hash
86                 spends       []*bc.Spend
87                 issuances    []*bc.Issuance
88                 coinbase     *bc.Coinbase
89                 muxSources   = make([]*bc.ValueSource, len(tx.Inputs))
90         )
91
92         for i, inp := range tx.Inputs {
93                 if oldSp, ok := inp.TypedInput.(*SpendInput); ok {
94                         prog := &bc.Program{VmVersion: oldSp.VMVersion, Code: oldSp.ControlProgram}
95                         src := &bc.ValueSource{
96                                 Ref:      &oldSp.SourceID,
97                                 Value:    &oldSp.AssetAmount,
98                                 Position: oldSp.SourcePosition,
99                         }
100                         out := bc.NewOutput(src, prog, &oldSp.RefDataHash, 0) // ordinal doesn't matter for prevouts, only for result outputs
101                         prevoutID := addEntry(out)
102                         refdatahash := hashData(inp.ReferenceData)
103                         sp := bc.NewSpend(&prevoutID, &refdatahash, uint64(i))
104                         sp.WitnessArguments = oldSp.Arguments
105                         id := addEntry(sp)
106                         muxSources[i] = &bc.ValueSource{
107                                 Ref:   &id,
108                                 Value: &oldSp.AssetAmount,
109                         }
110                         if firstSpend == nil {
111                                 firstSpend = sp
112                                 firstSpendID = id
113                         }
114                         spends = append(spends, sp)
115                 }
116         }
117
118         for i, inp := range tx.Inputs {
119                 if oldIss, ok := inp.TypedInput.(*IssuanceInput); ok {
120                         // Note: asset definitions, initial block ids, and issuance
121                         // programs are omitted here because they do not contribute to
122                         // the body hash of an issuance.
123
124                         var (
125                                 anchorID    bc.Hash
126                                 setAnchored = func(*bc.Hash) {}
127                         )
128
129                         if len(oldIss.Nonce) > 0 {
130                                 assetID := oldIss.AssetID()
131
132                                 builder := vmutil.NewBuilder()
133                                 builder.AddData(oldIss.Nonce).AddOp(vm.OP_DROP)
134                                 builder.AddOp(vm.OP_ASSET).AddData(assetID.Bytes()).AddOp(vm.OP_EQUAL)
135                                 prog, _ := builder.Build() // error is impossible
136
137                                 nonce := bc.NewNonce(&bc.Program{VmVersion: 1, Code: prog})
138                                 anchorID = addEntry(nonce)
139                                 setAnchored = nonce.SetAnchored
140                         } else if firstSpend != nil {
141                                 anchorID = firstSpendID
142                                 setAnchored = firstSpend.SetAnchored
143                         }
144
145                         val := inp.AssetAmount()
146
147                         refdatahash := hashData(inp.ReferenceData)
148                         assetdefhash := hashData(oldIss.AssetDefinition)
149                         iss := bc.NewIssuance(&anchorID, &val, &refdatahash, uint64(i))
150                         iss.WitnessAssetDefinition = &bc.AssetDefinition{
151                                 InitialBlockId: &oldIss.InitialBlock,
152                                 Data:           &assetdefhash,
153                                 IssuanceProgram: &bc.Program{
154                                         VmVersion: oldIss.VMVersion,
155                                         Code:      oldIss.IssuanceProgram,
156                                 },
157                         }
158                         iss.WitnessArguments = oldIss.Arguments
159                         issID := addEntry(iss)
160                         setAnchored(&issID)
161
162                         muxSources[i] = &bc.ValueSource{
163                                 Ref:   &issID,
164                                 Value: &val,
165                         }
166                         issuances = append(issuances, iss)
167                 }
168         }
169
170         if len(tx.Inputs) == 1 {
171                 if oldCB, ok := tx.Inputs[0].TypedInput.(*CoinbaseInput); ok {
172                         cb := bc.NewCoinbase(oldCB.Arbitrary)
173                         cbID := addEntry(cb)
174
175                         out := tx.Outputs[0]
176                         muxSources = []*bc.ValueSource{{
177                                 Ref:   &cbID,
178                                 Value: &out.AssetAmount,
179                         }}
180                         coinbase = cb
181                 }
182         }
183
184         mux := bc.NewMux(muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
185         muxID := addEntry(mux)
186
187         for _, sp := range spends {
188                 spentOutput := entryMap[*sp.SpentOutputId].(*bc.Output)
189                 sp.SetDestination(&muxID, spentOutput.Source.Value, sp.Ordinal)
190         }
191         for _, iss := range issuances {
192                 iss.SetDestination(&muxID, iss.Value, iss.Ordinal)
193         }
194
195         if coinbase != nil {
196                 muxSource := mux.Sources[0]
197                 cb := entryMap[*muxSource.Ref].(*bc.Coinbase)
198                 cb.SetDestination(&muxID, muxSource.Value, 0)
199         }
200
201         var resultIDs []*bc.Hash
202
203         for i, out := range tx.Outputs {
204                 src := &bc.ValueSource{
205                         Ref:      &muxID,
206                         Value:    &out.AssetAmount,
207                         Position: uint64(i),
208                 }
209                 var dest *bc.ValueDestination
210                 if vmutil.IsUnspendable(out.ControlProgram) {
211                         // retirement
212                         refdatahash := hashData(out.ReferenceData)
213                         r := bc.NewRetirement(src, &refdatahash, uint64(i))
214                         rID := addEntry(r)
215                         resultIDs = append(resultIDs, &rID)
216                         dest = &bc.ValueDestination{
217                                 Ref:      &rID,
218                                 Position: 0,
219                         }
220                 } else {
221                         // non-retirement
222                         prog := &bc.Program{out.VMVersion, out.ControlProgram}
223                         refdatahash := hashData(out.ReferenceData)
224                         o := bc.NewOutput(src, prog, &refdatahash, uint64(i))
225                         oID := addEntry(o)
226                         resultIDs = append(resultIDs, &oID)
227                         dest = &bc.ValueDestination{
228                                 Ref:      &oID,
229                                 Position: 0,
230                         }
231                 }
232                 dest.Value = src.Value
233                 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
234         }
235
236         refdatahash := hashData(tx.ReferenceData)
237         h := bc.NewTxHeader(tx.Version, tx.SerializedSize, tx.TimeRange, resultIDs, &refdatahash)
238         headerID = addEntry(h)
239
240         return headerID, h, entryMap
241 }
242
243 func mapBlockHeader(old *BlockHeader) (bhID bc.Hash, bh *bc.BlockHeader) {
244         bh = bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, &old.Seed, old.Timestamp, &old.TransactionsMerkleRoot, &old.AssetsMerkleRoot, &old.TransactionStatus, old.Nonce, old.Bits)
245         bhID = bc.EntryID(bh)
246         return
247 }
248
249 // MapBlock converts a legacy block to bc block
250 func MapBlock(old *Block) *bc.Block {
251         if old == nil {
252                 return nil // if old is nil, so should new be
253         }
254         b := new(bc.Block)
255         b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
256         for _, oldTx := range old.Transactions {
257                 b.Transactions = append(b.Transactions, oldTx.Tx)
258                 b.BlockHeader.SerializedSize += oldTx.TxData.SerializedSize
259         }
260         return b
261 }
262
263 func hashData(data []byte) bc.Hash {
264         var b32 [32]byte
265         sha3pool.Sum256(b32[:], data)
266         return bc.NewHash(b32)
267 }