OSDN Git Service

delete some black utxo (#2129)
[bytom/bytom.git] / protocol / bc / types / map.go
1 package types
2
3 import (
4         "github.com/bytom/bytom/consensus"
5         "github.com/bytom/bytom/protocol/bc"
6         "github.com/bytom/bytom/protocol/vm"
7         "github.com/bytom/bytom/protocol/vm/vmutil"
8 )
9
10 type mapHelper struct {
11         txData   *TxData
12         entryMap map[bc.Hash]bc.Entry
13
14         // data using during the map process
15         spends    []*bc.Spend
16         issuances []*bc.Issuance
17         vetos     []*bc.VetoInput
18         coinbase  *bc.Coinbase
19
20         muxSources []*bc.ValueSource
21         mux        *bc.Mux
22
23         inputIDs       []bc.Hash
24         spentOutputIDs []bc.Hash
25         resultIDs      []*bc.Hash
26 }
27
28 func newMapHelper(txData *TxData) *mapHelper {
29         return &mapHelper{
30                 txData:         txData,
31                 entryMap:       make(map[bc.Hash]bc.Entry),
32                 spends:         []*bc.Spend{},
33                 issuances:      []*bc.Issuance{},
34                 vetos:          []*bc.VetoInput{},
35                 muxSources:     make([]*bc.ValueSource, len(txData.Inputs)),
36                 inputIDs:       make([]bc.Hash, len(txData.Inputs)),
37                 spentOutputIDs: []bc.Hash{},
38                 resultIDs:      []*bc.Hash{},
39         }
40 }
41
42 func (mh *mapHelper) addEntry(e bc.Entry) bc.Hash {
43         id := bc.EntryID(e)
44         mh.entryMap[id] = e
45         return id
46 }
47
48 func (mh *mapHelper) generateTx() *bc.Tx {
49         header := bc.NewTxHeader(mh.txData.Version, mh.txData.SerializedSize, mh.txData.TimeRange, mh.resultIDs)
50         return &bc.Tx{
51                 TxHeader:       header,
52                 ID:             mh.addEntry(header),
53                 Entries:        mh.entryMap,
54                 InputIDs:       mh.inputIDs,
55                 SpentOutputIDs: mh.spentOutputIDs,
56         }
57 }
58
59 func (mh *mapHelper) mapCoinbaseInput(i int, input *CoinbaseInput) {
60         mh.coinbase = bc.NewCoinbase(input.Arbitrary)
61         mh.inputIDs[i] = mh.addEntry(mh.coinbase)
62         var totalAmount uint64
63         for _, output := range mh.txData.Outputs {
64                 totalAmount += output.Amount
65         }
66         mh.muxSources[i] = &bc.ValueSource{
67                 Ref:   &mh.inputIDs[i],
68                 Value: &bc.AssetAmount{AssetId: consensus.BTMAssetID, Amount: totalAmount},
69         }
70 }
71
72 func (mh *mapHelper) mapIssuanceInput(i int, input *IssuanceInput) {
73         nonceHash := input.NonceHash()
74         assetDefHash := input.AssetDefinitionHash()
75         assetID := input.AssetID()
76         value := bc.AssetAmount{AssetId: &assetID, Amount: input.Amount}
77
78         issuance := bc.NewIssuance(&nonceHash, &value, uint64(i))
79         issuance.WitnessAssetDefinition = &bc.AssetDefinition{
80                 Data: &assetDefHash,
81                 IssuanceProgram: &bc.Program{
82                         VmVersion: input.VMVersion,
83                         Code:      input.IssuanceProgram,
84                 },
85         }
86
87         issuance.WitnessArguments = input.Arguments
88         mh.issuances = append(mh.issuances, issuance)
89         mh.inputIDs[i] = mh.addEntry(issuance)
90         mh.muxSources[i] = &bc.ValueSource{
91                 Ref:   &mh.inputIDs[i],
92                 Value: &value,
93         }
94 }
95
96 func (mh *mapHelper) mapSpendInput(i int, input *SpendInput) {
97         // create entry for prevout
98         prog := &bc.Program{VmVersion: input.VMVersion, Code: input.ControlProgram}
99         src := &bc.ValueSource{
100                 Ref:      &input.SourceID,
101                 Value:    &input.AssetAmount,
102                 Position: input.SourcePosition,
103         }
104
105         prevout := bc.NewOriginalOutput(src, prog, input.StateData, 0) // ordinal doesn't matter for prevouts, only for result outputs
106         prevoutID := mh.addEntry(prevout)
107
108         // create entry for spend
109         spend := bc.NewSpend(&prevoutID, uint64(i))
110         spend.WitnessArguments = input.Arguments
111         mh.spends = append(mh.spends, spend)
112         mh.inputIDs[i] = mh.addEntry(spend)
113         mh.spentOutputIDs = append(mh.spentOutputIDs, prevoutID)
114         mh.muxSources[i] = &bc.ValueSource{
115                 Ref:   &mh.inputIDs[i],
116                 Value: &input.AssetAmount,
117         }
118 }
119
120 func (mh *mapHelper) mapVetoInput(i int, input *VetoInput) {
121         prog := &bc.Program{VmVersion: input.VMVersion, Code: input.ControlProgram}
122         src := &bc.ValueSource{
123                 Ref:      &input.SourceID,
124                 Value:    &input.AssetAmount,
125                 Position: input.SourcePosition,
126         }
127
128         prevout := bc.NewVoteOutput(src, prog, input.StateData, 0, input.Vote) // ordinal doesn't matter for prevouts, only for result outputs
129         prevoutID := mh.addEntry(prevout)
130         // create entry for VetoInput
131         vetoInput := bc.NewVetoInput(&prevoutID, uint64(i))
132         vetoInput.WitnessArguments = input.Arguments
133         mh.vetos = append(mh.vetos, vetoInput)
134         mh.inputIDs[i] = mh.addEntry(vetoInput)
135         mh.spentOutputIDs = append(mh.spentOutputIDs, prevoutID)
136         mh.muxSources[i] = &bc.ValueSource{
137                 Ref:   &mh.inputIDs[i],
138                 Value: &input.AssetAmount,
139         }
140 }
141
142 func (mh *mapHelper) mapInputs() {
143         for i, input := range mh.txData.Inputs {
144                 switch typedInput := input.TypedInput.(type) {
145                 case *IssuanceInput:
146                         mh.mapIssuanceInput(i, typedInput)
147                 case *SpendInput:
148                         mh.mapSpendInput(i, typedInput)
149                 case *VetoInput:
150                         mh.mapVetoInput(i, typedInput)
151                 case *CoinbaseInput:
152                         mh.mapCoinbaseInput(i, typedInput)
153                 default:
154                         panic("fail on handle transaction input")
155                 }
156         }
157 }
158
159 func (mh *mapHelper) initMux() {
160         mh.mux = bc.NewMux(mh.muxSources, &bc.Program{VmVersion: 1, Code: []byte{byte(vm.OP_TRUE)}})
161         muxID := mh.addEntry(mh.mux)
162
163         // connect the inputs to the mux
164         for _, spend := range mh.spends {
165                 spentOutput := mh.entryMap[*spend.SpentOutputId].(*bc.OriginalOutput)
166                 spend.SetDestination(&muxID, spentOutput.Source.Value, spend.Ordinal)
167         }
168
169         for _, vetoInput := range mh.vetos {
170                 voteOutput := mh.entryMap[*vetoInput.SpentOutputId].(*bc.VoteOutput)
171                 vetoInput.SetDestination(&muxID, voteOutput.Source.Value, vetoInput.Ordinal)
172         }
173
174         for _, issuance := range mh.issuances {
175                 issuance.SetDestination(&muxID, issuance.Value, issuance.Ordinal)
176         }
177
178         if mh.coinbase != nil {
179                 mh.coinbase.SetDestination(&muxID, mh.mux.Sources[0].Value, 0)
180         }
181 }
182
183 func (mh *mapHelper) mapOutputs() {
184         muxID := bc.EntryID(mh.mux)
185         for i, out := range mh.txData.Outputs {
186                 src := &bc.ValueSource{Ref: &muxID, Value: &out.AssetAmount, Position: uint64(i)}
187                 prog := &bc.Program{out.VMVersion, out.ControlProgram}
188
189                 var resultID bc.Hash
190                 switch {
191                 case vmutil.IsUnspendable(out.ControlProgram):
192                         r := bc.NewRetirement(src, uint64(i))
193                         resultID = mh.addEntry(r)
194
195                 case out.OutputType() == OriginalOutputType:
196                         o := bc.NewOriginalOutput(src, prog, out.StateData, uint64(i))
197                         resultID = mh.addEntry(o)
198
199                 case out.OutputType() == VoteOutputType:
200                         voteOut, _ := out.TypedOutput.(*VoteOutput)
201                         v := bc.NewVoteOutput(src, prog, out.StateData, uint64(i), voteOut.Vote)
202                         resultID = mh.addEntry(v)
203
204                 default:
205                         panic("fail on handle transaction output")
206                 }
207
208                 mh.resultIDs = append(mh.resultIDs, &resultID)
209                 mh.mux.WitnessDestinations = append(mh.mux.WitnessDestinations, &bc.ValueDestination{
210                         Value:    src.Value,
211                         Ref:      &resultID,
212                         Position: 0,
213                 })
214         }
215 }
216
217 // MapTx converts a types TxData object into its entries-based
218 // representation.
219 func MapTx(txData *TxData) *bc.Tx {
220         mh := newMapHelper(txData)
221         mh.mapInputs()
222         mh.initMux()
223         mh.mapOutputs()
224         return mh.generateTx()
225 }
226
227 func mapBlockHeader(old *BlockHeader) (bc.Hash, *bc.BlockHeader) {
228         bh := bc.NewBlockHeader(old.Version, old.Height, &old.PreviousBlockHash, old.Timestamp, &old.TransactionsMerkleRoot)
229         return bc.EntryID(bh), bh
230 }
231
232 // MapBlock converts a types block to bc block
233 func MapBlock(old *Block) *bc.Block {
234         if old == nil {
235                 return nil
236         }
237
238         b := new(bc.Block)
239         b.ID, b.BlockHeader = mapBlockHeader(&old.BlockHeader)
240         for _, oldTx := range old.Transactions {
241                 b.Transactions = append(b.Transactions, oldTx.Tx)
242         }
243         return b
244 }