Golang implemented sidechain for Bytom
リビジョン | 8c4ce00e959a22bb2a0803d82e1a3597e96abd61 (tree) |
---|---|
日時 | 2020-03-23 21:05:58 |
作者 | oys <oys@oysd...> |
コミッター | oys |
merge mov
@@ -1,8 +1,6 @@ | ||
1 | 1 | package match |
2 | 2 | |
3 | 3 | import ( |
4 | - "encoding/hex" | |
5 | - "math" | |
6 | 4 | "math/big" |
7 | 5 | |
8 | 6 | "github.com/bytom/vapor/application/mov/common" |
@@ -13,19 +11,18 @@ import ( | ||
13 | 11 | "github.com/bytom/vapor/protocol/bc" |
14 | 12 | "github.com/bytom/vapor/protocol/bc/types" |
15 | 13 | "github.com/bytom/vapor/protocol/vm" |
16 | - "github.com/bytom/vapor/protocol/vm/vmutil" | |
17 | 14 | ) |
18 | 15 | |
19 | 16 | // Engine is used to generate math transactions |
20 | 17 | type Engine struct { |
21 | - orderBook *OrderBook | |
22 | - maxFeeRate float64 | |
23 | - nodeProgram []byte | |
18 | + orderBook *OrderBook | |
19 | + feeStrategy FeeStrategy | |
20 | + rewardProgram []byte | |
24 | 21 | } |
25 | 22 | |
26 | 23 | // NewEngine return a new Engine |
27 | -func NewEngine(orderBook *OrderBook, maxFeeRate float64, nodeProgram []byte) *Engine { | |
28 | - return &Engine{orderBook: orderBook, maxFeeRate: maxFeeRate, nodeProgram: nodeProgram} | |
24 | +func NewEngine(orderBook *OrderBook, feeStrategy FeeStrategy, rewardProgram []byte) *Engine { | |
25 | + return &Engine{orderBook: orderBook, feeStrategy: feeStrategy, rewardProgram: rewardProgram} | |
29 | 26 | } |
30 | 27 | |
31 | 28 | // HasMatchedTx check does the input trade pair can generate a match deal |
@@ -65,38 +62,20 @@ func (e *Engine) NextMatchedTx(tradePairs ...*common.TradePair) (*types.Tx, erro | ||
65 | 62 | return tx, nil |
66 | 63 | } |
67 | 64 | |
68 | -func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error { | |
69 | - txFee, err := CalcMatchedTxFee(txData, e.maxFeeRate) | |
70 | - if err != nil { | |
71 | - return err | |
65 | +func (e *Engine) addMatchTxFeeOutput(txData *types.TxData, refunds []RefundAssets, fees []*bc.AssetAmount) error { | |
66 | + for _, feeAmount := range fees { | |
67 | + txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*feeAmount.AssetId, feeAmount.Amount, e.rewardProgram)) | |
72 | 68 | } |
73 | 69 | |
74 | - for assetID, matchTxFee := range txFee { | |
75 | - feeAmount, reminder := matchTxFee.FeeAmount, int64(0) | |
76 | - if matchTxFee.FeeAmount > matchTxFee.MaxFeeAmount { | |
77 | - feeAmount = matchTxFee.MaxFeeAmount | |
78 | - reminder = matchTxFee.FeeAmount - matchTxFee.MaxFeeAmount | |
79 | - } | |
80 | - txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(feeAmount), e.nodeProgram)) | |
81 | - | |
82 | - // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction | |
83 | - averageAmount := reminder / int64(len(txData.Inputs)) | |
84 | - if averageAmount == 0 { | |
85 | - averageAmount = 1 | |
86 | - } | |
87 | - | |
88 | - for i := 0; i < len(txData.Inputs) && reminder > 0; i++ { | |
70 | + for i, refund := range refunds { | |
71 | + // each trading participant may be refunded multiple assets | |
72 | + for _, assetAmount := range refund { | |
89 | 73 | contractArgs, err := segwit.DecodeP2WMCProgram(txData.Inputs[i].ControlProgram()) |
90 | 74 | if err != nil { |
91 | 75 | return err |
92 | 76 | } |
93 | 77 | |
94 | - if i == len(txData.Inputs)-1 { | |
95 | - txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(reminder), contractArgs.SellerProgram)) | |
96 | - } else { | |
97 | - txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, uint64(averageAmount), contractArgs.SellerProgram)) | |
98 | - } | |
99 | - reminder -= averageAmount | |
78 | + txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*assetAmount.AssetId, assetAmount.Amount, contractArgs.SellerProgram)) | |
100 | 79 | } |
101 | 80 | } |
102 | 81 | return nil |
@@ -104,7 +83,7 @@ func (e *Engine) addMatchTxFeeOutput(txData *types.TxData) error { | ||
104 | 83 | |
105 | 84 | func (e *Engine) addPartialTradeOrder(tx *types.Tx) error { |
106 | 85 | for i, output := range tx.Outputs { |
107 | - if !segwit.IsP2WMCScript(output.ControlProgram()) { | |
86 | + if !segwit.IsP2WMCScript(output.ControlProgram()) || output.AssetAmount().Amount == 0 { | |
108 | 87 | continue |
109 | 88 | } |
110 | 89 |
@@ -120,17 +99,18 @@ func (e *Engine) addPartialTradeOrder(tx *types.Tx) error { | ||
120 | 99 | |
121 | 100 | func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) { |
122 | 101 | txData := &types.TxData{Version: 1} |
123 | - for i, order := range orders { | |
102 | + for _, order := range orders { | |
124 | 103 | input := types.NewSpendInput(nil, *order.Utxo.SourceID, *order.FromAssetID, order.Utxo.Amount, order.Utxo.SourcePos, order.Utxo.ControlProgram) |
125 | 104 | txData.Inputs = append(txData.Inputs, input) |
105 | + } | |
126 | 106 | |
127 | - oppositeOrder := orders[calcOppositeIndex(len(orders), i)] | |
128 | - if err := addMatchTxOutput(txData, input, order, oppositeOrder.Utxo.Amount); err != nil { | |
129 | - return nil, err | |
130 | - } | |
107 | + receivedAmounts, priceDiffs := CalcReceivedAmount(orders) | |
108 | + allocatedAssets := e.feeStrategy.Allocate(receivedAmounts, priceDiffs) | |
109 | + if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets.Receives); err != nil { | |
110 | + return nil, err | |
131 | 111 | } |
132 | 112 | |
133 | - if err := e.addMatchTxFeeOutput(txData); err != nil { | |
113 | + if err := e.addMatchTxFeeOutput(txData, allocatedAssets.Refunds, allocatedAssets.Fees); err != nil { | |
134 | 114 | return nil, err |
135 | 115 | } |
136 | 116 |
@@ -143,94 +123,71 @@ func (e *Engine) buildMatchTx(orders []*common.Order) (*types.Tx, error) { | ||
143 | 123 | return types.NewTx(*txData), nil |
144 | 124 | } |
145 | 125 | |
146 | -// MatchedTxFee is object to record the mov tx's fee information | |
147 | -type MatchedTxFee struct { | |
148 | - MaxFeeAmount int64 | |
149 | - FeeAmount int64 | |
150 | -} | |
151 | - | |
152 | -// CalcMatchedTxFee is used to calculate tx's MatchedTxFees | |
153 | -func CalcMatchedTxFee(txData *types.TxData, maxFeeRate float64) (map[bc.AssetID]*MatchedTxFee, error) { | |
154 | - assetFeeMap := make(map[bc.AssetID]*MatchedTxFee) | |
155 | - dealProgMaps := make(map[string]bool) | |
156 | - | |
157 | - for _, input := range txData.Inputs { | |
158 | - assetFeeMap[input.AssetID()] = &MatchedTxFee{FeeAmount: int64(input.AssetAmount().Amount)} | |
159 | - contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram()) | |
160 | - if err != nil { | |
161 | - return nil, err | |
162 | - } | |
163 | - | |
164 | - dealProgMaps[hex.EncodeToString(contractArgs.SellerProgram)] = true | |
165 | - } | |
166 | - | |
167 | - for _, input := range txData.Inputs { | |
168 | - contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram()) | |
126 | +func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts, deductFeeReceives []*bc.AssetAmount) error { | |
127 | + for i, order := range orders { | |
128 | + contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram) | |
169 | 129 | if err != nil { |
170 | - return nil, err | |
130 | + return err | |
171 | 131 | } |
172 | 132 | |
173 | - oppositeAmount := uint64(assetFeeMap[contractArgs.RequestedAsset].FeeAmount) | |
174 | - receiveAmount := vprMath.MinUint64(CalcRequestAmount(input.Amount(), contractArgs), oppositeAmount) | |
175 | - assetFeeMap[input.AssetID()].MaxFeeAmount = calcMaxFeeAmount(calcShouldPayAmount(receiveAmount, contractArgs), maxFeeRate) | |
176 | - } | |
133 | + requestAmount := CalcRequestAmount(order.Utxo.Amount, contractArgs.RatioNumerator, contractArgs.RatioDenominator) | |
134 | + receivedAmount := receivedAmounts[i].Amount | |
135 | + shouldPayAmount := calcShouldPayAmount(receivedAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator) | |
136 | + isPartialTrade := requestAmount > receivedAmount | |
177 | 137 | |
178 | - for _, output := range txData.Outputs { | |
179 | - assetAmount := output.AssetAmount() | |
180 | - if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) { | |
181 | - assetFeeMap[*assetAmount.AssetId].FeeAmount -= int64(assetAmount.Amount) | |
182 | - if assetFeeMap[*assetAmount.AssetId].FeeAmount <= 0 { | |
183 | - delete(assetFeeMap, *assetAmount.AssetId) | |
184 | - } | |
138 | + setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmounts[i].Amount) | |
139 | + txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, deductFeeReceives[i].Amount, contractArgs.SellerProgram)) | |
140 | + if isPartialTrade { | |
141 | + txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram)) | |
185 | 142 | } |
186 | 143 | } |
187 | - return assetFeeMap, nil | |
144 | + return nil | |
188 | 145 | } |
189 | 146 | |
190 | -func addMatchTxOutput(txData *types.TxData, txInput *types.TxInput, order *common.Order, oppositeAmount uint64) error { | |
191 | - contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram) | |
192 | - if err != nil { | |
193 | - return err | |
194 | - } | |
195 | - | |
196 | - requestAmount := CalcRequestAmount(order.Utxo.Amount, contractArgs) | |
197 | - receiveAmount := vprMath.MinUint64(requestAmount, oppositeAmount) | |
198 | - shouldPayAmount := calcShouldPayAmount(receiveAmount, contractArgs) | |
199 | - isPartialTrade := requestAmount > receiveAmount | |
200 | - | |
201 | - setMatchTxArguments(txInput, isPartialTrade, len(txData.Outputs), receiveAmount) | |
202 | - txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receiveAmount, contractArgs.SellerProgram)) | |
203 | - if isPartialTrade { | |
204 | - txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram)) | |
205 | - } | |
206 | - return nil | |
147 | +func calcOppositeIndex(size int, selfIdx int) int { | |
148 | + return (selfIdx + 1) % size | |
207 | 149 | } |
208 | 150 | |
209 | 151 | // CalcRequestAmount is from amount * numerator / ratioDenominator |
210 | -func CalcRequestAmount(fromAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 { | |
152 | +func CalcRequestAmount(fromAmount uint64, ratioNumerator, ratioDenominator int64) uint64 { | |
211 | 153 | res := big.NewInt(0).SetUint64(fromAmount) |
212 | - res.Mul(res, big.NewInt(contractArg.RatioNumerator)).Quo(res, big.NewInt(contractArg.RatioDenominator)) | |
154 | + res.Mul(res, big.NewInt(ratioNumerator)).Quo(res, big.NewInt(ratioDenominator)) | |
213 | 155 | if !res.IsUint64() { |
214 | 156 | return 0 |
215 | 157 | } |
216 | 158 | return res.Uint64() |
217 | 159 | } |
218 | 160 | |
219 | -func calcShouldPayAmount(receiveAmount uint64, contractArg *vmutil.MagneticContractArgs) uint64 { | |
161 | +func calcShouldPayAmount(receiveAmount uint64, ratioNumerator, ratioDenominator int64) uint64 { | |
220 | 162 | res := big.NewInt(0).SetUint64(receiveAmount) |
221 | - res.Mul(res, big.NewInt(contractArg.RatioDenominator)).Quo(res, big.NewInt(contractArg.RatioNumerator)) | |
163 | + res.Mul(res, big.NewInt(ratioDenominator)).Quo(res, big.NewInt(ratioNumerator)) | |
222 | 164 | if !res.IsUint64() { |
223 | 165 | return 0 |
224 | 166 | } |
225 | 167 | return res.Uint64() |
226 | 168 | } |
227 | 169 | |
228 | -func calcMaxFeeAmount(shouldPayAmount uint64, maxFeeRate float64) int64 { | |
229 | - return int64(math.Ceil(float64(shouldPayAmount) * maxFeeRate)) | |
230 | -} | |
170 | +// CalcReceivedAmount return amount of assets received by each participant in the matching transaction and the price difference | |
171 | +func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, []*bc.AssetAmount) { | |
172 | + var receivedAmounts, priceDiffs, shouldPayAmounts []*bc.AssetAmount | |
173 | + for i, order := range orders { | |
174 | + requestAmount := CalcRequestAmount(order.Utxo.Amount, order.RatioNumerator, order.RatioDenominator) | |
175 | + oppositeOrder := orders[calcOppositeIndex(len(orders), i)] | |
176 | + receiveAmount := vprMath.MinUint64(oppositeOrder.Utxo.Amount, requestAmount) | |
177 | + shouldPayAmount := calcShouldPayAmount(receiveAmount, order.RatioNumerator, order.RatioDenominator) | |
178 | + receivedAmounts = append(receivedAmounts, &bc.AssetAmount{AssetId: order.ToAssetID, Amount: receiveAmount}) | |
179 | + shouldPayAmounts = append(shouldPayAmounts, &bc.AssetAmount{AssetId: order.FromAssetID, Amount: shouldPayAmount}) | |
180 | + } | |
231 | 181 | |
232 | -func calcOppositeIndex(size int, selfIdx int) int { | |
233 | - return (selfIdx + 1) % size | |
182 | + for i, receivedAmount := range receivedAmounts { | |
183 | + oppositeShouldPayAmount := shouldPayAmounts[calcOppositeIndex(len(orders), i)] | |
184 | + if oppositeShouldPayAmount.Amount > receivedAmount.Amount { | |
185 | + assetID := oppositeShouldPayAmount.AssetId | |
186 | + amount := oppositeShouldPayAmount.Amount - receivedAmount.Amount | |
187 | + priceDiffs = append(priceDiffs, &bc.AssetAmount{AssetId: assetID, Amount: amount}) | |
188 | + } | |
189 | + } | |
190 | + return receivedAmounts, priceDiffs | |
234 | 191 | } |
235 | 192 | |
236 | 193 | // IsMatched check does the orders can be exchange |
@@ -240,13 +197,12 @@ func IsMatched(orders []*common.Order) bool { | ||
240 | 197 | return false |
241 | 198 | } |
242 | 199 | |
243 | - rate := big.NewRat(sortedOrders[0].RatioDenominator, sortedOrders[0].RatioNumerator) | |
244 | - oppositeRate := big.NewRat(1, 1) | |
245 | - for i := 1; i < len(sortedOrders); i++ { | |
246 | - oppositeRate.Mul(oppositeRate, big.NewRat(sortedOrders[i].RatioNumerator, sortedOrders[i].RatioDenominator)) | |
200 | + product := big.NewRat(1, 1) | |
201 | + for _, order := range orders { | |
202 | + product.Mul(product, big.NewRat(order.RatioNumerator, order.RatioDenominator)) | |
247 | 203 | } |
248 | - | |
249 | - return rate.Cmp(oppositeRate) >= 0 | |
204 | + one := big.NewRat(1, 1) | |
205 | + return product.Cmp(one) <= 0 | |
250 | 206 | } |
251 | 207 | |
252 | 208 | func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) { |
@@ -259,29 +215,6 @@ func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position i | ||
259 | 215 | txInput.SetArguments(arguments) |
260 | 216 | } |
261 | 217 | |
262 | -func validateTradePairs(tradePairs []*common.TradePair) error { | |
263 | - if len(tradePairs) < 2 { | |
264 | - return errors.New("size of trade pairs at least 2") | |
265 | - } | |
266 | - | |
267 | - assetMap := make(map[string]bool) | |
268 | - for _, tradePair := range tradePairs { | |
269 | - assetMap[tradePair.FromAssetID.String()] = true | |
270 | - if *tradePair.FromAssetID == *tradePair.ToAssetID { | |
271 | - return errors.New("from asset id can't equal to asset id") | |
272 | - } | |
273 | - } | |
274 | - | |
275 | - for _, tradePair := range tradePairs { | |
276 | - key := tradePair.ToAssetID.String() | |
277 | - if _, ok := assetMap[key]; !ok { | |
278 | - return errors.New("invalid trade pairs") | |
279 | - } | |
280 | - delete(assetMap, key) | |
281 | - } | |
282 | - return nil | |
283 | -} | |
284 | - | |
285 | 218 | func sortOrders(orders []*common.Order) []*common.Order { |
286 | 219 | if len(orders) == 0 { |
287 | 220 | return nil |
@@ -305,3 +238,26 @@ func sortOrders(orders []*common.Order) []*common.Order { | ||
305 | 238 | } |
306 | 239 | return sortedOrders |
307 | 240 | } |
241 | + | |
242 | +func validateTradePairs(tradePairs []*common.TradePair) error { | |
243 | + if len(tradePairs) < 2 { | |
244 | + return errors.New("size of trade pairs at least 2") | |
245 | + } | |
246 | + | |
247 | + assetMap := make(map[string]bool) | |
248 | + for _, tradePair := range tradePairs { | |
249 | + assetMap[tradePair.FromAssetID.String()] = true | |
250 | + if *tradePair.FromAssetID == *tradePair.ToAssetID { | |
251 | + return errors.New("from asset id can't equal to asset id") | |
252 | + } | |
253 | + } | |
254 | + | |
255 | + for _, tradePair := range tradePairs { | |
256 | + key := tradePair.ToAssetID.String() | |
257 | + if _, ok := assetMap[key]; !ok { | |
258 | + return errors.New("invalid trade pairs") | |
259 | + } | |
260 | + delete(assetMap, key) | |
261 | + } | |
262 | + return nil | |
263 | +} |
@@ -8,7 +8,6 @@ import ( | ||
8 | 8 | "github.com/bytom/vapor/protocol/bc" |
9 | 9 | "github.com/bytom/vapor/protocol/bc/types" |
10 | 10 | "github.com/bytom/vapor/protocol/validation" |
11 | - "github.com/bytom/vapor/testutil" | |
12 | 11 | ) |
13 | 12 | |
14 | 13 | func TestGenerateMatchedTxs(t *testing.T) { |
@@ -76,11 +75,21 @@ func TestGenerateMatchedTxs(t *testing.T) { | ||
76 | 75 | mock.MatchedTxs[6], |
77 | 76 | }, |
78 | 77 | }, |
78 | + { | |
79 | + desc: "multiple assets as a fee", | |
80 | + tradePairs: []*common.TradePair{btc2eth, eth2btc}, | |
81 | + initStoreOrders: []*common.Order{ | |
82 | + mock.Btc2EthOrders[0], mock.Eth2BtcOrders[3], | |
83 | + }, | |
84 | + wantMatchedTxs: []*types.Tx{ | |
85 | + mock.MatchedTxs[11], | |
86 | + }, | |
87 | + }, | |
79 | 88 | } |
80 | 89 | |
81 | 90 | for i, c := range cases { |
82 | 91 | movStore := mock.NewMovStore([]*common.TradePair{btc2eth, eth2btc}, c.initStoreOrders) |
83 | - matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), 0.05, mock.NodeProgram) | |
92 | + matchEngine := NewEngine(NewOrderBook(movStore, nil, nil), NewDefaultFeeStrategy(), mock.RewardProgram) | |
84 | 93 | var gotMatchedTxs []*types.Tx |
85 | 94 | for matchEngine.HasMatchedTx(c.tradePairs...) { |
86 | 95 | matchedTx, err := matchEngine.NextMatchedTx(c.tradePairs...) |
@@ -96,19 +105,19 @@ func TestGenerateMatchedTxs(t *testing.T) { | ||
96 | 105 | continue |
97 | 106 | } |
98 | 107 | |
99 | - for i, gotMatchedTx := range gotMatchedTxs { | |
108 | + for j, gotMatchedTx := range gotMatchedTxs { | |
100 | 109 | if _, err := validation.ValidateTx(gotMatchedTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Version: 1}}); err != nil { |
101 | 110 | t.Fatal(err) |
102 | 111 | } |
103 | 112 | |
104 | - c.wantMatchedTxs[i].Version = 1 | |
105 | - byteData, err := c.wantMatchedTxs[i].MarshalText() | |
113 | + c.wantMatchedTxs[j].Version = 1 | |
114 | + byteData, err := c.wantMatchedTxs[j].MarshalText() | |
106 | 115 | if err != nil { |
107 | 116 | t.Fatal(err) |
108 | 117 | } |
109 | 118 | |
110 | - c.wantMatchedTxs[i].SerializedSize = uint64(len(byteData)) | |
111 | - wantMatchedTx := types.NewTx(c.wantMatchedTxs[i].TxData) | |
119 | + c.wantMatchedTxs[j].SerializedSize = uint64(len(byteData)) | |
120 | + wantMatchedTx := types.NewTx(c.wantMatchedTxs[j].TxData) | |
112 | 121 | if gotMatchedTx.ID != wantMatchedTx.ID { |
113 | 122 | t.Errorf("#%d(%s) the tx hash of got matched tx: %s is not equals want matched tx: %s", i, c.desc, gotMatchedTx.ID.String(), wantMatchedTx.ID.String()) |
114 | 123 | } |
@@ -116,45 +125,6 @@ func TestGenerateMatchedTxs(t *testing.T) { | ||
116 | 125 | } |
117 | 126 | } |
118 | 127 | |
119 | -func TestCalcMatchedTxFee(t *testing.T) { | |
120 | - cases := []struct { | |
121 | - desc string | |
122 | - tx *types.TxData | |
123 | - maxFeeRate float64 | |
124 | - wantMatchedTxFee map[bc.AssetID]*MatchedTxFee | |
125 | - }{ | |
126 | - { | |
127 | - desc: "fee less than max fee", | |
128 | - maxFeeRate: 0.05, | |
129 | - wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 10, MaxFeeAmount: 26}}, | |
130 | - tx: &mock.MatchedTxs[1].TxData, | |
131 | - }, | |
132 | - { | |
133 | - desc: "fee refund in tx", | |
134 | - maxFeeRate: 0.05, | |
135 | - wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{mock.ETH: {FeeAmount: 27, MaxFeeAmount: 27}}, | |
136 | - tx: &mock.MatchedTxs[2].TxData, | |
137 | - }, | |
138 | - { | |
139 | - desc: "fee is zero", | |
140 | - maxFeeRate: 0.05, | |
141 | - wantMatchedTxFee: map[bc.AssetID]*MatchedTxFee{}, | |
142 | - tx: &mock.MatchedTxs[0].TxData, | |
143 | - }, | |
144 | - } | |
145 | - | |
146 | - for i, c := range cases { | |
147 | - gotMatchedTxFee, err := CalcMatchedTxFee(c.tx, c.maxFeeRate) | |
148 | - if err != nil { | |
149 | - t.Fatal(err) | |
150 | - } | |
151 | - | |
152 | - if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) { | |
153 | - t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee) | |
154 | - } | |
155 | - } | |
156 | -} | |
157 | - | |
158 | 128 | func TestValidateTradePairs(t *testing.T) { |
159 | 129 | cases := []struct { |
160 | 130 | desc string |
@@ -0,0 +1,108 @@ | ||
1 | +package match | |
2 | + | |
3 | +import ( | |
4 | + "math" | |
5 | + | |
6 | + "github.com/bytom/vapor/errors" | |
7 | + "github.com/bytom/vapor/protocol/bc" | |
8 | +) | |
9 | + | |
10 | +var ( | |
11 | + // ErrAmountOfFeeOutOfRange represent The fee charged is out of range | |
12 | + ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range") | |
13 | +) | |
14 | + | |
15 | +// AllocatedAssets represent reallocated assets after calculating fees | |
16 | +type AllocatedAssets struct { | |
17 | + Receives []*bc.AssetAmount | |
18 | + Refunds []RefundAssets | |
19 | + Fees []*bc.AssetAmount | |
20 | +} | |
21 | + | |
22 | +// RefundAssets represent alias for assetAmount array, because each transaction participant can be refunded multiple assets | |
23 | +type RefundAssets []*bc.AssetAmount | |
24 | + | |
25 | +// FeeStrategy used to indicate how to charge a matching fee | |
26 | +type FeeStrategy interface { | |
27 | + // Allocate will allocate the price differential in matching transaction to the participants and the fee | |
28 | + // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered | |
29 | + // @param priceDiffs price differential of matching transaction | |
30 | + // @return reallocated assets after calculating fees | |
31 | + Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets | |
32 | + | |
33 | + // Validate verify that the fee charged for a matching transaction is correct | |
34 | + Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error | |
35 | +} | |
36 | + | |
37 | +// DefaultFeeStrategy represent the default fee charge strategy | |
38 | +type DefaultFeeStrategy struct {} | |
39 | + | |
40 | +// NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy | |
41 | +func NewDefaultFeeStrategy() *DefaultFeeStrategy { | |
42 | + return &DefaultFeeStrategy{} | |
43 | +} | |
44 | + | |
45 | +// Allocate will allocate the price differential in matching transaction to the participants and the fee | |
46 | +func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets { | |
47 | + feeMap := make(map[bc.AssetID]uint64) | |
48 | + for _, priceDiff := range priceDiffs { | |
49 | + feeMap[*priceDiff.AssetId] = priceDiff.Amount | |
50 | + } | |
51 | + | |
52 | + var fees []*bc.AssetAmount | |
53 | + refunds := make([]RefundAssets, len(receiveAmounts)) | |
54 | + receives := make([]*bc.AssetAmount, len(receiveAmounts)) | |
55 | + | |
56 | + for i, receiveAmount := range receiveAmounts { | |
57 | + amount := receiveAmount.Amount | |
58 | + minFeeAmount := d.calcMinFeeAmount(amount) | |
59 | + receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount} | |
60 | + feeMap[*receiveAmount.AssetId] += minFeeAmount | |
61 | + | |
62 | + maxFeeAmount := d.calcMaxFeeAmount(amount) | |
63 | + feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0) | |
64 | + if feeAmount > maxFeeAmount { | |
65 | + reminder = feeAmount - maxFeeAmount | |
66 | + feeAmount = maxFeeAmount | |
67 | + } | |
68 | + | |
69 | + fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount}) | |
70 | + | |
71 | + // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction | |
72 | + averageAmount := reminder / uint64(len(receiveAmounts)) | |
73 | + if averageAmount == 0 { | |
74 | + averageAmount = 1 | |
75 | + } | |
76 | + | |
77 | + for j := 0; j < len(receiveAmounts) && reminder > 0; j++ { | |
78 | + refundAmount := averageAmount | |
79 | + if j == len(receiveAmounts)-1 { | |
80 | + refundAmount = reminder | |
81 | + } | |
82 | + refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount}) | |
83 | + reminder -= averageAmount | |
84 | + } | |
85 | + } | |
86 | + return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees} | |
87 | +} | |
88 | + | |
89 | +// Validate verify that the fee charged for a matching transaction is correct | |
90 | +func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error { | |
91 | + for _, receiveAmount := range receiveAmounts { | |
92 | + feeAmount := feeAmounts[*receiveAmount.AssetId] | |
93 | + maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount) | |
94 | + minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount) | |
95 | + if feeAmount < minFeeAmount || feeAmount > maxFeeAmount { | |
96 | + return ErrAmountOfFeeOutOfRange | |
97 | + } | |
98 | + } | |
99 | + return nil | |
100 | +} | |
101 | + | |
102 | +func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 { | |
103 | + return uint64(math.Ceil(float64(amount) / 1000)) | |
104 | +} | |
105 | + | |
106 | +func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 { | |
107 | + return uint64(math.Ceil(float64(amount) * 0.05)) | |
108 | +} |
@@ -10,11 +10,11 @@ import ( | ||
10 | 10 | ) |
11 | 11 | |
12 | 12 | var ( |
13 | - BTC = bc.NewAssetID([32]byte{1}) | |
14 | - ETH = bc.NewAssetID([32]byte{2}) | |
15 | - EOS = bc.NewAssetID([32]byte{3}) | |
16 | - ETC = bc.NewAssetID([32]byte{4}) | |
17 | - NodeProgram = []byte{0x58} | |
13 | + BTC = bc.NewAssetID([32]byte{1}) | |
14 | + ETH = bc.NewAssetID([32]byte{2}) | |
15 | + EOS = bc.NewAssetID([32]byte{3}) | |
16 | + ETC = bc.NewAssetID([32]byte{4}) | |
17 | + RewardProgram = []byte{0x58} | |
18 | 18 | |
19 | 19 | Btc2EthOrders = []*common.Order{ |
20 | 20 | { |
@@ -104,6 +104,18 @@ var ( | ||
104 | 104 | ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255"), 1, 54.0), |
105 | 105 | }, |
106 | 106 | }, |
107 | + { | |
108 | + FromAssetID: Ð, | |
109 | + ToAssetID: &BTC, | |
110 | + RatioNumerator: 1, | |
111 | + RatioDenominator: 150, | |
112 | + Utxo: &common.MovUtxo{ | |
113 | + SourceID: hashPtr(testutil.MustDecodeHash("82752cda63c877a8529d7a7461da6096673e45b3e0b019ce44aa18687ad20445")), | |
114 | + SourcePos: 0, | |
115 | + Amount: 600, | |
116 | + ControlProgram: MustCreateP2WMCProgram(BTC, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256"), 1, 150.0), | |
117 | + }, | |
118 | + }, | |
107 | 119 | } |
108 | 120 | |
109 | 121 | Eos2EtcOrders = []*common.Order{ |
@@ -253,10 +265,13 @@ var ( | ||
253 | 265 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, Eth2BtcOrders[1].Utxo.ControlProgram), |
254 | 266 | }, |
255 | 267 | Outputs: []*types.TxOutput{ |
256 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
268 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
257 | 269 | // re-order |
258 | 270 | types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram), |
259 | - types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")), | |
271 | + types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")), | |
272 | + // fee | |
273 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram), | |
274 | + types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram), | |
260 | 275 | }, |
261 | 276 | }), |
262 | 277 |
@@ -267,9 +282,11 @@ var ( | ||
267 | 282 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[0].Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.SourcePos, Eth2BtcOrders[0].Utxo.ControlProgram), |
268 | 283 | }, |
269 | 284 | Outputs: []*types.TxOutput{ |
270 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
271 | - types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
272 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, NodeProgram), | |
285 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
286 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
287 | + // fee | |
288 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 11, RewardProgram), | |
289 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram), | |
273 | 290 | }, |
274 | 291 | }), |
275 | 292 |
@@ -280,15 +297,16 @@ var ( | ||
280 | 297 | types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram), |
281 | 298 | }, |
282 | 299 | Outputs: []*types.TxOutput{ |
283 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
284 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
300 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
301 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
285 | 302 | // re-order |
286 | 303 | types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram), |
287 | 304 | // fee |
288 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 27, NodeProgram), | |
305 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 25, RewardProgram), | |
306 | + types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 1, RewardProgram), | |
289 | 307 | // refund |
290 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 6, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
291 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
308 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
309 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
292 | 310 | }, |
293 | 311 | }), |
294 | 312 | types.NewTx(types.TxData{ |
@@ -297,10 +315,13 @@ var ( | ||
297 | 315 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, testutil.MustDecodeHash("39bdb7058a0c31fb740af8e3c382bf608efff1b041cd4dd461332722ad24552a"), *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram), |
298 | 316 | }, |
299 | 317 | Outputs: []*types.TxOutput{ |
300 | - types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 270, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")), | |
318 | + types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 269, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")), | |
301 | 319 | // re-order |
302 | 320 | types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, 15, Btc2EthOrders[1].Utxo.ControlProgram), |
303 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 5, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
321 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 4, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
322 | + // fee | |
323 | + types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 1, RewardProgram), | |
324 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram), | |
304 | 325 | }, |
305 | 326 | }), |
306 | 327 |
@@ -311,10 +332,13 @@ var ( | ||
311 | 332 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[1], 0).Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, 0, Eth2BtcOrders[1].Utxo.ControlProgram), |
312 | 333 | }, |
313 | 334 | Outputs: []*types.TxOutput{ |
314 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
335 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
315 | 336 | // re-order |
316 | 337 | types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram), |
317 | - types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")), | |
338 | + types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")), | |
339 | + // fee | |
340 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram), | |
341 | + types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram), | |
318 | 342 | }, |
319 | 343 | }), |
320 | 344 |
@@ -325,8 +349,11 @@ var ( | ||
325 | 349 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Etc2EosMakerTxs[0], 0).Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram), |
326 | 350 | }, |
327 | 351 | Outputs: []*types.TxOutput{ |
328 | - types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
329 | - types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")), | |
352 | + types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
353 | + types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")), | |
354 | + // fee | |
355 | + types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram), | |
356 | + types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram), | |
330 | 357 | }, |
331 | 358 | }), |
332 | 359 |
@@ -338,9 +365,13 @@ var ( | ||
338 | 365 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eos2BtcOrders[0].Utxo.SourceID, *Eos2BtcOrders[0].FromAssetID, Eos2BtcOrders[0].Utxo.Amount, Eos2BtcOrders[0].Utxo.SourcePos, Eos2BtcOrders[0].Utxo.ControlProgram), |
339 | 366 | }, |
340 | 367 | Outputs: []*types.TxOutput{ |
341 | - types.NewIntraChainOutput(ETH, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
342 | - types.NewIntraChainOutput(EOS, 1000, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")), | |
343 | - types.NewIntraChainOutput(BTC, 10, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")), | |
368 | + types.NewIntraChainOutput(ETH, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
369 | + types.NewIntraChainOutput(EOS, 999, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")), | |
370 | + types.NewIntraChainOutput(BTC, 9, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")), | |
371 | + // fee | |
372 | + types.NewIntraChainOutput(ETH, 1, RewardProgram), | |
373 | + types.NewIntraChainOutput(EOS, 1, RewardProgram), | |
374 | + types.NewIntraChainOutput(BTC, 1, RewardProgram), | |
344 | 375 | }, |
345 | 376 | }), |
346 | 377 |
@@ -351,10 +382,13 @@ var ( | ||
351 | 382 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram), |
352 | 383 | }, |
353 | 384 | Outputs: []*types.TxOutput{ |
354 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
355 | - types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
385 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
386 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
356 | 387 | // re-order |
357 | 388 | types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, 404, Eth2BtcOrders[0].Utxo.ControlProgram), |
389 | + // fee | |
390 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram), | |
391 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram), | |
358 | 392 | }, |
359 | 393 | }), |
360 | 394 |
@@ -365,12 +399,13 @@ var ( | ||
365 | 399 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram), |
366 | 400 | }, |
367 | 401 | Outputs: []*types.TxOutput{ |
368 | - types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 810, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")), | |
402 | + types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 809, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")), | |
369 | 403 | // re-order |
370 | 404 | types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, Btc2EthOrders[3].Utxo.ControlProgram), |
371 | - types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 15, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
405 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 14, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
372 | 406 | // fee |
373 | - types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, NodeProgram), | |
407 | + types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 2, RewardProgram), | |
408 | + types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram), | |
374 | 409 | }, |
375 | 410 | }), |
376 | 411 |
@@ -381,8 +416,11 @@ var ( | ||
381 | 416 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Etc2EosOrders[0].Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram), |
382 | 417 | }, |
383 | 418 | Outputs: []*types.TxOutput{ |
384 | - types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
385 | - types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")), | |
419 | + types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
420 | + types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")), | |
421 | + // fee | |
422 | + types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram), | |
423 | + types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram), | |
386 | 424 | }, |
387 | 425 | }), |
388 | 426 |
@@ -393,9 +431,32 @@ var ( | ||
393 | 431 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram), |
394 | 432 | }, |
395 | 433 | Outputs: []*types.TxOutput{ |
396 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
397 | - types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
398 | - types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, NodeProgram), | |
434 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
435 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")), | |
436 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, RewardProgram), | |
437 | + // fee | |
438 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram), | |
439 | + types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram), | |
440 | + }, | |
441 | + }), | |
442 | + | |
443 | + // full matched transaction from Btc2EthOrders[0] Eth2BtcOrders[3] | |
444 | + types.NewTx(types.TxData{ | |
445 | + Inputs: []*types.TxInput{ | |
446 | + types.NewSpendInput([][]byte{vm.Int64Bytes(0), vm.Int64Bytes(1)}, *Btc2EthOrders[0].Utxo.SourceID, *Btc2EthOrders[0].FromAssetID, Btc2EthOrders[0].Utxo.Amount, Btc2EthOrders[0].Utxo.SourcePos, Btc2EthOrders[0].Utxo.ControlProgram), | |
447 | + types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[3].Utxo.SourceID, *Eth2BtcOrders[3].FromAssetID, Eth2BtcOrders[3].Utxo.Amount, Eth2BtcOrders[3].Utxo.SourcePos, Eth2BtcOrders[3].Utxo.ControlProgram), | |
448 | + }, | |
449 | + Outputs: []*types.TxOutput{ | |
450 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
451 | + types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")), | |
452 | + // fee | |
453 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 25, RewardProgram), | |
454 | + types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 1, RewardProgram), | |
455 | + // refund | |
456 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
457 | + types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
458 | + types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")), | |
459 | + types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")), | |
399 | 460 | }, |
400 | 461 | }), |
401 | 462 | } |
@@ -1,10 +1,13 @@ | ||
1 | 1 | package mov |
2 | 2 | |
3 | 3 | import ( |
4 | + "encoding/hex" | |
5 | + | |
4 | 6 | "github.com/bytom/vapor/application/mov/common" |
5 | 7 | "github.com/bytom/vapor/application/mov/contract" |
6 | 8 | "github.com/bytom/vapor/application/mov/database" |
7 | 9 | "github.com/bytom/vapor/application/mov/match" |
10 | + "github.com/bytom/vapor/consensus" | |
8 | 11 | "github.com/bytom/vapor/consensus/segwit" |
9 | 12 | dbm "github.com/bytom/vapor/database/leveldb" |
10 | 13 | "github.com/bytom/vapor/errors" |
@@ -12,50 +15,49 @@ import ( | ||
12 | 15 | "github.com/bytom/vapor/protocol/bc/types" |
13 | 16 | ) |
14 | 17 | |
15 | -const maxFeeRate = 0.05 | |
16 | - | |
17 | 18 | var ( |
18 | - errInvalidTradePairs = errors.New("The trade pairs in the tx input is invalid") | |
19 | - errStatusFailMustFalse = errors.New("status fail of transaction does not allow to be true") | |
20 | - errInputProgramMustP2WMCScript = errors.New("input program of trade tx must p2wmc script") | |
21 | - errExistCancelOrderInMatchedTx = errors.New("can't exist cancel order in the matched transaction") | |
22 | - errExistTradeInCancelOrderTx = errors.New("can't exist trade in the cancel order transaction") | |
23 | - errAmountOfFeeGreaterThanMaximum = errors.New("amount of fee greater than max fee amount") | |
24 | - errAssetIDMustUniqueInMatchedTx = errors.New("asset id must unique in matched transaction") | |
25 | - errRatioOfTradeLessThanZero = errors.New("ratio arguments must greater than zero") | |
26 | - errSpendOutputIDIsIncorrect = errors.New("spend output id of matched tx is not equals to actual matched tx") | |
27 | - errRequestAmountMath = errors.New("request amount of order less than one or big than max of int64") | |
28 | - errNotMatchedOrder = errors.New("order in matched tx is not matched") | |
19 | + errInvalidTradePairs = errors.New("The trade pairs in the tx input is invalid") | |
20 | + errStatusFailMustFalse = errors.New("status fail of transaction does not allow to be true") | |
21 | + errInputProgramMustP2WMCScript = errors.New("input program of trade tx must p2wmc script") | |
22 | + errExistCancelOrderInMatchedTx = errors.New("can't exist cancel order in the matched transaction") | |
23 | + errExistTradeInCancelOrderTx = errors.New("can't exist trade in the cancel order transaction") | |
24 | + errAssetIDMustUniqueInMatchedTx = errors.New("asset id must unique in matched transaction") | |
25 | + errRatioOfTradeLessThanZero = errors.New("ratio arguments must greater than zero") | |
26 | + errSpendOutputIDIsIncorrect = errors.New("spend output id of matched tx is not equals to actual matched tx") | |
27 | + errRequestAmountMath = errors.New("request amount of order less than one or big than max of int64") | |
28 | + errNotMatchedOrder = errors.New("order in matched tx is not matched") | |
29 | + errNotConfiguredRewardProgram = errors.New("reward program is not configured properly") | |
30 | + errRewardProgramIsWrong = errors.New("the reward program is not correct") | |
29 | 31 | ) |
30 | 32 | |
31 | -// MovCore represent the core logic of the match module, which include generate match transactions before packing the block, | |
33 | +// Core represent the core logic of the match module, which include generate match transactions before packing the block, | |
32 | 34 | // verify the match transaction in block is correct, and update the order table according to the transaction. |
33 | -type MovCore struct { | |
35 | +type Core struct { | |
34 | 36 | movStore database.MovStore |
35 | 37 | startBlockHeight uint64 |
36 | 38 | } |
37 | 39 | |
38 | -// NewMovCore return a instance of MovCore by path of mov db | |
39 | -func NewMovCore(dbBackend, dbDir string, startBlockHeight uint64) *MovCore { | |
40 | +// NewCore return a instance of Core by path of mov db | |
41 | +func NewCore(dbBackend, dbDir string, startBlockHeight uint64) *Core { | |
40 | 42 | movDB := dbm.NewDB("mov", dbBackend, dbDir) |
41 | - return &MovCore{movStore: database.NewLevelDBMovStore(movDB), startBlockHeight: startBlockHeight} | |
43 | + return &Core{movStore: database.NewLevelDBMovStore(movDB), startBlockHeight: startBlockHeight} | |
44 | +} | |
45 | + | |
46 | +// NewCoreWithDB return a instance of Core by movStore | |
47 | +func NewCoreWithDB(store *database.LevelDBMovStore, startBlockHeight uint64) *Core { | |
48 | + return &Core{movStore: store, startBlockHeight: startBlockHeight} | |
42 | 49 | } |
43 | 50 | |
44 | 51 | // ApplyBlock parse pending order and cancel from the the transactions of block |
45 | 52 | // and add pending order to the dex db, remove cancel order from dex db. |
46 | -func (m *MovCore) ApplyBlock(block *types.Block) error { | |
53 | +func (m *Core) ApplyBlock(block *types.Block) error { | |
47 | 54 | if block.Height < m.startBlockHeight { |
48 | 55 | return nil |
49 | 56 | } |
50 | 57 | |
51 | 58 | if block.Height == m.startBlockHeight { |
52 | 59 | blockHash := block.Hash() |
53 | - if err := m.movStore.InitDBState(block.Height, &blockHash); err != nil { | |
54 | - return err | |
55 | - } | |
56 | - | |
57 | - // the next block can send orders | |
58 | - return nil | |
60 | + return m.movStore.InitDBState(block.Height, &blockHash) | |
59 | 61 | } |
60 | 62 | |
61 | 63 | if err := m.validateMatchedTxSequence(block.Transactions); err != nil { |
@@ -71,7 +73,7 @@ func (m *MovCore) ApplyBlock(block *types.Block) error { | ||
71 | 73 | } |
72 | 74 | |
73 | 75 | // BeforeProposalBlock return all transactions than can be matched, and the number of transactions cannot exceed the given capacity. |
74 | -func (m *MovCore) BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) { | |
76 | +func (m *Core) BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) { | |
75 | 77 | if blockHeight <= m.startBlockHeight { |
76 | 78 | return nil, nil |
77 | 79 | } |
@@ -81,14 +83,20 @@ func (m *MovCore) BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, block | ||
81 | 83 | return nil, err |
82 | 84 | } |
83 | 85 | |
84 | - matchEngine := match.NewEngine(orderBook, maxFeeRate, nodeProgram) | |
86 | + program, _ := getRewardProgram(blockHeight) | |
87 | + rewardProgram, err := hex.DecodeString(program) | |
88 | + if err != nil { | |
89 | + return nil, errNotConfiguredRewardProgram | |
90 | + } | |
91 | + | |
92 | + matchEngine := match.NewEngine(orderBook, match.NewDefaultFeeStrategy(), rewardProgram) | |
85 | 93 | tradePairIterator := database.NewTradePairIterator(m.movStore) |
86 | 94 | matchCollector := newMatchTxCollector(matchEngine, tradePairIterator, gasLeft, isTimeout) |
87 | 95 | return matchCollector.result() |
88 | 96 | } |
89 | 97 | |
90 | 98 | // ChainStatus return the current block height and block hash in dex core |
91 | -func (m *MovCore) ChainStatus() (uint64, *bc.Hash, error) { | |
99 | +func (m *Core) ChainStatus() (uint64, *bc.Hash, error) { | |
92 | 100 | state, err := m.movStore.GetMovDatabaseState() |
93 | 101 | if err != nil { |
94 | 102 | return 0, nil, err |
@@ -99,7 +107,7 @@ func (m *MovCore) ChainStatus() (uint64, *bc.Hash, error) { | ||
99 | 107 | |
100 | 108 | // DetachBlock parse pending order and cancel from the the transactions of block |
101 | 109 | // and add cancel order to the dex db, remove pending order from dex db. |
102 | -func (m *MovCore) DetachBlock(block *types.Block) error { | |
110 | +func (m *Core) DetachBlock(block *types.Block) error { | |
103 | 111 | if block.Height < m.startBlockHeight { |
104 | 112 | return nil |
105 | 113 | } |
@@ -113,7 +121,7 @@ func (m *MovCore) DetachBlock(block *types.Block) error { | ||
113 | 121 | } |
114 | 122 | |
115 | 123 | // IsDust block the transaction that are not generated by the match engine |
116 | -func (m *MovCore) IsDust(tx *types.Tx) bool { | |
124 | +func (m *Core) IsDust(tx *types.Tx) bool { | |
117 | 125 | for _, input := range tx.Inputs { |
118 | 126 | if segwit.IsP2WMCScript(input.ControlProgram()) && !contract.IsCancelClauseSelector(input) { |
119 | 127 | return true |
@@ -123,25 +131,20 @@ func (m *MovCore) IsDust(tx *types.Tx) bool { | ||
123 | 131 | } |
124 | 132 | |
125 | 133 | // Name return the name of current module |
126 | -func (m *MovCore) Name() string { | |
134 | +func (m *Core) Name() string { | |
127 | 135 | return "MOV" |
128 | 136 | } |
129 | 137 | |
130 | 138 | // StartHeight return the start block height of current module |
131 | -func (m *MovCore) StartHeight() uint64 { | |
139 | +func (m *Core) StartHeight() uint64 { | |
132 | 140 | return m.startBlockHeight |
133 | 141 | } |
134 | 142 | |
135 | 143 | // ValidateBlock no need to verify the block header, because the first module has been verified. |
136 | 144 | // just need to verify the transactions in the block. |
137 | -func (m *MovCore) ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error { | |
138 | - return m.ValidateTxs(block.Transactions, verifyResults) | |
139 | -} | |
140 | - | |
141 | -// ValidateTxs validate the trade transaction. | |
142 | -func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error { | |
143 | - for i, tx := range txs { | |
144 | - if err := m.ValidateTx(tx, verifyResults[i]); err != nil { | |
145 | +func (m *Core) ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error { | |
146 | + for i, tx := range block.Transactions { | |
147 | + if err := m.ValidateTx(tx, verifyResults[i], block.Height); err != nil { | |
145 | 148 | return err |
146 | 149 | } |
147 | 150 | } |
@@ -149,9 +152,17 @@ func (m *MovCore) ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResul | ||
149 | 152 | } |
150 | 153 | |
151 | 154 | // ValidateTx validate one transaction. |
152 | -func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error { | |
155 | +func (m *Core) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error { | |
156 | + if blockHeight <= m.startBlockHeight { | |
157 | + return nil | |
158 | + } | |
159 | + | |
160 | + if verifyResult.StatusFail { | |
161 | + return errStatusFailMustFalse | |
162 | + } | |
163 | + | |
153 | 164 | if common.IsMatchedTx(tx) { |
154 | - if err := validateMatchedTx(tx, verifyResult); err != nil { | |
165 | + if err := validateMatchedTx(tx, verifyResult, blockHeight); err != nil { | |
155 | 166 | return err |
156 | 167 | } |
157 | 168 | } else if common.IsCancelOrderTx(tx) { |
@@ -164,9 +175,6 @@ func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) erro | ||
164 | 175 | if !segwit.IsP2WMCScript(output.ControlProgram()) { |
165 | 176 | continue |
166 | 177 | } |
167 | - if verifyResult.StatusFail { | |
168 | - return errStatusFailMustFalse | |
169 | - } | |
170 | 178 | |
171 | 179 | if err := validateMagneticContractArgs(output.AssetAmount(), output.ControlProgram()); err != nil { |
172 | 180 | return err |
@@ -175,11 +183,44 @@ func (m *MovCore) ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) erro | ||
175 | 183 | return nil |
176 | 184 | } |
177 | 185 | |
178 | -func validateCancelOrderTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error { | |
179 | - if verifyResult.StatusFail { | |
180 | - return errStatusFailMustFalse | |
186 | +// matchedTxFee is object to record the mov tx's fee information | |
187 | +type matchedTxFee struct { | |
188 | + rewardProgram []byte | |
189 | + amount uint64 | |
190 | +} | |
191 | + | |
192 | +// calcFeeAmount return the amount of fee in the matching transaction | |
193 | +func calcFeeAmount(matchedTx *types.Tx) (map[bc.AssetID]*matchedTxFee, error) { | |
194 | + assetFeeMap := make(map[bc.AssetID]*matchedTxFee) | |
195 | + dealProgMaps := make(map[string]bool) | |
196 | + | |
197 | + for _, input := range matchedTx.Inputs { | |
198 | + assetFeeMap[input.AssetID()] = &matchedTxFee{amount: input.AssetAmount().Amount} | |
199 | + contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram()) | |
200 | + if err != nil { | |
201 | + return nil, err | |
202 | + } | |
203 | + | |
204 | + dealProgMaps[hex.EncodeToString(contractArgs.SellerProgram)] = true | |
181 | 205 | } |
182 | 206 | |
207 | + for _, output := range matchedTx.Outputs { | |
208 | + assetAmount := output.AssetAmount() | |
209 | + if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) { | |
210 | + assetFeeMap[*assetAmount.AssetId].amount -= assetAmount.Amount | |
211 | + if assetFeeMap[*assetAmount.AssetId].amount <= 0 { | |
212 | + delete(assetFeeMap, *assetAmount.AssetId) | |
213 | + } | |
214 | + } else if assetFeeMap[*assetAmount.AssetId].rewardProgram == nil { | |
215 | + assetFeeMap[*assetAmount.AssetId].rewardProgram = output.ControlProgram() | |
216 | + } else { | |
217 | + return nil, errors.Wrap(errRewardProgramIsWrong, "double reward program") | |
218 | + } | |
219 | + } | |
220 | + return assetFeeMap, nil | |
221 | +} | |
222 | + | |
223 | +func validateCancelOrderTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error { | |
183 | 224 | for _, input := range tx.Inputs { |
184 | 225 | if !segwit.IsP2WMCScript(input.ControlProgram()) { |
185 | 226 | return errInputProgramMustP2WMCScript |
@@ -206,17 +247,13 @@ func validateMagneticContractArgs(fromAssetAmount bc.AssetAmount, program []byte | ||
206 | 247 | return errRatioOfTradeLessThanZero |
207 | 248 | } |
208 | 249 | |
209 | - if match.CalcRequestAmount(fromAssetAmount.Amount, contractArgs) < 1 { | |
250 | + if match.CalcRequestAmount(fromAssetAmount.Amount, contractArgs.RatioNumerator, contractArgs.RatioDenominator) < 1 { | |
210 | 251 | return errRequestAmountMath |
211 | 252 | } |
212 | 253 | return nil |
213 | 254 | } |
214 | 255 | |
215 | -func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error { | |
216 | - if verifyResult.StatusFail { | |
217 | - return errStatusFailMustFalse | |
218 | - } | |
219 | - | |
256 | +func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error { | |
220 | 257 | fromAssetIDMap := make(map[string]bool) |
221 | 258 | toAssetIDMap := make(map[string]bool) |
222 | 259 | for i, input := range tx.Inputs { |
@@ -241,24 +278,37 @@ func validateMatchedTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error { | ||
241 | 278 | return errAssetIDMustUniqueInMatchedTx |
242 | 279 | } |
243 | 280 | |
244 | - return validateMatchedTxFeeAmount(tx) | |
281 | + return validateMatchedTxFee(tx, blockHeight) | |
245 | 282 | } |
246 | 283 | |
247 | -func validateMatchedTxFeeAmount(tx *types.Tx) error { | |
248 | - txFee, err := match.CalcMatchedTxFee(&tx.TxData, maxFeeRate) | |
284 | +func validateMatchedTxFee(tx *types.Tx, blockHeight uint64) error { | |
285 | + matchedTxFees, err := calcFeeAmount(tx) | |
249 | 286 | if err != nil { |
250 | 287 | return err |
251 | 288 | } |
252 | 289 | |
253 | - for _, amount := range txFee { | |
254 | - if amount.FeeAmount > amount.MaxFeeAmount { | |
255 | - return errAmountOfFeeGreaterThanMaximum | |
290 | + for _, fee := range matchedTxFees { | |
291 | + if err := validateRewardProgram(blockHeight, hex.EncodeToString(fee.rewardProgram)); err != nil { | |
292 | + return err | |
256 | 293 | } |
257 | 294 | } |
258 | - return nil | |
295 | + | |
296 | + orders, err := getDeleteOrdersFromTx(tx) | |
297 | + if err != nil { | |
298 | + return err | |
299 | + } | |
300 | + | |
301 | + receivedAmount, _ := match.CalcReceivedAmount(orders) | |
302 | + feeAmounts := make(map[bc.AssetID]uint64) | |
303 | + for assetID, fee := range matchedTxFees { | |
304 | + feeAmounts[assetID] = fee.amount | |
305 | + } | |
306 | + | |
307 | + feeStrategy := match.NewDefaultFeeStrategy() | |
308 | + return feeStrategy.Validate(receivedAmount, feeAmounts) | |
259 | 309 | } |
260 | 310 | |
261 | -func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error { | |
311 | +func (m *Core) validateMatchedTxSequence(txs []*types.Tx) error { | |
262 | 312 | orderBook := match.NewOrderBook(m.movStore, nil, nil) |
263 | 313 | for _, tx := range txs { |
264 | 314 | if common.IsMatchedTx(tx) { |
@@ -348,15 +398,8 @@ func decodeTxsOrders(txs []*types.Tx) ([]*common.Order, []*common.Order, error) | ||
348 | 398 | } |
349 | 399 | |
350 | 400 | func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook, error) { |
351 | - var nonMatchedTxs []*types.Tx | |
352 | - for _, tx := range txs { | |
353 | - if !common.IsMatchedTx(tx) { | |
354 | - nonMatchedTxs = append(nonMatchedTxs, tx) | |
355 | - } | |
356 | - } | |
357 | - | |
358 | 401 | var arrivalAddOrders, arrivalDelOrders []*common.Order |
359 | - for _, tx := range nonMatchedTxs { | |
402 | + for _, tx := range txs { | |
360 | 403 | addOrders, err := getAddOrdersFromTx(tx) |
361 | 404 | if err != nil { |
362 | 405 | return nil, err |
@@ -381,6 +424,10 @@ func getAddOrdersFromTx(tx *types.Tx) ([]*common.Order, error) { | ||
381 | 424 | continue |
382 | 425 | } |
383 | 426 | |
427 | + if output.AssetAmount().Amount == 0 { | |
428 | + continue | |
429 | + } | |
430 | + | |
384 | 431 | order, err := common.NewOrderFromOutput(tx, i) |
385 | 432 | if err != nil { |
386 | 433 | return nil, err |
@@ -436,3 +483,31 @@ func mergeOrders(addOrderMap, deleteOrderMap map[string]*common.Order) ([]*commo | ||
436 | 483 | } |
437 | 484 | return addOrders, deleteOrders |
438 | 485 | } |
486 | + | |
487 | +// getRewardProgram return the reward program by specified block height | |
488 | +// if no reward program configured, then will return empty string | |
489 | +// if reward program of 0-100 height is configured, but the specified height is 200, then will return 0-100's reward program | |
490 | +// the second return value represent whether to find exactly | |
491 | +func getRewardProgram(height uint64) (string, bool) { | |
492 | + rewardPrograms := consensus.ActiveNetParams.MovRewardPrograms | |
493 | + if len(rewardPrograms) == 0 { | |
494 | + return "", false | |
495 | + } | |
496 | + | |
497 | + var program string | |
498 | + for _, rewardProgram := range rewardPrograms { | |
499 | + program = rewardProgram.Program | |
500 | + if height >= rewardProgram.BeginBlock && height <= rewardProgram.EndBlock { | |
501 | + return program, true | |
502 | + } | |
503 | + } | |
504 | + return program, false | |
505 | +} | |
506 | + | |
507 | +func validateRewardProgram(height uint64, program string) error { | |
508 | + rewardProgram, exact := getRewardProgram(height) | |
509 | + if exact && rewardProgram != program { | |
510 | + return errRewardProgramIsWrong | |
511 | + } | |
512 | + return nil | |
513 | +} |
@@ -1,12 +1,14 @@ | ||
1 | 1 | package mov |
2 | 2 | |
3 | 3 | import ( |
4 | + "encoding/hex" | |
4 | 5 | "math" |
5 | 6 | "os" |
6 | 7 | "testing" |
7 | 8 | |
8 | 9 | "github.com/bytom/vapor/application/mov/common" |
9 | 10 | "github.com/bytom/vapor/application/mov/database" |
11 | + "github.com/bytom/vapor/application/mov/match" | |
10 | 12 | "github.com/bytom/vapor/application/mov/mock" |
11 | 13 | "github.com/bytom/vapor/consensus" |
12 | 14 | dbm "github.com/bytom/vapor/database/leveldb" |
@@ -262,7 +264,7 @@ func TestApplyBlock(t *testing.T) { | ||
262 | 264 | t.Fatal(err) |
263 | 265 | } |
264 | 266 | |
265 | - movCore := &MovCore{movStore: store} | |
267 | + movCore := &Core{movStore: store} | |
266 | 268 | if err := c.blockFunc(movCore, c.block); err != c.wantError { |
267 | 269 | t.Errorf("#%d(%s):apply block want error(%v), got error(%v)", i, c.desc, c.wantError, err) |
268 | 270 | } |
@@ -287,6 +289,14 @@ func TestApplyBlock(t *testing.T) { | ||
287 | 289 | } |
288 | 290 | |
289 | 291 | func TestValidateBlock(t *testing.T) { |
292 | + consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{ | |
293 | + { | |
294 | + BeginBlock: 0, | |
295 | + EndBlock: 100, | |
296 | + Program: hex.EncodeToString(mock.RewardProgram), | |
297 | + }, | |
298 | + } | |
299 | + | |
290 | 300 | cases := []struct { |
291 | 301 | desc string |
292 | 302 | block *types.Block |
@@ -362,9 +372,11 @@ func TestValidateBlock(t *testing.T) { | ||
362 | 372 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram), |
363 | 373 | }, |
364 | 374 | Outputs: []*types.TxOutput{ |
365 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")), | |
366 | - types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")), | |
367 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}), | |
375 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")), | |
376 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")), | |
377 | + // fee | |
378 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram), | |
379 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram), | |
368 | 380 | }, |
369 | 381 | }), |
370 | 382 | }, |
@@ -383,9 +395,10 @@ func TestValidateBlock(t *testing.T) { | ||
383 | 395 | types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}), |
384 | 396 | }, |
385 | 397 | Outputs: []*types.TxOutput{ |
386 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")), | |
387 | - types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")), | |
388 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}), | |
398 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")), | |
399 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")), | |
400 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram), | |
401 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram), | |
389 | 402 | types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}), |
390 | 403 | }, |
391 | 404 | }), |
@@ -405,10 +418,11 @@ func TestValidateBlock(t *testing.T) { | ||
405 | 418 | types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram), |
406 | 419 | }, |
407 | 420 | Outputs: []*types.TxOutput{ |
408 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")), | |
409 | - types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")), | |
410 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}), | |
411 | - types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}), | |
421 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")), | |
422 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")), | |
423 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram), | |
424 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram), | |
425 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")), | |
412 | 426 | }, |
413 | 427 | }), |
414 | 428 | }, |
@@ -427,7 +441,7 @@ func TestValidateBlock(t *testing.T) { | ||
427 | 441 | }, |
428 | 442 | Outputs: []*types.TxOutput{ |
429 | 443 | types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")), |
430 | - types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}), | |
444 | + types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram), | |
431 | 445 | }, |
432 | 446 | }), |
433 | 447 | }, |
@@ -445,18 +459,19 @@ func TestValidateBlock(t *testing.T) { | ||
445 | 459 | types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *mock.Eth2BtcOrders[2].Utxo.SourceID, *mock.Eth2BtcOrders[2].FromAssetID, mock.Eth2BtcOrders[2].Utxo.Amount, mock.Eth2BtcOrders[2].Utxo.SourcePos, mock.Eth2BtcOrders[2].Utxo.ControlProgram), |
446 | 460 | }, |
447 | 461 | Outputs: []*types.TxOutput{ |
448 | - types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")), | |
449 | - types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("55")), | |
462 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")), | |
463 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")), | |
450 | 464 | // re-order |
451 | 465 | types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram), |
452 | 466 | // fee |
453 | - types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, []byte{0x59}), | |
467 | + types.NewIntraChainOutput(*mock.Btc2EthOrders[2].ToAssetID, 41, mock.RewardProgram), | |
468 | + types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 1, mock.RewardProgram), | |
454 | 469 | }, |
455 | 470 | }), |
456 | 471 | }, |
457 | 472 | }, |
458 | 473 | verifyResults: []*bc.TxVerifyResult{{StatusFail: false}}, |
459 | - wantError: errAmountOfFeeGreaterThanMaximum, | |
474 | + wantError: match.ErrAmountOfFeeOutOfRange, | |
460 | 475 | }, |
461 | 476 | { |
462 | 477 | desc: "ratio numerator is zero", |
@@ -500,14 +515,71 @@ func TestValidateBlock(t *testing.T) { | ||
500 | 515 | } |
501 | 516 | |
502 | 517 | for i, c := range cases { |
503 | - movCore := &MovCore{} | |
518 | + movCore := &Core{} | |
519 | + c.block.Height = 3456786543 | |
504 | 520 | if err := movCore.ValidateBlock(c.block, c.verifyResults); err != c.wantError { |
505 | 521 | t.Errorf("#%d(%s):validate block want error(%v), got error(%v)", i, c.desc, c.wantError, err) |
506 | 522 | } |
507 | 523 | } |
508 | 524 | } |
509 | 525 | |
526 | +func TestCalcMatchedTxFee(t *testing.T) { | |
527 | + cases := []struct { | |
528 | + desc string | |
529 | + tx types.TxData | |
530 | + maxFeeRate float64 | |
531 | + wantMatchedTxFee map[bc.AssetID]*matchedTxFee | |
532 | + }{ | |
533 | + { | |
534 | + desc: "fee less than max fee", | |
535 | + maxFeeRate: 0.05, | |
536 | + wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{ | |
537 | + mock.ETH: {amount: 11, rewardProgram: mock.RewardProgram}, | |
538 | + mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram}, | |
539 | + }, | |
540 | + tx: mock.MatchedTxs[1].TxData, | |
541 | + }, | |
542 | + { | |
543 | + desc: "fee refund in tx", | |
544 | + maxFeeRate: 0.05, | |
545 | + wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{ | |
546 | + mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram}, | |
547 | + mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram}, | |
548 | + }, | |
549 | + tx: mock.MatchedTxs[2].TxData, | |
550 | + }, | |
551 | + { | |
552 | + desc: "no price diff", | |
553 | + maxFeeRate: 0.05, | |
554 | + wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{ | |
555 | + mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram}, | |
556 | + mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram}, | |
557 | + }, | |
558 | + tx: mock.MatchedTxs[0].TxData, | |
559 | + }, | |
560 | + } | |
561 | + | |
562 | + for i, c := range cases { | |
563 | + gotMatchedTxFee, err := calcFeeAmount(types.NewTx(c.tx)) | |
564 | + if err != nil { | |
565 | + t.Fatal(err) | |
566 | + } | |
567 | + | |
568 | + if !testutil.DeepEqual(gotMatchedTxFee, c.wantMatchedTxFee) { | |
569 | + t.Errorf("#%d(%s):fail to caculate matched tx fee, got (%v), want (%v)", i, c.desc, gotMatchedTxFee, c.wantMatchedTxFee) | |
570 | + } | |
571 | + } | |
572 | +} | |
573 | + | |
510 | 574 | func TestBeforeProposalBlock(t *testing.T) { |
575 | + consensus.ActiveNetParams.MovRewardPrograms = []consensus.MovRewardProgram{ | |
576 | + { | |
577 | + BeginBlock: 0, | |
578 | + EndBlock: 100, | |
579 | + Program: hex.EncodeToString(mock.RewardProgram), | |
580 | + }, | |
581 | + } | |
582 | + | |
511 | 583 | cases := []struct { |
512 | 584 | desc string |
513 | 585 | initOrders []*common.Order |
@@ -571,8 +643,8 @@ func TestBeforeProposalBlock(t *testing.T) { | ||
571 | 643 | t.Fatal(err) |
572 | 644 | } |
573 | 645 | |
574 | - movCore := &MovCore{movStore: store} | |
575 | - gotMatchedTxs, err := movCore.BeforeProposalBlock(nil, []byte{0x51}, 2, c.gasLeft, func() bool { return false }) | |
646 | + movCore := &Core{movStore: store} | |
647 | + gotMatchedTxs, err := movCore.BeforeProposalBlock(nil, 2, c.gasLeft, func() bool { return false }) | |
576 | 648 | if err != nil { |
577 | 649 | t.Fatal(err) |
578 | 650 | } |
@@ -724,7 +796,7 @@ func TestValidateMatchedTxSequence(t *testing.T) { | ||
724 | 796 | t.Fatal(err) |
725 | 797 | } |
726 | 798 | |
727 | - movCore := &MovCore{movStore: store} | |
799 | + movCore := &Core{movStore: store} | |
728 | 800 | if err := movCore.validateMatchedTxSequence(c.transactions); err != c.wantError { |
729 | 801 | t.Errorf("#%d(%s):wanet error(%v), got error(%v)", i, c.desc, c.wantError, err) |
730 | 802 | } |
@@ -734,13 +806,13 @@ func TestValidateMatchedTxSequence(t *testing.T) { | ||
734 | 806 | } |
735 | 807 | } |
736 | 808 | |
737 | -type testFun func(movCore *MovCore, block *types.Block) error | |
809 | +type testFun func(movCore *Core, block *types.Block) error | |
738 | 810 | |
739 | -func applyBlock(movCore *MovCore, block *types.Block) error { | |
811 | +func applyBlock(movCore *Core, block *types.Block) error { | |
740 | 812 | return movCore.ApplyBlock(block) |
741 | 813 | } |
742 | 814 | |
743 | -func detachBlock(movCore *MovCore, block *types.Block) error { | |
815 | +func detachBlock(movCore *Core, block *types.Block) error { | |
744 | 816 | return movCore.DetachBlock(block) |
745 | 817 | } |
746 | 818 |
@@ -8,6 +8,7 @@ import ( | ||
8 | 8 | "github.com/bytom/vapor/errors" |
9 | 9 | ) |
10 | 10 | |
11 | +// AddDataWitness append data to the witness array | |
11 | 12 | func (si *SigningInstruction) AddDataWitness(data chainjson.HexBytes) { |
12 | 13 | dw := DataWitness(data) |
13 | 14 | si.WitnessComponents = append(si.WitnessComponents, &dw) |
@@ -15,6 +15,7 @@ var ( | ||
15 | 15 | config = cfg.DefaultConfig() |
16 | 16 | ) |
17 | 17 | |
18 | +// RootCmd is the command for run node | |
18 | 19 | var RootCmd = &cobra.Command{ |
19 | 20 | Use: "vapord", |
20 | 21 | Short: "Multiple asset management.", |
@@ -6,6 +6,7 @@ import ( | ||
6 | 6 | "encoding/hex" |
7 | 7 | ) |
8 | 8 | |
9 | +// FromHex convert hex byte string to []byte | |
9 | 10 | func FromHex(s string) []byte { |
10 | 11 | if len(s) > 1 { |
11 | 12 | if s[0:2] == "0x" { |
@@ -19,21 +20,25 @@ func FromHex(s string) []byte { | ||
19 | 20 | return nil |
20 | 21 | } |
21 | 22 | |
23 | +// Bytes2Hex convert byte array to string | |
22 | 24 | func Bytes2Hex(d []byte) string { |
23 | 25 | return hex.EncodeToString(d) |
24 | 26 | } |
25 | 27 | |
28 | +// Hex2Bytes convert hex string to byte array | |
26 | 29 | func Hex2Bytes(str string) []byte { |
27 | 30 | h, _ := hex.DecodeString(str) |
28 | 31 | return h |
29 | 32 | } |
30 | 33 | |
34 | +// Unit64ToBytes convert uint64 to bytes | |
31 | 35 | func Unit64ToBytes(n uint64) []byte { |
32 | 36 | buf := make([]byte, 8) |
33 | 37 | binary.LittleEndian.PutUint64(buf, n) |
34 | 38 | return buf |
35 | 39 | } |
36 | 40 | |
41 | +// BytesToUnit64 convert bytes to uint64 | |
37 | 42 | func BytesToUnit64(b []byte) uint64 { |
38 | 43 | return binary.LittleEndian.Uint64(b) |
39 | 44 | } |
@@ -4,6 +4,7 @@ import ( | ||
4 | 4 | "encoding/json" |
5 | 5 | ) |
6 | 6 | |
7 | +// IsOpenFederationIssueAsset check if the asset definition satisfy ofmf asset | |
7 | 8 | func IsOpenFederationIssueAsset(rawDefinitionByte []byte) bool { |
8 | 9 | var defMap map[string]interface{} |
9 | 10 | if err := json.Unmarshal(rawDefinitionByte, &defMap); err != nil { |
@@ -20,8 +21,5 @@ func IsOpenFederationIssueAsset(rawDefinitionByte []byte) bool { | ||
20 | 21 | return false |
21 | 22 | } |
22 | 23 | |
23 | - if issueAssetAction != "cross_chain" { | |
24 | - return false | |
25 | - } | |
26 | - return true | |
24 | + return issueAssetAction == "open_federation_cross_chain" | |
27 | 25 | } |
@@ -1,23 +0,0 @@ | ||
1 | -package common | |
2 | - | |
3 | -// timeSorter implements sort.Interface to allow a slice of timestamps to | |
4 | -// be sorted. | |
5 | -type TimeSorter []uint64 | |
6 | - | |
7 | -// Len returns the number of timestamps in the slice. It is part of the | |
8 | -// sort.Interface implementation. | |
9 | -func (s TimeSorter) Len() int { | |
10 | - return len(s) | |
11 | -} | |
12 | - | |
13 | -// Swap swaps the timestamps at the passed indices. It is part of the | |
14 | -// sort.Interface implementation. | |
15 | -func (s TimeSorter) Swap(i, j int) { | |
16 | - s[i], s[j] = s[j], s[i] | |
17 | -} | |
18 | - | |
19 | -// Less returns whether the timstamp with index i should sort before the | |
20 | -// timestamp with index j. It is part of the sort.Interface implementation. | |
21 | -func (s TimeSorter) Less(i, j int) bool { | |
22 | - return s[i] < s[j] | |
23 | -} |
@@ -75,6 +75,13 @@ type ProducerSubsidy struct { | ||
75 | 75 | Subsidy uint64 |
76 | 76 | } |
77 | 77 | |
78 | +// MovRewardProgram is a reward address corresponding to the range of the specified block height when matching transactions | |
79 | +type MovRewardProgram struct { | |
80 | + BeginBlock uint64 | |
81 | + EndBlock uint64 | |
82 | + Program string | |
83 | +} | |
84 | + | |
78 | 85 | // Params store the config for different network |
79 | 86 | type Params struct { |
80 | 87 | // Name defines a human-readable identifier for the network. |
@@ -103,7 +110,12 @@ type Params struct { | ||
103 | 110 | ProducerSubsidys []ProducerSubsidy |
104 | 111 | |
105 | 112 | SoftForkPoint map[uint64]uint64 |
113 | + | |
114 | + // Mov will only start when the block height is greater than this value | |
106 | 115 | MovStartHeight uint64 |
116 | + | |
117 | + // Used to receive rewards for matching transactions | |
118 | + MovRewardPrograms []MovRewardProgram | |
107 | 119 | } |
108 | 120 | |
109 | 121 | // ActiveNetParams is the active NetParams |
@@ -145,7 +157,15 @@ var MainNetParams = Params{ | ||
145 | 157 | ProducerSubsidys: []ProducerSubsidy{ |
146 | 158 | {BeginBlock: 1, EndBlock: 63072000, Subsidy: 9512938}, |
147 | 159 | }, |
148 | - SoftForkPoint: map[uint64]uint64{SoftFork001: 10461600}, | |
160 | + SoftForkPoint: map[uint64]uint64{SoftFork001: 10461600}, | |
161 | + MovStartHeight: 43354800, | |
162 | + MovRewardPrograms: []MovRewardProgram{ | |
163 | + { | |
164 | + BeginBlock: 1, | |
165 | + EndBlock: 126144000, | |
166 | + Program: "00141d00f85e220e35a23282cfc7f91fe7b34bf6dc18", | |
167 | + }, | |
168 | + }, | |
149 | 169 | } |
150 | 170 | |
151 | 171 | // TestNetParams is the config for vapor-testnet |
@@ -234,6 +254,7 @@ func BytomMainNetParams(vaporParam *Params) *Params { | ||
234 | 254 | return &Params{Bech32HRPSegwit: bech32HRPSegwit} |
235 | 255 | } |
236 | 256 | |
257 | +// InitActiveNetParams load the config by chain ID | |
237 | 258 | func InitActiveNetParams(chainID string) error { |
238 | 259 | var exist bool |
239 | 260 | if ActiveNetParams, exist = NetParams[chainID]; !exist { |
@@ -45,7 +45,7 @@ func accountIndexKey(xpubs []chainkd.XPub) []byte { | ||
45 | 45 | return append(AccountIndexPrefix, hash[:]...) |
46 | 46 | } |
47 | 47 | |
48 | -func Bip44ContractIndexKey(accountID string, change bool) []byte { | |
48 | +func bip44ContractIndexKey(accountID string, change bool) []byte { | |
49 | 49 | key := append(ContractIndexPrefix, []byte(accountID)...) |
50 | 50 | if change { |
51 | 51 | return append(key, 0x01) |
@@ -96,7 +96,7 @@ func (store *AccountStore) InitBatch() acc.AccountStore { | ||
96 | 96 | // CommitBatch commit batch |
97 | 97 | func (store *AccountStore) CommitBatch() error { |
98 | 98 | if store.batch == nil { |
99 | - return errors.New("AccountStore commit fail, store batch is nil.") | |
99 | + return errors.New("accountStore commit fail, store batch is nil") | |
100 | 100 | } |
101 | 101 | store.batch.Write() |
102 | 102 | store.batch = nil |
@@ -119,8 +119,8 @@ func (store *AccountStore) DeleteAccount(account *acc.Account) error { | ||
119 | 119 | } |
120 | 120 | |
121 | 121 | // delete bip44 contract index |
122 | - batch.Delete(Bip44ContractIndexKey(account.ID, false)) | |
123 | - batch.Delete(Bip44ContractIndexKey(account.ID, true)) | |
122 | + batch.Delete(bip44ContractIndexKey(account.ID, false)) | |
123 | + batch.Delete(bip44ContractIndexKey(account.ID, true)) | |
124 | 124 | |
125 | 125 | // delete contract index |
126 | 126 | batch.Delete(contractIndexKey(account.ID)) |
@@ -216,7 +216,7 @@ func (store *AccountStore) GetAccountIndex(xpubs []chainkd.XPub) uint64 { | ||
216 | 216 | // GetBip44ContractIndex get bip44 contract index |
217 | 217 | func (store *AccountStore) GetBip44ContractIndex(accountID string, change bool) uint64 { |
218 | 218 | index := uint64(0) |
219 | - if rawIndexBytes := store.db.Get(Bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil { | |
219 | + if rawIndexBytes := store.db.Get(bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil { | |
220 | 220 | index = common.BytesToUnit64(rawIndexBytes) |
221 | 221 | } |
222 | 222 | return index |
@@ -367,9 +367,9 @@ func (store *AccountStore) SetAccountIndex(account *acc.Account) { | ||
367 | 367 | // SetBip44ContractIndex set contract index |
368 | 368 | func (store *AccountStore) SetBip44ContractIndex(accountID string, change bool, index uint64) { |
369 | 369 | if store.batch == nil { |
370 | - store.db.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index)) | |
370 | + store.db.Set(bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index)) | |
371 | 371 | } else { |
372 | - store.batch.Set(Bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index)) | |
372 | + store.batch.Set(bip44ContractIndexKey(accountID, change), common.Unit64ToBytes(index)) | |
373 | 373 | } |
374 | 374 | } |
375 | 375 |
@@ -1,12 +1,12 @@ | ||
1 | 1 | package database |
2 | 2 | |
3 | 3 | import ( |
4 | - "github.com/golang/protobuf/proto" | |
5 | 4 | dbm "github.com/bytom/vapor/database/leveldb" |
6 | 5 | "github.com/bytom/vapor/database/storage" |
7 | 6 | "github.com/bytom/vapor/errors" |
8 | 7 | "github.com/bytom/vapor/protocol/bc" |
9 | 8 | "github.com/bytom/vapor/protocol/state" |
9 | + "github.com/golang/protobuf/proto" | |
10 | 10 | ) |
11 | 11 | |
12 | 12 | const utxoPreFix = "UT:" |
@@ -91,6 +91,7 @@ func saveUtxoView(batch dbm.Batch, view *state.UtxoViewpoint) error { | ||
91 | 91 | return nil |
92 | 92 | } |
93 | 93 | |
94 | +// SaveUtxoView is export for intergation test | |
94 | 95 | func SaveUtxoView(batch dbm.Batch, view *state.UtxoViewpoint) error { |
95 | 96 | return saveUtxoView(batch, view) |
96 | 97 | } |
@@ -31,6 +31,7 @@ const ( | ||
31 | 31 | recoveryKey //recoveryKey key for db store recovery info. |
32 | 32 | ) |
33 | 33 | |
34 | +// pre-define variables | |
34 | 35 | var ( |
35 | 36 | walletStore = []byte("WS:") |
36 | 37 | SUTXOPrefix = append(walletStore, sutxoPrefix, colon) |
@@ -66,10 +67,12 @@ func calcUnconfirmedTxKey(formatKey string) []byte { | ||
66 | 67 | return append(UnconfirmedTxPrefix, []byte(formatKey)...) |
67 | 68 | } |
68 | 69 | |
70 | +// CalcGlobalTxIndexKey calculate tx hash index key | |
69 | 71 | func CalcGlobalTxIndexKey(txID string) []byte { |
70 | 72 | return append(GlobalTxIndexPrefix, []byte(txID)...) |
71 | 73 | } |
72 | 74 | |
75 | +// CalcGlobalTxIndex calcuate the block index + position index key | |
73 | 76 | func CalcGlobalTxIndex(blockHash *bc.Hash, position uint64) []byte { |
74 | 77 | txIdx := make([]byte, 40) |
75 | 78 | copy(txIdx[:32], blockHash.Bytes()) |
@@ -109,7 +112,7 @@ func (store *WalletStore) InitBatch() wallet.WalletStore { | ||
109 | 112 | // CommitBatch commit batch |
110 | 113 | func (store *WalletStore) CommitBatch() error { |
111 | 114 | if store.batch == nil { |
112 | - return errors.New("WalletStore commit fail, store batch is nil.") | |
115 | + return errors.New("walletStore commit fail, store batch is nil") | |
113 | 116 | } |
114 | 117 | |
115 | 118 | store.batch.Write() |
@@ -347,6 +350,7 @@ func (store *WalletStore) ListAccountUTXOs(id string, isSmartContract bool) ([]* | ||
347 | 350 | return confirmedUTXOs, nil |
348 | 351 | } |
349 | 352 | |
353 | +// ListTransactions list tx by filter args | |
350 | 354 | func (store *WalletStore) ListTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) { |
351 | 355 | annotatedTxs := []*query.AnnotatedTx{} |
352 | 356 | var startKey []byte |
@@ -1,5 +1,6 @@ | ||
1 | 1 | package math |
2 | 2 | |
3 | +// MinUint64 return the min of x and y | |
3 | 4 | func MinUint64(x, y uint64) uint64 { |
4 | 5 | if x < y { |
5 | 6 | return x |
@@ -27,11 +27,7 @@ var ( | ||
27 | 27 | maxNumOfBlocksRegularSync = uint64(128) |
28 | 28 | ) |
29 | 29 | |
30 | -type FastSync interface { | |
31 | - process() error | |
32 | - setSyncPeer(peer *peers.Peer) | |
33 | -} | |
34 | - | |
30 | +// Fetcher is the interface for fetch struct | |
35 | 31 | type Fetcher interface { |
36 | 32 | processBlock(peerID string, block *types.Block) |
37 | 33 | processBlocks(peerID string, blocks []*types.Block) |
@@ -56,7 +52,7 @@ type headersMsg struct { | ||
56 | 52 | |
57 | 53 | type blockKeeper struct { |
58 | 54 | chain Chain |
59 | - fastSync FastSync | |
55 | + fastSync *fastSync | |
60 | 56 | msgFetcher Fetcher |
61 | 57 | peers *peers.PeerSet |
62 | 58 | syncPeer *peers.Peer |
@@ -76,7 +72,7 @@ func newBlockKeeper(chain Chain, peers *peers.PeerSet, fastSyncDB dbm.DB) *block | ||
76 | 72 | } |
77 | 73 | } |
78 | 74 | |
79 | -func (bk *blockKeeper) locateBlocks(locator []*bc.Hash, stopHash *bc.Hash) ([]*types.Block, error) { | |
75 | +func (bk *blockKeeper) locateBlocks(locator []*bc.Hash, stopHash *bc.Hash, isTimeout func() bool) ([]*types.Block, error) { | |
80 | 76 | headers, err := bk.locateHeaders(locator, stopHash, 0, maxNumOfBlocksPerMsg) |
81 | 77 | if err != nil { |
82 | 78 | return nil, err |
@@ -91,6 +87,9 @@ func (bk *blockKeeper) locateBlocks(locator []*bc.Hash, stopHash *bc.Hash) ([]*t | ||
91 | 87 | } |
92 | 88 | |
93 | 89 | blocks = append(blocks, block) |
90 | + if isTimeout() { | |
91 | + break | |
92 | + } | |
94 | 93 | } |
95 | 94 | return blocks, nil |
96 | 95 | } |
@@ -448,7 +448,8 @@ func TestLocateBlocks(t *testing.T) { | ||
448 | 448 | want = append(want, blocks[i]) |
449 | 449 | } |
450 | 450 | |
451 | - got, err := bk.locateBlocks(locator, &c.stopHash) | |
451 | + mockTimeout := func() bool { return false } | |
452 | + got, err := bk.locateBlocks(locator, &c.stopHash, mockTimeout) | |
452 | 453 | if err != c.wantErr { |
453 | 454 | t.Errorf("case %d: got %v want err = %v", i, err, c.wantErr) |
454 | 455 | } |
@@ -12,17 +12,13 @@ import ( | ||
12 | 12 | |
13 | 13 | var errOrphanBlock = errors.New("fast sync inserting orphan block") |
14 | 14 | |
15 | -type BlockProcessor interface { | |
16 | - process(chan struct{}, chan struct{}, uint64, *sync.WaitGroup) | |
17 | -} | |
18 | - | |
19 | 15 | type blockProcessor struct { |
20 | 16 | chain Chain |
21 | - storage Storage | |
17 | + storage *storage | |
22 | 18 | peers *peers.PeerSet |
23 | 19 | } |
24 | 20 | |
25 | -func newBlockProcessor(chain Chain, storage Storage, peers *peers.PeerSet) *blockProcessor { | |
21 | +func newBlockProcessor(chain Chain, storage *storage, peers *peers.PeerSet) *blockProcessor { | |
26 | 22 | return &blockProcessor{ |
27 | 23 | chain: chain, |
28 | 24 | peers: peers, |
@@ -29,12 +29,12 @@ var ( | ||
29 | 29 | type fastSync struct { |
30 | 30 | chain Chain |
31 | 31 | msgFetcher MsgFetcher |
32 | - blockProcessor BlockProcessor | |
32 | + blockProcessor *blockProcessor | |
33 | 33 | peers *peers.PeerSet |
34 | 34 | mainSyncPeer *peers.Peer |
35 | 35 | } |
36 | 36 | |
37 | -func newFastSync(chain Chain, msgFetcher MsgFetcher, storage Storage, peers *peers.PeerSet) *fastSync { | |
37 | +func newFastSync(chain Chain, msgFetcher MsgFetcher, storage *storage, peers *peers.PeerSet) *fastSync { | |
38 | 38 | return &fastSync{ |
39 | 39 | chain: chain, |
40 | 40 | msgFetcher: msgFetcher, |
@@ -3,6 +3,7 @@ package chainmgr | ||
3 | 3 | import ( |
4 | 4 | "errors" |
5 | 5 | "reflect" |
6 | + "time" | |
6 | 7 | |
7 | 8 | log "github.com/sirupsen/logrus" |
8 | 9 |
@@ -38,6 +39,7 @@ type Chain interface { | ||
38 | 39 | ValidateTx(*types.Tx) (bool, error) |
39 | 40 | } |
40 | 41 | |
42 | +// Switch is the interface for network layer | |
41 | 43 | type Switch interface { |
42 | 44 | AddReactor(name string, reactor p2p.Reactor) p2p.Reactor |
43 | 45 | Start() (bool, error) |
@@ -90,6 +92,7 @@ func NewManager(config *cfg.Config, sw Switch, chain Chain, mempool Mempool, dis | ||
90 | 92 | return manager, nil |
91 | 93 | } |
92 | 94 | |
95 | +// AddPeer add the network layer peer to logic layer | |
93 | 96 | func (m *Manager) AddPeer(peer peers.BasePeer) { |
94 | 97 | m.peers.AddPeer(peer) |
95 | 98 | } |
@@ -154,7 +157,12 @@ func (m *Manager) handleGetBlockMsg(peer *peers.Peer, msg *msgs.GetBlockMessage) | ||
154 | 157 | } |
155 | 158 | |
156 | 159 | func (m *Manager) handleGetBlocksMsg(peer *peers.Peer, msg *msgs.GetBlocksMessage) { |
157 | - blocks, err := m.blockKeeper.locateBlocks(msg.GetBlockLocator(), msg.GetStopHash()) | |
160 | + endTime := time.Now().Add(requireBlocksTimeout / 2) | |
161 | + isTimeout := func() bool { | |
162 | + return time.Now().After(endTime) | |
163 | + } | |
164 | + | |
165 | + blocks, err := m.blockKeeper.locateBlocks(msg.GetBlockLocator(), msg.GetStopHash(), isTimeout) | |
158 | 166 | if err != nil || len(blocks) == 0 { |
159 | 167 | return |
160 | 168 | } |
@@ -354,10 +362,12 @@ func (m *Manager) processMsg(basePeer peers.BasePeer, msgType byte, msg msgs.Blo | ||
354 | 362 | } |
355 | 363 | } |
356 | 364 | |
365 | +// RemovePeer delete peer for peer set | |
357 | 366 | func (m *Manager) RemovePeer(peerID string) { |
358 | 367 | m.peers.RemovePeer(peerID) |
359 | 368 | } |
360 | 369 | |
370 | +// SendStatus sent the current self status to remote peer | |
361 | 371 | func (m *Manager) SendStatus(peer peers.BasePeer) error { |
362 | 372 | p := m.peers.GetPeer(peer.ID()) |
363 | 373 | if p == nil { |
@@ -371,6 +381,7 @@ func (m *Manager) SendStatus(peer peers.BasePeer) error { | ||
371 | 381 | return nil |
372 | 382 | } |
373 | 383 | |
384 | +// Start the network logic layer | |
374 | 385 | func (m *Manager) Start() error { |
375 | 386 | var err error |
376 | 387 | m.txMsgSub, err = m.eventDispatcher.Subscribe(core.TxMsgEvent{}) |
@@ -24,7 +24,7 @@ const ( | ||
24 | 24 | var ( |
25 | 25 | requireBlockTimeout = 20 * time.Second |
26 | 26 | requireHeadersTimeout = 30 * time.Second |
27 | - requireBlocksTimeout = 50 * time.Second | |
27 | + requireBlocksTimeout = 90 * time.Second | |
28 | 28 | checkSyncPeerNumInterval = 5 * time.Second |
29 | 29 | |
30 | 30 | errRequestBlocksTimeout = errors.New("request blocks timeout") |
@@ -33,6 +33,7 @@ var ( | ||
33 | 33 | errSendMsg = errors.New("send message error") |
34 | 34 | ) |
35 | 35 | |
36 | +// MsgFetcher is the interface for msg fetch struct | |
36 | 37 | type MsgFetcher interface { |
37 | 38 | resetParameter() |
38 | 39 | addSyncPeer(peerID string) |
@@ -51,7 +52,7 @@ type fetchBlocksResult struct { | ||
51 | 52 | } |
52 | 53 | |
53 | 54 | type msgFetcher struct { |
54 | - storage Storage | |
55 | + storage *storage | |
55 | 56 | syncPeers *fastSyncPeers |
56 | 57 | peers *peers.PeerSet |
57 | 58 | blockProcessCh chan *blockMsg |
@@ -61,7 +62,7 @@ type msgFetcher struct { | ||
61 | 62 | mux sync.RWMutex |
62 | 63 | } |
63 | 64 | |
64 | -func newMsgFetcher(storage Storage, peers *peers.PeerSet) *msgFetcher { | |
65 | +func newMsgFetcher(storage *storage, peers *peers.PeerSet) *msgFetcher { | |
65 | 66 | return &msgFetcher{ |
66 | 67 | storage: storage, |
67 | 68 | syncPeers: newFastSyncPeers(), |
@@ -15,13 +15,7 @@ var ( | ||
15 | 15 | errDBFindBlock = errors.New("can't find block from DB") |
16 | 16 | ) |
17 | 17 | |
18 | -type Storage interface { | |
19 | - resetParameter() | |
20 | - writeBlocks(peerID string, blocks []*types.Block) error | |
21 | - readBlock(height uint64) (*blockStorage, error) | |
22 | - deleteBlock(height uint64) | |
23 | -} | |
24 | - | |
18 | +// LocalStore is the interface for persistent storage | |
25 | 19 | type LocalStore interface { |
26 | 20 | writeBlock(block *types.Block) error |
27 | 21 | readBlock(height uint64) (*types.Block, error) |
@@ -5,6 +5,7 @@ import ( | ||
5 | 5 | "errors" |
6 | 6 | "net" |
7 | 7 | "net/http" |
8 | + // debug tool | |
8 | 9 | _ "net/http/pprof" |
9 | 10 | "path/filepath" |
10 | 11 | "reflect" |
@@ -84,7 +85,7 @@ func NewNode(config *cfg.Config) *Node { | ||
84 | 85 | accessTokens := accesstoken.NewStore(tokenDB) |
85 | 86 | |
86 | 87 | dispatcher := event.NewDispatcher() |
87 | - movCore := mov.NewMovCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) | |
88 | + movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) | |
88 | 89 | assetFilter := protocol.NewAssetFilter(config.CrossChain.AssetWhitelist) |
89 | 90 | txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore, assetFilter}, dispatcher) |
90 | 91 | chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher) |
@@ -177,7 +178,7 @@ func Rollback(config *cfg.Config, targetHeight uint64) error { | ||
177 | 178 | store := database.NewStore(coreDB) |
178 | 179 | |
179 | 180 | dispatcher := event.NewDispatcher() |
180 | - movCore := mov.NewMovCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) | |
181 | + movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight) | |
181 | 182 | txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher) |
182 | 183 | chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher) |
183 | 184 | if err != nil { |
@@ -235,7 +236,7 @@ func checkConfig(chain *protocol.Chain, config *cfg.Config) error { | ||
235 | 236 | typedInput := genesisBlock.Transactions[0].Inputs[0].TypedInput |
236 | 237 | if v, ok := typedInput.(*types.CoinbaseInput); ok { |
237 | 238 | if !reflect.DeepEqual(fedpegScript, v.Arbitrary) { |
238 | - return errors.New("config xpubs don't equal genesis block xpubs.") | |
239 | + return errors.New("config xpubs don't equal genesis block xpubs") | |
239 | 240 | } |
240 | 241 | } |
241 | 242 | return nil |
@@ -272,6 +273,7 @@ func (n *Node) initAndstartAPIServer() { | ||
272 | 273 | n.api.StartServer(*listenAddr) |
273 | 274 | } |
274 | 275 | |
276 | +// OnStart implements BaseService | |
275 | 277 | func (n *Node) OnStart() error { |
276 | 278 | if n.miningEnable { |
277 | 279 | if _, err := n.wallet.AccountMgr.GetMiningAddress(); err != nil { |
@@ -303,6 +305,7 @@ func (n *Node) OnStart() error { | ||
303 | 305 | return nil |
304 | 306 | } |
305 | 307 | |
308 | +// OnStop implements BaseService | |
306 | 309 | func (n *Node) OnStop() { |
307 | 310 | n.notificationMgr.Shutdown() |
308 | 311 | n.notificationMgr.WaitForShutdown() |
@@ -316,6 +319,7 @@ func (n *Node) OnStop() { | ||
316 | 319 | n.eventDispatcher.Stop() |
317 | 320 | } |
318 | 321 | |
322 | +// RunForever listen to the stop signal | |
319 | 323 | func (n *Node) RunForever() { |
320 | 324 | // Sleep forever and then... |
321 | 325 | cmn.TrapSignal(func() { |
@@ -102,7 +102,7 @@ func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) e | ||
102 | 102 | continue |
103 | 103 | } |
104 | 104 | |
105 | - results, gasLeft := preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft) | |
105 | + results, gasLeft := b.preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft) | |
106 | 106 | for _, result := range results { |
107 | 107 | if result.err != nil && !result.gasOnly { |
108 | 108 | log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("mining block generation: skip tx due to") |
@@ -139,11 +139,6 @@ func (b *blockBuilder) applyTransactionFromPool() error { | ||
139 | 139 | } |
140 | 140 | |
141 | 141 | func (b *blockBuilder) applyTransactionFromSubProtocol() error { |
142 | - cp, err := b.accountManager.GetCoinbaseControlProgram() | |
143 | - if err != nil { | |
144 | - return err | |
145 | - } | |
146 | - | |
147 | 142 | isTimeout := func() bool { |
148 | 143 | return b.getTimeoutStatus() > timeoutOk |
149 | 144 | } |
@@ -153,7 +148,7 @@ func (b *blockBuilder) applyTransactionFromSubProtocol() error { | ||
153 | 148 | break |
154 | 149 | } |
155 | 150 | |
156 | - subTxs, err := p.BeforeProposalBlock(b.block.Transactions, cp, b.block.Height, b.gasLeft, isTimeout) | |
151 | + subTxs, err := p.BeforeProposalBlock(b.block.Transactions, b.block.Height, b.gasLeft, isTimeout) | |
157 | 152 | if err != nil { |
158 | 153 | log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package") |
159 | 154 | continue |
@@ -294,7 +289,7 @@ type validateTxResult struct { | ||
294 | 289 | err error |
295 | 290 | } |
296 | 291 | |
297 | -func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) { | |
292 | +func (b *blockBuilder) preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) { | |
298 | 293 | var results []*validateTxResult |
299 | 294 | bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}} |
300 | 295 | bcTxs := make([]*bc.Tx, len(txs)) |
@@ -328,7 +323,7 @@ func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoView | ||
328 | 323 | continue |
329 | 324 | } |
330 | 325 | |
331 | - if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil { | |
326 | + if err := b.validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil { | |
332 | 327 | results = append(results, &validateTxResult{tx: txs[i], err: err}) |
333 | 328 | continue |
334 | 329 | } |
@@ -339,10 +334,10 @@ func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoView | ||
339 | 334 | return results, gasLeft |
340 | 335 | } |
341 | 336 | |
342 | -func validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error { | |
337 | +func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error { | |
343 | 338 | for _, subProtocol := range subProtocols { |
344 | 339 | verifyResult := &bc.TxVerifyResult{StatusFail: statusFail} |
345 | - if err := subProtocol.ValidateTx(tx, verifyResult); err != nil { | |
340 | + if err := subProtocol.ValidateTx(tx, verifyResult, b.block.Height); err != nil { | |
346 | 341 | return err |
347 | 342 | } |
348 | 343 | } |
@@ -7,14 +7,15 @@ import ( | ||
7 | 7 | "github.com/bytom/vapor/protocol/bc/types" |
8 | 8 | ) |
9 | 9 | |
10 | -type assetFilter struct { | |
10 | +// AssetFilter is struct for allow open federation asset cross chain | |
11 | +type AssetFilter struct { | |
11 | 12 | whitelist map[string]struct{} |
12 | 13 | } |
13 | 14 | |
14 | 15 | // NewAssetFilter returns a assetFilter according a whitelist, |
15 | 16 | // which is a strings list cancated via comma |
16 | -func NewAssetFilter(whitelist string) *assetFilter { | |
17 | - af := &assetFilter{whitelist: make(map[string]struct{})} | |
17 | +func NewAssetFilter(whitelist string) *AssetFilter { | |
18 | + af := &AssetFilter{whitelist: make(map[string]struct{})} | |
18 | 19 | af.whitelist[consensus.BTMAssetID.String()] = struct{}{} |
19 | 20 | for _, assetID := range strings.Split(whitelist, ",") { |
20 | 21 | af.whitelist[strings.ToLower(assetID)] = struct{}{} |
@@ -25,7 +26,7 @@ func NewAssetFilter(whitelist string) *assetFilter { | ||
25 | 26 | // IsDust implements the DustFilterer interface. |
26 | 27 | // It filters a transaction as long as there is one asset neither BTM or in the whitelist |
27 | 28 | // No need to check the output assets types becauese they must have been cover in input assets types |
28 | -func (af *assetFilter) IsDust(tx *types.Tx) bool { | |
29 | +func (af *AssetFilter) IsDust(tx *types.Tx) bool { | |
29 | 30 | for _, input := range tx.Inputs { |
30 | 31 | if _, ok := input.TypedInput.(*types.CrossChainInput); !ok { |
31 | 32 | continue |
@@ -142,8 +142,13 @@ func (c *Chain) validateSign(block *types.Block) error { | ||
142 | 142 | |
143 | 143 | if err := c.checkNodeSign(&block.BlockHeader, node, block.Get(node.Order)); err == errDoubleSignBlock { |
144 | 144 | log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block") |
145 | - block.BlockWitness.Delete(node.Order) | |
146 | - continue | |
145 | + // if the blocker double sign & become the mainchain, that means | |
146 | + // all the side chain will reject the main chain make the chain | |
147 | + // fork. All the node will ban each other & can't roll back | |
148 | + if blocker != pubKey { | |
149 | + block.BlockWitness.Delete(node.Order) | |
150 | + continue | |
151 | + } | |
147 | 152 | } else if err != nil { |
148 | 153 | return err |
149 | 154 | } |
@@ -228,23 +233,22 @@ func (c *Chain) signBlockHeader(blockHeader *types.BlockHeader) ([]byte, error) | ||
228 | 233 | xpub := xprv.XPub() |
229 | 234 | node, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpub.String()) |
230 | 235 | blockHash := blockHeader.Hash() |
231 | - blockHashStr := blockHash.String() | |
232 | 236 | if err == errNotFoundConsensusNode { |
233 | - log.WithFields(log.Fields{"module": logModule, "blockHash": blockHashStr}).Warn("can't find consensus node of current node") | |
237 | + log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String()}).Debug("can't find consensus node of current node") | |
234 | 238 | return nil, nil |
235 | 239 | } else if err != nil { |
236 | 240 | return nil, err |
237 | 241 | } |
238 | 242 | |
239 | - if err := c.checkDoubleSign(blockHeader, node.XPub.String()); err == errDoubleSignBlock { | |
240 | - log.WithFields(log.Fields{"module": logModule, "blockHash": blockHashStr}).Warn("current node has double sign the block") | |
243 | + if len(blockHeader.Get(node.Order)) != 0 { | |
241 | 244 | return nil, nil |
242 | - } else if err != nil { | |
243 | - return nil, err | |
244 | 245 | } |
245 | 246 | |
246 | - if signature := blockHeader.Get(node.Order); len(signature) != 0 { | |
247 | + if err := c.checkDoubleSign(blockHeader, node.XPub.String()); err == errDoubleSignBlock { | |
248 | + log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String()}).Warn("current node has double sign the block") | |
247 | 249 | return nil, nil |
250 | + } else if err != nil { | |
251 | + return nil, err | |
248 | 252 | } |
249 | 253 | |
250 | 254 | signature := xprv.Sign(blockHeader.Hash().Bytes()) |
@@ -102,6 +102,7 @@ func (b *Block) readFrom(r *blockchain.Reader) error { | ||
102 | 102 | return nil |
103 | 103 | } |
104 | 104 | |
105 | +// WriteTo write block to io.Writer | |
105 | 106 | func (b *Block) WriteTo(w io.Writer) (int64, error) { |
106 | 107 | ew := errors.NewWriter(w) |
107 | 108 | if err := b.writeTo(ew, SerBlockFull); err != nil { |
@@ -6,6 +6,7 @@ import ( | ||
6 | 6 | "github.com/bytom/vapor/encoding/blockchain" |
7 | 7 | ) |
8 | 8 | |
9 | +// BlockWitness save the consensus node sign | |
9 | 10 | type BlockWitness struct { |
10 | 11 | // Witness is a vector of arguments for validating this block. |
11 | 12 | Witness [][]byte |
@@ -21,6 +22,7 @@ func (bw *BlockWitness) writeTo(w io.Writer) error { | ||
21 | 22 | return err |
22 | 23 | } |
23 | 24 | |
25 | +// Set save data to index position | |
24 | 26 | func (bw *BlockWitness) Set(index uint64, data []byte) { |
25 | 27 | if uint64(len(bw.Witness)) <= index { |
26 | 28 | newWitness := make([][]byte, index+1, index+1) |
@@ -30,12 +32,14 @@ func (bw *BlockWitness) Set(index uint64, data []byte) { | ||
30 | 32 | bw.Witness[index] = data |
31 | 33 | } |
32 | 34 | |
35 | +// Delete remove data from index position | |
33 | 36 | func (bw *BlockWitness) Delete(index uint64) { |
34 | 37 | if uint64(len(bw.Witness)) > index { |
35 | 38 | bw.Witness[index] = nil |
36 | 39 | } |
37 | 40 | } |
38 | 41 | |
42 | +// Get return data from index position | |
39 | 43 | func (bw *BlockWitness) Get(index uint64) []byte { |
40 | 44 | if uint64(len(bw.Witness)) > index { |
41 | 45 | return bw.Witness[index] |
@@ -28,4 +28,5 @@ func NewCrossChainOutput(assetID bc.AssetID, amount uint64, controlProgram []byt | ||
28 | 28 | } |
29 | 29 | } |
30 | 30 | |
31 | +// OutputType implement the txout interface | |
31 | 32 | func (it *CrossChainOutput) OutputType() uint8 { return CrossChainOutputType } |
@@ -28,4 +28,5 @@ func NewIntraChainOutput(assetID bc.AssetID, amount uint64, controlProgram []byt | ||
28 | 28 | } |
29 | 29 | } |
30 | 30 | |
31 | +// OutputType implement the txout interface | |
31 | 32 | func (it *IntraChainOutput) OutputType() uint8 { return IntraChainOutputType } |
@@ -30,4 +30,5 @@ func NewVoteOutput(assetID bc.AssetID, amount uint64, controlProgram []byte, vot | ||
30 | 30 | } |
31 | 31 | } |
32 | 32 | |
33 | +// OutputType implement the txout interface | |
33 | 34 | func (it *VoteOutput) OutputType() uint8 { return VoteOutputType } |
@@ -20,7 +20,7 @@ var ( | ||
20 | 20 | |
21 | 21 | // BlockExist check is a block in chain or orphan |
22 | 22 | func (c *Chain) BlockExist(hash *bc.Hash) bool { |
23 | - if bh, err := c.store.GetBlockHeader(hash); err == nil && bh.Height <= c.BestBlockHeight() { | |
23 | + if _, err := c.store.GetBlockHeader(hash); err == nil { | |
24 | 24 | return true |
25 | 25 | } |
26 | 26 | return c.orphanManage.BlockExist(hash) |
@@ -104,6 +104,7 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { | ||
104 | 104 | if err != nil { |
105 | 105 | return err |
106 | 106 | } |
107 | + | |
107 | 108 | if err := consensusResult.ApplyBlock(block); err != nil { |
108 | 109 | return err |
109 | 110 | } |
@@ -19,14 +19,14 @@ const ( | ||
19 | 19 | maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS) |
20 | 20 | ) |
21 | 21 | |
22 | +// Protocoler is interface for layer 2 consensus protocol | |
22 | 23 | type Protocoler interface { |
23 | 24 | Name() string |
24 | 25 | StartHeight() uint64 |
25 | - BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) | |
26 | + BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) | |
26 | 27 | ChainStatus() (uint64, *bc.Hash, error) |
27 | 28 | ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error |
28 | - ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error | |
29 | - ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error | |
29 | + ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error | |
30 | 30 | ApplyBlock(block *types.Block) error |
31 | 31 | DetachBlock(block *types.Block) error |
32 | 32 | } |
@@ -174,6 +174,7 @@ func (c *Chain) InMainChain(hash bc.Hash) bool { | ||
174 | 174 | return *blockHash == hash |
175 | 175 | } |
176 | 176 | |
177 | +// SubProtocols return list of layer 2 consensus protocol | |
177 | 178 | func (c *Chain) SubProtocols() []Protocoler { |
178 | 179 | return c.subProtocols |
179 | 180 | } |
@@ -9,6 +9,7 @@ import ( | ||
9 | 9 | "github.com/bytom/vapor/protocol/state" |
10 | 10 | ) |
11 | 11 | |
12 | +// predefine errors | |
12 | 13 | var ( |
13 | 14 | ErrNotFoundConsensusResult = errors.New("can't find the vote result by given sequence") |
14 | 15 | ) |
@@ -54,7 +54,7 @@ func (c *Chain) validateTx(tx *types.Tx, bh *types.BlockHeader) (bool, error) { | ||
54 | 54 | |
55 | 55 | txVerifyResult := &bc.TxVerifyResult{StatusFail: err != nil} |
56 | 56 | for _, p := range c.subProtocols { |
57 | - if err := p.ValidateTx(tx, txVerifyResult); err != nil { | |
57 | + if err := p.ValidateTx(tx, txVerifyResult, bh.Height); err != nil { | |
58 | 58 | c.txPool.AddErrCache(&tx.ID, err) |
59 | 59 | return false, err |
60 | 60 | } |
@@ -43,10 +43,12 @@ var ( | ||
43 | 43 | ErrDustTx = errors.New("transaction is dust tx") |
44 | 44 | ) |
45 | 45 | |
46 | +// DustFilterer is inerface for dust transaction filter rule | |
46 | 47 | type DustFilterer interface { |
47 | 48 | IsDust(tx *types.Tx) bool |
48 | 49 | } |
49 | 50 | |
51 | +// TxMsgEvent is message wrap for subscribe event | |
50 | 52 | type TxMsgEvent struct{ TxMsg *TxPoolMsg } |
51 | 53 | |
52 | 54 | // TxDesc store tx and related info for mining strategy |
@@ -922,8 +922,10 @@ func TestMagneticContractTx(t *testing.T) { | ||
922 | 922 | types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 200000000, 0, programBuyer), |
923 | 923 | }, |
924 | 924 | Outputs: []*types.TxOutput{ |
925 | - types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram), | |
926 | - types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram), | |
925 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 199800000, sellerArgs.SellerProgram), | |
926 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 99900000, buyerArgs.SellerProgram), | |
927 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000, []byte{0x51}), | |
928 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000, []byte{0x51}), | |
927 | 929 | }, |
928 | 930 | }), |
929 | 931 | }, |
@@ -942,9 +944,11 @@ func TestMagneticContractTx(t *testing.T) { | ||
942 | 944 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer), |
943 | 945 | }, |
944 | 946 | Outputs: []*types.TxOutput{ |
945 | - types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram), | |
947 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 99900000, sellerArgs.SellerProgram), | |
946 | 948 | types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, programSeller), |
947 | - types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram), | |
949 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 49950000, buyerArgs.SellerProgram), | |
950 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000, []byte{0x51}), | |
951 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000, []byte{0x51}), | |
948 | 952 | }, |
949 | 953 | }), |
950 | 954 | }, |
@@ -963,9 +967,11 @@ func TestMagneticContractTx(t *testing.T) { | ||
963 | 967 | types.NewSpendInput([][]byte{vm.Int64Bytes(100000000), vm.Int64Bytes(1), vm.Int64Bytes(0)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 300000000, 0, programBuyer), |
964 | 968 | }, |
965 | 969 | Outputs: []*types.TxOutput{ |
966 | - types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000000, sellerArgs.SellerProgram), | |
967 | - types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000000, buyerArgs.SellerProgram), | |
970 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 199800000, sellerArgs.SellerProgram), | |
971 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 99900000, buyerArgs.SellerProgram), | |
968 | 972 | types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, programBuyer), |
973 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 200000, []byte{0x51}), | |
974 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 100000, []byte{0x51}), | |
969 | 975 | }, |
970 | 976 | }), |
971 | 977 | }, |
@@ -1063,9 +1069,11 @@ func TestMagneticContractTx(t *testing.T) { | ||
1063 | 1069 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 20}, sellerArgs.RequestedAsset, 100000000, 0, programBuyer), |
1064 | 1070 | }, |
1065 | 1071 | Outputs: []*types.TxOutput{ |
1066 | - types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000000, sellerArgs.SellerProgram), | |
1072 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 99900000, sellerArgs.SellerProgram), | |
1067 | 1073 | types.NewIntraChainOutput(buyerArgs.RequestedAsset, 150000000, []byte{0x55}), |
1068 | - types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000000, buyerArgs.SellerProgram), | |
1074 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 49950000, buyerArgs.SellerProgram), | |
1075 | + types.NewIntraChainOutput(sellerArgs.RequestedAsset, 100000, []byte{0x51}), | |
1076 | + types.NewIntraChainOutput(buyerArgs.RequestedAsset, 50000, []byte{0x51}), | |
1069 | 1077 | }, |
1070 | 1078 | }), |
1071 | 1079 | }, |
@@ -1139,9 +1147,12 @@ func TestRingMagneticContractTx(t *testing.T) { | ||
1139 | 1147 | types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, bc.Hash{V0: 30}, bobArgs.RequestedAsset, 400000000, 0, jackProgram), |
1140 | 1148 | }, |
1141 | 1149 | Outputs: []*types.TxOutput{ |
1142 | - types.NewIntraChainOutput(aliceArgs.RequestedAsset, 200000000, aliceArgs.SellerProgram), | |
1143 | - types.NewIntraChainOutput(bobArgs.RequestedAsset, 400000000, bobArgs.SellerProgram), | |
1144 | - types.NewIntraChainOutput(jackArgs.RequestedAsset, 100000000, jackArgs.SellerProgram), | |
1150 | + types.NewIntraChainOutput(aliceArgs.RequestedAsset, 199800000, aliceArgs.SellerProgram), | |
1151 | + types.NewIntraChainOutput(bobArgs.RequestedAsset, 399600000, bobArgs.SellerProgram), | |
1152 | + types.NewIntraChainOutput(jackArgs.RequestedAsset, 99900000, jackArgs.SellerProgram), | |
1153 | + types.NewIntraChainOutput(aliceArgs.RequestedAsset, 200000, []byte{0x51}), | |
1154 | + types.NewIntraChainOutput(bobArgs.RequestedAsset, 400000, []byte{0x51}), | |
1155 | + types.NewIntraChainOutput(jackArgs.RequestedAsset, 100000, []byte{0x51}), | |
1145 | 1156 | }, |
1146 | 1157 | }), |
1147 | 1158 | }, |
@@ -1161,16 +1172,16 @@ func TestValidateOpenFederationIssueAsset(t *testing.T) { | ||
1161 | 1172 | tx := &types.Tx{TxData: types.TxData{Version: 1}} |
1162 | 1173 | tx.Inputs = append(tx.Inputs, types.NewCrossChainInput(nil, |
1163 | 1174 | testutil.MustDecodeHash("449143cb95389d19a1939879681168f78cc62614f4e0fb41f17b3232528a709d"), |
1164 | - testutil.MustDecodeAsset("ed9d55880b4c88d8579e6c542b0e714eb8c05226ae84b6794a18aad2e1124f70"), | |
1175 | + testutil.MustDecodeAsset("60b550a772ddde42717ef3ab1178cf4f712a02fc9faf3678aa5468facff128f5"), | |
1165 | 1176 | 100000000, |
1166 | 1177 | 0, |
1167 | 1178 | 1, |
1168 | - testutil.MustDecodeHexString("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b0a202020202269737375655f61737365745f616374696f6e223a202263726f73735f636861696e220a20207d2c0a2020226e616d65223a2022454f53222c0a20202271756f72756d223a20312c0a20202272656973737565223a202274727565222c0a20202273796d626f6c223a2022454f53220a7d"), | |
1179 | + testutil.MustDecodeHexString("7b0a202022646563696d616c73223a20382c0a2020226465736372697074696f6e223a207b0a202020202269737375655f61737365745f616374696f6e223a20226f70656e5f66656465726174696f6e5f63726f73735f636861696e220a20207d2c0a2020226e616d65223a2022454f53222c0a20202271756f72756d223a20312c0a20202272656973737565223a202274727565222c0a20202273796d626f6c223a2022454f53220a7d"), | |
1169 | 1180 | testutil.MustDecodeHexString("ae20d827c281d47f5de93f98544b20468feaac046bf8b89bd51102f6e971f09d215920be43bb856fe337b37f5f09040c2b6cdbe23eaf5aa4770b16ea51fdfc45514c295152ad"), |
1170 | 1181 | )) |
1171 | 1182 | |
1172 | 1183 | tx.Outputs = append(tx.Outputs, types.NewIntraChainOutput( |
1173 | - testutil.MustDecodeAsset("ed9d55880b4c88d8579e6c542b0e714eb8c05226ae84b6794a18aad2e1124f70"), | |
1184 | + testutil.MustDecodeAsset("60b550a772ddde42717ef3ab1178cf4f712a02fc9faf3678aa5468facff128f5"), | |
1174 | 1185 | 100000000, |
1175 | 1186 | testutil.MustDecodeHexString("0014d8dd58f374f58cffb1b1a7cc1e18a712b4fe67b5"), |
1176 | 1187 | )) |
@@ -1193,7 +1204,7 @@ func TestValidateOpenFederationIssueAsset(t *testing.T) { | ||
1193 | 1204 | } |
1194 | 1205 | } |
1195 | 1206 | |
1196 | -func toByte64(str string) [64]byte{ | |
1207 | +func toByte64(str string) [64]byte { | |
1197 | 1208 | var result [64]byte |
1198 | 1209 | bytes := testutil.MustDecodeHexString(str) |
1199 | 1210 | for i := range bytes { |
@@ -1201,6 +1212,7 @@ func toByte64(str string) [64]byte{ | ||
1201 | 1212 | } |
1202 | 1213 | return result |
1203 | 1214 | } |
1215 | + | |
1204 | 1216 | // A txFixture is returned by sample (below) to produce a sample |
1205 | 1217 | // transaction, which takes a separate, optional _input_ txFixture to |
1206 | 1218 | // affect the transaction that's built. The components of the |
@@ -166,14 +166,16 @@ func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) { | ||
166 | 166 | // standardProgram: Program, |
167 | 167 | // sellerKey: PublicKey) locks valueAmount of valueAsset { |
168 | 168 | // clause partialTrade(exchangeAmount: Amount) { |
169 | -// define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioNumerator | |
170 | -// verify actualAmount > 0 && actualAmount < valueAmount | |
171 | -// lock exchangeAmount of requestedAsset with sellerProgram | |
169 | +// define actualAmount: Integer = exchangeAmount * ratioDenominator / ratioNumerator | |
170 | +// verify actualAmount > 0 && actualAmount < valueAmount | |
171 | +// define receiveAmount: Integer = exchangeAmount * 999 / 1000 | |
172 | +// lock receiveAmount of requestedAsset with sellerProgram | |
172 | 173 | // lock valueAmount-actualAmount of valueAsset with standardProgram |
173 | 174 | // unlock actualAmount of valueAsset |
174 | 175 | // } |
175 | 176 | // clause fullTrade() { |
176 | 177 | // define requestedAmount: Integer = valueAmount * ratioNumerator / ratioDenominator |
178 | +// define requestedAmount: Integer = requestedAmount * 999 / 1000 | |
177 | 179 | // verify requestedAmount > 0 |
178 | 180 | // lock requestedAmount of requestedAsset with sellerProgram |
179 | 181 | // unlock valueAmount of valueAsset |
@@ -219,12 +221,15 @@ func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) { | ||
219 | 221 | // TOALTSTACK [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position>] |
220 | 222 | // 6 [... exchangeAmount sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> 6] |
221 | 223 | // ROLL [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount] |
222 | -// 3 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 3] | |
223 | -// ROLL [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset] | |
224 | -// 1 [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1] | |
225 | -// 5 [... sellerKey standardProgram sellerProgram actualAmount <position> exchangeAmount requestedAsset 1 5] | |
226 | -// ROLL [... sellerKey standardProgram actualAmount <position> exchangeAmount requestedAsset 1 sellerProgram] | |
227 | -// CHECKOUTPUT [... sellerKey standardProgram actualAmount checkOutput(exchangeAmount, requestedAsset, sellerProgram)] | |
224 | +// 999 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 999] | |
225 | +// 1000 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> exchangeAmount 1000] | |
226 | +// MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount] | |
227 | +// 3 [... sellerKey standardProgram sellerProgram requestedAsset actualAmount <position> receiveAmount 3] | |
228 | +// ROLL [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset] | |
229 | +// 1 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1] | |
230 | +// 5 [... sellerKey standardProgram sellerProgram actualAmount <position> receiveAmount requestedAsset 1 5] | |
231 | +// ROLL [... sellerKey standardProgram actualAmount <position> receiveAmount requestedAsset 1 sellerProgram] | |
232 | +// CHECKOUTPUT [... sellerKey standardProgram actualAmount checkOutput(receiveAmount, requestedAsset, sellerProgram)] | |
228 | 233 | // VERIFY [... sellerKey standardProgram actualAmount] |
229 | 234 | // FROMALTSTACK [... sellerKey standardProgram actualAmount <position>] |
230 | 235 | // 1 [... sellerKey standardProgram actualAmount <position> 1] |
@@ -246,6 +251,9 @@ func P2WMCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) { | ||
246 | 251 | // 3 [... sellerKey standardProgram sellerProgram ratioDenominator requestedAsset valueAmount ratioNumerator 3] |
247 | 252 | // ROLL [... sellerKey standardProgram sellerProgram requestedAsset valueAmount ratioNumerator ratioDenominator] |
248 | 253 | // MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount] |
254 | +// 999 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999] | |
255 | +// 1000 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount 999 1000] | |
256 | +// MULFRACTION [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount] | |
249 | 257 | // DUP [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount] |
250 | 258 | // 0 [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount requestedAmount 0] |
251 | 259 | // GREATERTHAN [... sellerKey standardProgram sellerProgram requestedAsset requestedAmount (requestedAmount > 0)] |
@@ -315,6 +323,9 @@ func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) { | ||
315 | 323 | builder.AddOp(vm.OP_TOALTSTACK) |
316 | 324 | builder.AddOp(vm.OP_6) |
317 | 325 | builder.AddOp(vm.OP_ROLL) |
326 | + builder.AddInt64(999) | |
327 | + builder.AddInt64(1000) | |
328 | + builder.AddOp(vm.OP_MULFRACTION) | |
318 | 329 | builder.AddOp(vm.OP_3) |
319 | 330 | builder.AddOp(vm.OP_ROLL) |
320 | 331 | builder.AddOp(vm.OP_1) |
@@ -340,6 +351,9 @@ func P2MCProgram(magneticContractArgs MagneticContractArgs) ([]byte, error) { | ||
340 | 351 | builder.AddOp(vm.OP_3) |
341 | 352 | builder.AddOp(vm.OP_ROLL) |
342 | 353 | builder.AddOp(vm.OP_MULFRACTION) |
354 | + builder.AddInt64(999) | |
355 | + builder.AddInt64(1000) | |
356 | + builder.AddOp(vm.OP_MULFRACTION) | |
343 | 357 | builder.AddOp(vm.OP_DUP) |
344 | 358 | builder.AddOp(vm.OP_0) |
345 | 359 | builder.AddOp(vm.OP_GREATERTHAN) |
@@ -0,0 +1,1571 @@ | ||
1 | +package test | |
2 | + | |
3 | +import ( | |
4 | + "os" | |
5 | + "testing" | |
6 | + | |
7 | + "github.com/bytom/vapor/application/mov" | |
8 | + movDatabase "github.com/bytom/vapor/application/mov/database" | |
9 | + "github.com/bytom/vapor/consensus" | |
10 | + "github.com/bytom/vapor/database" | |
11 | + dbm "github.com/bytom/vapor/database/leveldb" | |
12 | + "github.com/bytom/vapor/database/storage" | |
13 | + "github.com/bytom/vapor/protocol" | |
14 | + "github.com/bytom/vapor/protocol/bc" | |
15 | + "github.com/bytom/vapor/protocol/bc/types" | |
16 | + "github.com/bytom/vapor/protocol/state" | |
17 | + "github.com/bytom/vapor/testutil" | |
18 | +) | |
19 | + | |
20 | +type chainData struct { | |
21 | + bestBlockHeader *types.BlockHeader | |
22 | + lastIrrBlockHeader *types.BlockHeader | |
23 | + utxoViewPoint *state.UtxoViewpoint | |
24 | + storedBlocks []*types.Block | |
25 | + consensusResults []*state.ConsensusResult | |
26 | +} | |
27 | + | |
28 | +func TestRollback(t *testing.T) { | |
29 | + cases := []struct { | |
30 | + desc string | |
31 | + movStartHeight uint64 | |
32 | + RoundVoteBlockNums uint64 | |
33 | + beforeChainData *chainData | |
34 | + wantChainData *chainData | |
35 | + rollbackToTargetHeight uint64 | |
36 | + }{ | |
37 | + { | |
38 | + desc: "rollback from height 1 to 0", | |
39 | + movStartHeight: 10, | |
40 | + RoundVoteBlockNums: 1200, | |
41 | + rollbackToTargetHeight: 0, | |
42 | + beforeChainData: &chainData{ | |
43 | + bestBlockHeader: &types.BlockHeader{ | |
44 | + Height: 1, | |
45 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
46 | + }, | |
47 | + lastIrrBlockHeader: &types.BlockHeader{ | |
48 | + Height: 1, | |
49 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
50 | + }, | |
51 | + utxoViewPoint: &state.UtxoViewpoint{ | |
52 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
53 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
54 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
55 | + }, | |
56 | + }, | |
57 | + storedBlocks: []*types.Block{ | |
58 | + { | |
59 | + BlockHeader: types.BlockHeader{ | |
60 | + Height: 0, | |
61 | + }, | |
62 | + Transactions: []*types.Tx{ | |
63 | + types.NewTx(types.TxData{ | |
64 | + Inputs: []*types.TxInput{ | |
65 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
66 | + }, | |
67 | + Outputs: []*types.TxOutput{ | |
68 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
69 | + }, | |
70 | + }), | |
71 | + }, | |
72 | + }, | |
73 | + { | |
74 | + BlockHeader: types.BlockHeader{ | |
75 | + Height: 1, | |
76 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
77 | + }, | |
78 | + Transactions: []*types.Tx{ | |
79 | + types.NewTx(types.TxData{ | |
80 | + Inputs: []*types.TxInput{ | |
81 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
82 | + }, | |
83 | + Outputs: []*types.TxOutput{ | |
84 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
85 | + }, | |
86 | + }), | |
87 | + }, | |
88 | + }, | |
89 | + }, | |
90 | + consensusResults: []*state.ConsensusResult{ | |
91 | + { | |
92 | + Seq: 1, | |
93 | + NumOfVote: map[string]uint64{ | |
94 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100002000, | |
95 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
96 | + }, | |
97 | + BlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
98 | + BlockHeight: 1, | |
99 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(1) + 10000000000}, | |
100 | + }, | |
101 | + { | |
102 | + Seq: 0, | |
103 | + NumOfVote: map[string]uint64{ | |
104 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
105 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
106 | + }, | |
107 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
108 | + BlockHeight: 0, | |
109 | + CoinbaseReward: map[string]uint64{}, | |
110 | + }, | |
111 | + }, | |
112 | + }, | |
113 | + wantChainData: &chainData{ | |
114 | + bestBlockHeader: &types.BlockHeader{ | |
115 | + Height: 0, | |
116 | + }, | |
117 | + lastIrrBlockHeader: &types.BlockHeader{ | |
118 | + Height: 0, | |
119 | + }, | |
120 | + storedBlocks: []*types.Block{ | |
121 | + { | |
122 | + BlockHeader: types.BlockHeader{ | |
123 | + Height: 0, | |
124 | + }, | |
125 | + Transactions: []*types.Tx{ | |
126 | + types.NewTx(types.TxData{ | |
127 | + Inputs: []*types.TxInput{ | |
128 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
129 | + }, | |
130 | + Outputs: []*types.TxOutput{ | |
131 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
132 | + }, | |
133 | + }), | |
134 | + }, | |
135 | + }, | |
136 | + }, | |
137 | + utxoViewPoint: &state.UtxoViewpoint{ | |
138 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
139 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
140 | + }, | |
141 | + }, | |
142 | + consensusResults: []*state.ConsensusResult{ | |
143 | + { | |
144 | + Seq: 0, | |
145 | + NumOfVote: map[string]uint64{ | |
146 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
147 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
148 | + }, | |
149 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
150 | + BlockHeight: 0, | |
151 | + CoinbaseReward: map[string]uint64{}, | |
152 | + }, | |
153 | + }, | |
154 | + }, | |
155 | + }, | |
156 | + { | |
157 | + desc: "rollback from height 2 to 0", | |
158 | + movStartHeight: 10, | |
159 | + RoundVoteBlockNums: 1200, | |
160 | + rollbackToTargetHeight: 0, | |
161 | + beforeChainData: &chainData{ | |
162 | + bestBlockHeader: &types.BlockHeader{ | |
163 | + Height: 2, | |
164 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
165 | + }, | |
166 | + lastIrrBlockHeader: &types.BlockHeader{ | |
167 | + Height: 2, | |
168 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
169 | + }, | |
170 | + utxoViewPoint: &state.UtxoViewpoint{ | |
171 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
172 | + testutil.MustDecodeHash("afee09925bea1695424450a91ad082a378f20534627fa5cb63f036846347ee08"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
173 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
174 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
175 | + }, | |
176 | + }, | |
177 | + storedBlocks: []*types.Block{ | |
178 | + { | |
179 | + BlockHeader: types.BlockHeader{ | |
180 | + Height: 0, | |
181 | + }, | |
182 | + Transactions: []*types.Tx{ | |
183 | + types.NewTx(types.TxData{ | |
184 | + Inputs: []*types.TxInput{ | |
185 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
186 | + }, | |
187 | + Outputs: []*types.TxOutput{ | |
188 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
189 | + }, | |
190 | + }), | |
191 | + }, | |
192 | + }, | |
193 | + { | |
194 | + BlockHeader: types.BlockHeader{ | |
195 | + Height: 1, | |
196 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
197 | + }, | |
198 | + Transactions: []*types.Tx{ | |
199 | + types.NewTx(types.TxData{ | |
200 | + Inputs: []*types.TxInput{ | |
201 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
202 | + }, | |
203 | + Outputs: []*types.TxOutput{ | |
204 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
205 | + }, | |
206 | + }), | |
207 | + }, | |
208 | + }, | |
209 | + { | |
210 | + BlockHeader: types.BlockHeader{ | |
211 | + Height: 2, | |
212 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
213 | + }, | |
214 | + Transactions: []*types.Tx{ | |
215 | + types.NewTx(types.TxData{ | |
216 | + Inputs: []*types.TxInput{ | |
217 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 3000, 0, []byte{0, 1}), | |
218 | + }, | |
219 | + Outputs: []*types.TxOutput{ | |
220 | + types.NewVoteOutput(*consensus.BTMAssetID, 2500, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
221 | + }, | |
222 | + }), | |
223 | + }, | |
224 | + }, | |
225 | + }, | |
226 | + consensusResults: []*state.ConsensusResult{ | |
227 | + { | |
228 | + Seq: 1, | |
229 | + NumOfVote: map[string]uint64{ | |
230 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100004500, | |
231 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
232 | + }, | |
233 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
234 | + BlockHeight: 2, | |
235 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(2) + 10000000000}, | |
236 | + }, | |
237 | + { | |
238 | + Seq: 0, | |
239 | + NumOfVote: map[string]uint64{ | |
240 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
241 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
242 | + }, | |
243 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
244 | + BlockHeight: 0, | |
245 | + CoinbaseReward: map[string]uint64{}, | |
246 | + }, | |
247 | + }, | |
248 | + }, | |
249 | + wantChainData: &chainData{ | |
250 | + bestBlockHeader: &types.BlockHeader{ | |
251 | + Height: 0, | |
252 | + }, | |
253 | + lastIrrBlockHeader: &types.BlockHeader{ | |
254 | + Height: 0, | |
255 | + }, | |
256 | + storedBlocks: []*types.Block{ | |
257 | + { | |
258 | + BlockHeader: types.BlockHeader{ | |
259 | + Height: 0, | |
260 | + }, | |
261 | + Transactions: []*types.Tx{ | |
262 | + types.NewTx(types.TxData{ | |
263 | + Inputs: []*types.TxInput{ | |
264 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
265 | + }, | |
266 | + Outputs: []*types.TxOutput{ | |
267 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
268 | + }, | |
269 | + }), | |
270 | + }, | |
271 | + }, | |
272 | + }, | |
273 | + utxoViewPoint: &state.UtxoViewpoint{ | |
274 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
275 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
276 | + }, | |
277 | + }, | |
278 | + consensusResults: []*state.ConsensusResult{ | |
279 | + { | |
280 | + Seq: 0, | |
281 | + NumOfVote: map[string]uint64{ | |
282 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
283 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
284 | + }, | |
285 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
286 | + BlockHeight: 0, | |
287 | + CoinbaseReward: map[string]uint64{}, | |
288 | + }, | |
289 | + }, | |
290 | + }, | |
291 | + }, | |
292 | + { | |
293 | + desc: "rollback from height 2 to 1", | |
294 | + movStartHeight: 10, | |
295 | + RoundVoteBlockNums: 1200, | |
296 | + rollbackToTargetHeight: 1, | |
297 | + beforeChainData: &chainData{ | |
298 | + bestBlockHeader: &types.BlockHeader{ | |
299 | + Height: 2, | |
300 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
301 | + }, | |
302 | + lastIrrBlockHeader: &types.BlockHeader{ | |
303 | + Height: 2, | |
304 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
305 | + }, | |
306 | + utxoViewPoint: &state.UtxoViewpoint{ | |
307 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
308 | + testutil.MustDecodeHash("afee09925bea1695424450a91ad082a378f20534627fa5cb63f036846347ee08"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
309 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
310 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
311 | + }, | |
312 | + }, | |
313 | + storedBlocks: []*types.Block{ | |
314 | + { | |
315 | + BlockHeader: types.BlockHeader{ | |
316 | + Height: 0, | |
317 | + }, | |
318 | + Transactions: []*types.Tx{ | |
319 | + types.NewTx(types.TxData{ | |
320 | + Inputs: []*types.TxInput{ | |
321 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
322 | + }, | |
323 | + Outputs: []*types.TxOutput{ | |
324 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
325 | + }, | |
326 | + }), | |
327 | + }, | |
328 | + }, | |
329 | + { | |
330 | + BlockHeader: types.BlockHeader{ | |
331 | + Height: 1, | |
332 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
333 | + }, | |
334 | + Transactions: []*types.Tx{ | |
335 | + types.NewTx(types.TxData{ | |
336 | + Inputs: []*types.TxInput{ | |
337 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
338 | + }, | |
339 | + Outputs: []*types.TxOutput{ | |
340 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
341 | + }, | |
342 | + }), | |
343 | + }, | |
344 | + }, | |
345 | + { | |
346 | + BlockHeader: types.BlockHeader{ | |
347 | + Height: 2, | |
348 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
349 | + }, | |
350 | + Transactions: []*types.Tx{ | |
351 | + types.NewTx(types.TxData{ | |
352 | + Inputs: []*types.TxInput{ | |
353 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 3000, 0, []byte{0, 1}), | |
354 | + }, | |
355 | + Outputs: []*types.TxOutput{ | |
356 | + types.NewVoteOutput(*consensus.BTMAssetID, 2500, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
357 | + }, | |
358 | + }), | |
359 | + }, | |
360 | + }, | |
361 | + }, | |
362 | + consensusResults: []*state.ConsensusResult{ | |
363 | + { | |
364 | + Seq: 1, | |
365 | + NumOfVote: map[string]uint64{ | |
366 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100004500, | |
367 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
368 | + }, | |
369 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
370 | + BlockHeight: 2, | |
371 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 500}, | |
372 | + }, | |
373 | + { | |
374 | + Seq: 0, | |
375 | + NumOfVote: map[string]uint64{ | |
376 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
377 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
378 | + }, | |
379 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
380 | + BlockHeight: 0, | |
381 | + CoinbaseReward: map[string]uint64{}, | |
382 | + }, | |
383 | + }, | |
384 | + }, | |
385 | + wantChainData: &chainData{ | |
386 | + bestBlockHeader: &types.BlockHeader{ | |
387 | + Height: 1, | |
388 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
389 | + }, | |
390 | + lastIrrBlockHeader: &types.BlockHeader{ | |
391 | + Height: 1, | |
392 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
393 | + }, | |
394 | + storedBlocks: []*types.Block{ | |
395 | + { | |
396 | + BlockHeader: types.BlockHeader{ | |
397 | + Height: 0, | |
398 | + }, | |
399 | + Transactions: []*types.Tx{ | |
400 | + types.NewTx(types.TxData{ | |
401 | + Inputs: []*types.TxInput{ | |
402 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
403 | + }, | |
404 | + Outputs: []*types.TxOutput{ | |
405 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
406 | + }, | |
407 | + }), | |
408 | + }, | |
409 | + }, | |
410 | + { | |
411 | + BlockHeader: types.BlockHeader{ | |
412 | + Height: 1, | |
413 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
414 | + }, | |
415 | + Transactions: []*types.Tx{ | |
416 | + types.NewTx(types.TxData{ | |
417 | + Inputs: []*types.TxInput{ | |
418 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
419 | + }, | |
420 | + Outputs: []*types.TxOutput{ | |
421 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
422 | + }, | |
423 | + }), | |
424 | + }, | |
425 | + }, | |
426 | + }, | |
427 | + utxoViewPoint: &state.UtxoViewpoint{ | |
428 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
429 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
430 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
431 | + }, | |
432 | + }, | |
433 | + consensusResults: []*state.ConsensusResult{ | |
434 | + { | |
435 | + Seq: 1, | |
436 | + NumOfVote: map[string]uint64{ | |
437 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100002000, | |
438 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
439 | + }, | |
440 | + BlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
441 | + BlockHeight: 1, | |
442 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(1)}, | |
443 | + }, | |
444 | + { | |
445 | + Seq: 0, | |
446 | + NumOfVote: map[string]uint64{ | |
447 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
448 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
449 | + }, | |
450 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
451 | + BlockHeight: 0, | |
452 | + CoinbaseReward: map[string]uint64{}, | |
453 | + }, | |
454 | + }, | |
455 | + }, | |
456 | + }, | |
457 | + { | |
458 | + desc: "rollback from height 2 to 1, RoundVoteBlockNums is 2", | |
459 | + movStartHeight: 10, | |
460 | + RoundVoteBlockNums: 2, | |
461 | + rollbackToTargetHeight: 1, | |
462 | + beforeChainData: &chainData{ | |
463 | + bestBlockHeader: &types.BlockHeader{ | |
464 | + Height: 2, | |
465 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
466 | + }, | |
467 | + lastIrrBlockHeader: &types.BlockHeader{ | |
468 | + Height: 2, | |
469 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
470 | + }, | |
471 | + utxoViewPoint: &state.UtxoViewpoint{ | |
472 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
473 | + testutil.MustDecodeHash("afee09925bea1695424450a91ad082a378f20534627fa5cb63f036846347ee08"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
474 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
475 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
476 | + }, | |
477 | + }, | |
478 | + storedBlocks: []*types.Block{ | |
479 | + { | |
480 | + BlockHeader: types.BlockHeader{ | |
481 | + Height: 0, | |
482 | + }, | |
483 | + Transactions: []*types.Tx{ | |
484 | + types.NewTx(types.TxData{ | |
485 | + Inputs: []*types.TxInput{ | |
486 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
487 | + }, | |
488 | + Outputs: []*types.TxOutput{ | |
489 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
490 | + }, | |
491 | + }), | |
492 | + }, | |
493 | + }, | |
494 | + { | |
495 | + BlockHeader: types.BlockHeader{ | |
496 | + Height: 1, | |
497 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
498 | + }, | |
499 | + Transactions: []*types.Tx{ | |
500 | + types.NewTx(types.TxData{ | |
501 | + Inputs: []*types.TxInput{ | |
502 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
503 | + }, | |
504 | + Outputs: []*types.TxOutput{ | |
505 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
506 | + }, | |
507 | + }), | |
508 | + }, | |
509 | + }, | |
510 | + { | |
511 | + BlockHeader: types.BlockHeader{ | |
512 | + Height: 2, | |
513 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
514 | + }, | |
515 | + Transactions: []*types.Tx{ | |
516 | + types.NewTx(types.TxData{ | |
517 | + Inputs: []*types.TxInput{ | |
518 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 3000, 0, []byte{0, 1}), | |
519 | + }, | |
520 | + Outputs: []*types.TxOutput{ | |
521 | + types.NewVoteOutput(*consensus.BTMAssetID, 2500, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
522 | + }, | |
523 | + }), | |
524 | + }, | |
525 | + }, | |
526 | + }, | |
527 | + consensusResults: []*state.ConsensusResult{ | |
528 | + { | |
529 | + Seq: 1, | |
530 | + NumOfVote: map[string]uint64{ | |
531 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100004500, | |
532 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
533 | + }, | |
534 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
535 | + BlockHeight: 2, | |
536 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 500}, | |
537 | + }, | |
538 | + { | |
539 | + Seq: 0, | |
540 | + NumOfVote: map[string]uint64{ | |
541 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
542 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
543 | + }, | |
544 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
545 | + BlockHeight: 0, | |
546 | + CoinbaseReward: map[string]uint64{}, | |
547 | + }, | |
548 | + }, | |
549 | + }, | |
550 | + wantChainData: &chainData{ | |
551 | + bestBlockHeader: &types.BlockHeader{ | |
552 | + Height: 1, | |
553 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
554 | + }, | |
555 | + lastIrrBlockHeader: &types.BlockHeader{ | |
556 | + Height: 1, | |
557 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
558 | + }, | |
559 | + storedBlocks: []*types.Block{ | |
560 | + { | |
561 | + BlockHeader: types.BlockHeader{ | |
562 | + Height: 0, | |
563 | + }, | |
564 | + Transactions: []*types.Tx{ | |
565 | + types.NewTx(types.TxData{ | |
566 | + Inputs: []*types.TxInput{ | |
567 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 1000, 0, []byte{0, 1}), | |
568 | + }, | |
569 | + Outputs: []*types.TxOutput{ | |
570 | + types.NewVoteOutput(*consensus.BTMAssetID, 1000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
571 | + }, | |
572 | + }), | |
573 | + }, | |
574 | + }, | |
575 | + { | |
576 | + BlockHeader: types.BlockHeader{ | |
577 | + Height: 1, | |
578 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
579 | + }, | |
580 | + Transactions: []*types.Tx{ | |
581 | + types.NewTx(types.TxData{ | |
582 | + Inputs: []*types.TxInput{ | |
583 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 2000, 0, []byte{0, 1}), | |
584 | + }, | |
585 | + Outputs: []*types.TxOutput{ | |
586 | + types.NewVoteOutput(*consensus.BTMAssetID, 2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
587 | + }, | |
588 | + }), | |
589 | + }, | |
590 | + }, | |
591 | + }, | |
592 | + utxoViewPoint: &state.UtxoViewpoint{ | |
593 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
594 | + testutil.MustDecodeHash("51f538be366172bed5359a016dce26b952024c9607caf6af609ad723982c2e06"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
595 | + testutil.MustDecodeHash("e2370262a129b90174195a76c298d872a56af042eae17657e154bcc46d41b3ba"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
596 | + }, | |
597 | + }, | |
598 | + consensusResults: []*state.ConsensusResult{ | |
599 | + { | |
600 | + Seq: 1, | |
601 | + NumOfVote: map[string]uint64{ | |
602 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100002000, | |
603 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
604 | + }, | |
605 | + BlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
606 | + BlockHeight: 1, | |
607 | + CoinbaseReward: map[string]uint64{"0001": consensus.BlockSubsidy(1)}, | |
608 | + }, | |
609 | + { | |
610 | + Seq: 0, | |
611 | + NumOfVote: map[string]uint64{ | |
612 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
613 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 100002000, | |
614 | + }, | |
615 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
616 | + BlockHeight: 0, | |
617 | + CoinbaseReward: map[string]uint64{}, | |
618 | + }, | |
619 | + }, | |
620 | + }, | |
621 | + }, | |
622 | + { | |
623 | + desc: "rollback from height 3 to 1, RoundVoteBlockNums is 2", | |
624 | + movStartHeight: 10, | |
625 | + RoundVoteBlockNums: 2, | |
626 | + rollbackToTargetHeight: 1, | |
627 | + beforeChainData: &chainData{ | |
628 | + bestBlockHeader: &types.BlockHeader{ | |
629 | + Height: 3, | |
630 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
631 | + }, | |
632 | + lastIrrBlockHeader: &types.BlockHeader{ | |
633 | + Height: 3, | |
634 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
635 | + }, | |
636 | + utxoViewPoint: &state.UtxoViewpoint{ | |
637 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
638 | + testutil.MustDecodeHash("5b4d53fbc2a489847f34dd0e0c085797fe7cf0a3a9a2f3231d11bdad16dea2be"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 3, Spent: true}, | |
639 | + testutil.MustDecodeHash("4c2b719d10fc6b9c2a7c343491ddd8c0d6bd57f9c6680bfda557689c182cf685"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
640 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
641 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
642 | + }, | |
643 | + }, | |
644 | + storedBlocks: []*types.Block{ | |
645 | + { | |
646 | + BlockHeader: types.BlockHeader{ | |
647 | + Height: 0, | |
648 | + }, | |
649 | + Transactions: []*types.Tx{ | |
650 | + types.NewTx(types.TxData{ | |
651 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
652 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
653 | + }), | |
654 | + types.NewTx(types.TxData{ | |
655 | + Inputs: []*types.TxInput{ | |
656 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
657 | + }, | |
658 | + Outputs: []*types.TxOutput{ | |
659 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
660 | + }, | |
661 | + }), | |
662 | + }, | |
663 | + }, | |
664 | + { | |
665 | + BlockHeader: types.BlockHeader{ | |
666 | + Height: 1, | |
667 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
668 | + }, | |
669 | + Transactions: []*types.Tx{ | |
670 | + types.NewTx(types.TxData{ | |
671 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
672 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
673 | + }), | |
674 | + types.NewTx(types.TxData{ | |
675 | + Inputs: []*types.TxInput{ | |
676 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
677 | + }, | |
678 | + Outputs: []*types.TxOutput{ | |
679 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
680 | + }, | |
681 | + }), | |
682 | + }, | |
683 | + }, | |
684 | + { | |
685 | + BlockHeader: types.BlockHeader{ | |
686 | + Height: 2, | |
687 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
688 | + }, | |
689 | + Transactions: []*types.Tx{ | |
690 | + types.NewTx(types.TxData{ | |
691 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
692 | + Outputs: []*types.TxOutput{ | |
693 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
694 | + }, | |
695 | + }), | |
696 | + types.NewTx(types.TxData{ | |
697 | + Inputs: []*types.TxInput{ | |
698 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 300000000, 0, []byte{0, 1}), | |
699 | + }, | |
700 | + Outputs: []*types.TxOutput{ | |
701 | + types.NewVoteOutput(*consensus.BTMAssetID, 250000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
702 | + }, | |
703 | + }), | |
704 | + }, | |
705 | + }, | |
706 | + { | |
707 | + BlockHeader: types.BlockHeader{ | |
708 | + Height: 3, | |
709 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
710 | + }, | |
711 | + Transactions: []*types.Tx{ | |
712 | + types.NewTx(types.TxData{ | |
713 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
714 | + Outputs: []*types.TxOutput{ | |
715 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
716 | + types.NewIntraChainOutput(bc.AssetID{}, consensus.BlockSubsidy(1)+consensus.BlockSubsidy(2)+50002000, []byte{0x51}), | |
717 | + }, | |
718 | + }), | |
719 | + types.NewTx(types.TxData{ | |
720 | + Inputs: []*types.TxInput{ | |
721 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 400000000, 0, []byte{0, 1}), | |
722 | + }, | |
723 | + Outputs: []*types.TxOutput{ | |
724 | + types.NewVoteOutput(*consensus.BTMAssetID, 160000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
725 | + }, | |
726 | + }), | |
727 | + }, | |
728 | + }, | |
729 | + }, | |
730 | + consensusResults: []*state.ConsensusResult{ | |
731 | + { | |
732 | + Seq: 2, | |
733 | + NumOfVote: map[string]uint64{ | |
734 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 609998000, | |
735 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
736 | + }, | |
737 | + BlockHash: testutil.MustDecodeHash("0c1cd1c0a6e6161f437c382cca21ce28921234ed7c4f252f7e4bbc9a523b74ac"), | |
738 | + BlockHeight: 3, | |
739 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(3) + 240000000}, | |
740 | + }, | |
741 | + { | |
742 | + Seq: 1, | |
743 | + NumOfVote: map[string]uint64{ | |
744 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 449998000, | |
745 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
746 | + }, | |
747 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
748 | + BlockHeight: 2, | |
749 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 50002000}, | |
750 | + }, | |
751 | + { | |
752 | + Seq: 0, | |
753 | + NumOfVote: map[string]uint64{ | |
754 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
755 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
756 | + }, | |
757 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
758 | + BlockHeight: 0, | |
759 | + CoinbaseReward: map[string]uint64{}, | |
760 | + }, | |
761 | + }, | |
762 | + }, | |
763 | + wantChainData: &chainData{ | |
764 | + bestBlockHeader: &types.BlockHeader{ | |
765 | + Height: 1, | |
766 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
767 | + }, | |
768 | + lastIrrBlockHeader: &types.BlockHeader{ | |
769 | + Height: 1, | |
770 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
771 | + }, | |
772 | + storedBlocks: []*types.Block{ | |
773 | + { | |
774 | + BlockHeader: types.BlockHeader{ | |
775 | + Height: 0, | |
776 | + }, | |
777 | + Transactions: []*types.Tx{ | |
778 | + types.NewTx(types.TxData{ | |
779 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
780 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
781 | + }), | |
782 | + types.NewTx(types.TxData{ | |
783 | + Inputs: []*types.TxInput{ | |
784 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
785 | + }, | |
786 | + Outputs: []*types.TxOutput{ | |
787 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
788 | + }, | |
789 | + }), | |
790 | + }, | |
791 | + }, | |
792 | + { | |
793 | + BlockHeader: types.BlockHeader{ | |
794 | + Height: 1, | |
795 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
796 | + }, | |
797 | + Transactions: []*types.Tx{ | |
798 | + types.NewTx(types.TxData{ | |
799 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
800 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
801 | + }), | |
802 | + types.NewTx(types.TxData{ | |
803 | + Inputs: []*types.TxInput{ | |
804 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
805 | + }, | |
806 | + Outputs: []*types.TxOutput{ | |
807 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
808 | + }, | |
809 | + }), | |
810 | + }, | |
811 | + }, | |
812 | + }, | |
813 | + utxoViewPoint: &state.UtxoViewpoint{ | |
814 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
815 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
816 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
817 | + }, | |
818 | + }, | |
819 | + consensusResults: []*state.ConsensusResult{ | |
820 | + { | |
821 | + Seq: 1, | |
822 | + NumOfVote: map[string]uint64{ | |
823 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000 + 100000000 - 2000, | |
824 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
825 | + }, | |
826 | + BlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
827 | + BlockHeight: 1, | |
828 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + 2000}, | |
829 | + }, | |
830 | + { | |
831 | + Seq: 0, | |
832 | + NumOfVote: map[string]uint64{ | |
833 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
834 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
835 | + }, | |
836 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
837 | + BlockHeight: 0, | |
838 | + CoinbaseReward: map[string]uint64{}, | |
839 | + }, | |
840 | + }, | |
841 | + }, | |
842 | + }, | |
843 | + { | |
844 | + desc: "rollback from height 3 to 2, RoundVoteBlockNums is 2", | |
845 | + movStartHeight: 10, | |
846 | + RoundVoteBlockNums: 2, | |
847 | + rollbackToTargetHeight: 2, | |
848 | + beforeChainData: &chainData{ | |
849 | + bestBlockHeader: &types.BlockHeader{ | |
850 | + Height: 3, | |
851 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
852 | + }, | |
853 | + lastIrrBlockHeader: &types.BlockHeader{ | |
854 | + Height: 3, | |
855 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
856 | + }, | |
857 | + utxoViewPoint: &state.UtxoViewpoint{ | |
858 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
859 | + testutil.MustDecodeHash("5b4d53fbc2a489847f34dd0e0c085797fe7cf0a3a9a2f3231d11bdad16dea2be"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 3, Spent: true}, | |
860 | + testutil.MustDecodeHash("4c2b719d10fc6b9c2a7c343491ddd8c0d6bd57f9c6680bfda557689c182cf685"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
861 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
862 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
863 | + }, | |
864 | + }, | |
865 | + storedBlocks: []*types.Block{ | |
866 | + { | |
867 | + BlockHeader: types.BlockHeader{ | |
868 | + Height: 0, | |
869 | + }, | |
870 | + Transactions: []*types.Tx{ | |
871 | + types.NewTx(types.TxData{ | |
872 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
873 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
874 | + }), | |
875 | + types.NewTx(types.TxData{ | |
876 | + Inputs: []*types.TxInput{ | |
877 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
878 | + }, | |
879 | + Outputs: []*types.TxOutput{ | |
880 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
881 | + }, | |
882 | + }), | |
883 | + }, | |
884 | + }, | |
885 | + { | |
886 | + BlockHeader: types.BlockHeader{ | |
887 | + Height: 1, | |
888 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
889 | + }, | |
890 | + Transactions: []*types.Tx{ | |
891 | + types.NewTx(types.TxData{ | |
892 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
893 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
894 | + }), | |
895 | + types.NewTx(types.TxData{ | |
896 | + Inputs: []*types.TxInput{ | |
897 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
898 | + }, | |
899 | + Outputs: []*types.TxOutput{ | |
900 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
901 | + }, | |
902 | + }), | |
903 | + }, | |
904 | + }, | |
905 | + { | |
906 | + BlockHeader: types.BlockHeader{ | |
907 | + Height: 2, | |
908 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
909 | + }, | |
910 | + Transactions: []*types.Tx{ | |
911 | + types.NewTx(types.TxData{ | |
912 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
913 | + Outputs: []*types.TxOutput{ | |
914 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
915 | + }, | |
916 | + }), | |
917 | + types.NewTx(types.TxData{ | |
918 | + Inputs: []*types.TxInput{ | |
919 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 300000000, 0, []byte{0, 1}), | |
920 | + }, | |
921 | + Outputs: []*types.TxOutput{ | |
922 | + types.NewVoteOutput(*consensus.BTMAssetID, 250000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
923 | + }, | |
924 | + }), | |
925 | + }, | |
926 | + }, | |
927 | + { | |
928 | + BlockHeader: types.BlockHeader{ | |
929 | + Height: 3, | |
930 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
931 | + }, | |
932 | + Transactions: []*types.Tx{ | |
933 | + types.NewTx(types.TxData{ | |
934 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
935 | + Outputs: []*types.TxOutput{ | |
936 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
937 | + types.NewIntraChainOutput(bc.AssetID{}, consensus.BlockSubsidy(1)+consensus.BlockSubsidy(2)+50002000, []byte{0x51}), | |
938 | + }, | |
939 | + }), | |
940 | + types.NewTx(types.TxData{ | |
941 | + Inputs: []*types.TxInput{ | |
942 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 400000000, 0, []byte{0, 1}), | |
943 | + }, | |
944 | + Outputs: []*types.TxOutput{ | |
945 | + types.NewVoteOutput(*consensus.BTMAssetID, 160000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
946 | + }, | |
947 | + }), | |
948 | + }, | |
949 | + }, | |
950 | + }, | |
951 | + consensusResults: []*state.ConsensusResult{ | |
952 | + { | |
953 | + Seq: 2, | |
954 | + NumOfVote: map[string]uint64{ | |
955 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 609998000, | |
956 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
957 | + }, | |
958 | + BlockHash: testutil.MustDecodeHash("0c1cd1c0a6e6161f437c382cca21ce28921234ed7c4f252f7e4bbc9a523b74ac"), | |
959 | + BlockHeight: 3, | |
960 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(3) + 240000000}, | |
961 | + }, | |
962 | + { | |
963 | + Seq: 1, | |
964 | + NumOfVote: map[string]uint64{ | |
965 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 449998000, | |
966 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
967 | + }, | |
968 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
969 | + BlockHeight: 2, | |
970 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 50002000}, | |
971 | + }, | |
972 | + { | |
973 | + Seq: 0, | |
974 | + NumOfVote: map[string]uint64{ | |
975 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
976 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
977 | + }, | |
978 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
979 | + BlockHeight: 0, | |
980 | + CoinbaseReward: map[string]uint64{}, | |
981 | + }, | |
982 | + }, | |
983 | + }, | |
984 | + wantChainData: &chainData{ | |
985 | + bestBlockHeader: &types.BlockHeader{ | |
986 | + Height: 2, | |
987 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
988 | + }, | |
989 | + lastIrrBlockHeader: &types.BlockHeader{ | |
990 | + Height: 2, | |
991 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
992 | + }, | |
993 | + storedBlocks: []*types.Block{ | |
994 | + { | |
995 | + BlockHeader: types.BlockHeader{ | |
996 | + Height: 0, | |
997 | + }, | |
998 | + Transactions: []*types.Tx{ | |
999 | + types.NewTx(types.TxData{ | |
1000 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1001 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1002 | + }), | |
1003 | + types.NewTx(types.TxData{ | |
1004 | + Inputs: []*types.TxInput{ | |
1005 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
1006 | + }, | |
1007 | + Outputs: []*types.TxOutput{ | |
1008 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
1009 | + }, | |
1010 | + }), | |
1011 | + }, | |
1012 | + }, | |
1013 | + { | |
1014 | + BlockHeader: types.BlockHeader{ | |
1015 | + Height: 1, | |
1016 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1017 | + }, | |
1018 | + Transactions: []*types.Tx{ | |
1019 | + types.NewTx(types.TxData{ | |
1020 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1021 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1022 | + }), | |
1023 | + types.NewTx(types.TxData{ | |
1024 | + Inputs: []*types.TxInput{ | |
1025 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
1026 | + }, | |
1027 | + Outputs: []*types.TxOutput{ | |
1028 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1029 | + }, | |
1030 | + }), | |
1031 | + }, | |
1032 | + }, | |
1033 | + { | |
1034 | + BlockHeader: types.BlockHeader{ | |
1035 | + Height: 2, | |
1036 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
1037 | + }, | |
1038 | + Transactions: []*types.Tx{ | |
1039 | + types.NewTx(types.TxData{ | |
1040 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1041 | + Outputs: []*types.TxOutput{ | |
1042 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1043 | + }, | |
1044 | + }), | |
1045 | + types.NewTx(types.TxData{ | |
1046 | + Inputs: []*types.TxInput{ | |
1047 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 300000000, 0, []byte{0, 1}), | |
1048 | + }, | |
1049 | + Outputs: []*types.TxOutput{ | |
1050 | + types.NewVoteOutput(*consensus.BTMAssetID, 250000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1051 | + }, | |
1052 | + }), | |
1053 | + }, | |
1054 | + }, | |
1055 | + }, | |
1056 | + utxoViewPoint: &state.UtxoViewpoint{ | |
1057 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
1058 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
1059 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
1060 | + testutil.MustDecodeHash("4c2b719d10fc6b9c2a7c343491ddd8c0d6bd57f9c6680bfda557689c182cf685"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
1061 | + }, | |
1062 | + }, | |
1063 | + consensusResults: []*state.ConsensusResult{ | |
1064 | + { | |
1065 | + Seq: 1, | |
1066 | + NumOfVote: map[string]uint64{ | |
1067 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000 + 100000000 - 2000 + 250000000, | |
1068 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1069 | + }, | |
1070 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
1071 | + BlockHeight: 2, | |
1072 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 50002000}, | |
1073 | + }, | |
1074 | + { | |
1075 | + Seq: 0, | |
1076 | + NumOfVote: map[string]uint64{ | |
1077 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
1078 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1079 | + }, | |
1080 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1081 | + BlockHeight: 0, | |
1082 | + CoinbaseReward: map[string]uint64{}, | |
1083 | + }, | |
1084 | + }, | |
1085 | + }, | |
1086 | + }, | |
1087 | + { | |
1088 | + desc: "rollback from height 4 to 2, there is two chain , and round vote block nums is 2", | |
1089 | + movStartHeight: 10, | |
1090 | + RoundVoteBlockNums: 2, | |
1091 | + rollbackToTargetHeight: 2, | |
1092 | + beforeChainData: &chainData{ | |
1093 | + bestBlockHeader: &types.BlockHeader{ | |
1094 | + Height: 5, | |
1095 | + Timestamp: uint64(1528945008), | |
1096 | + PreviousBlockHash: testutil.MustDecodeHash("64a41230412f26a5c0a1734515d9e177bd3573be2ae1d55c4533509a7c9cce8e"), | |
1097 | + }, | |
1098 | + lastIrrBlockHeader: &types.BlockHeader{ | |
1099 | + Height: 5, | |
1100 | + Timestamp: uint64(1528945008), | |
1101 | + PreviousBlockHash: testutil.MustDecodeHash("64a41230412f26a5c0a1734515d9e177bd3573be2ae1d55c4533509a7c9cce8e"), | |
1102 | + }, | |
1103 | + utxoViewPoint: &state.UtxoViewpoint{ | |
1104 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
1105 | + testutil.MustDecodeHash("3c07f3159d4e2a0527129d644a8fcd09ce26555e94c9c7f348464120ef463275"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 5, Spent: true}, | |
1106 | + testutil.MustDecodeHash("927144d2a391e17dc12184f5ae163b994984132ad72c34d854bb9009b68cd4cc"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 4, Spent: true}, | |
1107 | + testutil.MustDecodeHash("fa43f4ca43bcb0e94d43b52c56d1740dea1329b59a44f6ee045d70446881c514"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 3, Spent: true}, | |
1108 | + testutil.MustDecodeHash("f081ccd0c97ae34bc5580a0405d9b1ed0b0ed9e1410f1786b7112b348a412e3d"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 4, Spent: true}, | |
1109 | + testutil.MustDecodeHash("2704fa67c76e020b08ffa3f93a500acebcaf68b45ba43d8b3b08b68c5bb1eff1"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 3, Spent: true}, | |
1110 | + testutil.MustDecodeHash("4c2b719d10fc6b9c2a7c343491ddd8c0d6bd57f9c6680bfda557689c182cf685"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
1111 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
1112 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
1113 | + }, | |
1114 | + }, | |
1115 | + storedBlocks: []*types.Block{ | |
1116 | + { | |
1117 | + BlockHeader: types.BlockHeader{ | |
1118 | + Height: 0, | |
1119 | + }, | |
1120 | + Transactions: []*types.Tx{ | |
1121 | + types.NewTx(types.TxData{ | |
1122 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1123 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1124 | + }), | |
1125 | + types.NewTx(types.TxData{ | |
1126 | + Inputs: []*types.TxInput{ | |
1127 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
1128 | + }, | |
1129 | + Outputs: []*types.TxOutput{ | |
1130 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
1131 | + }, | |
1132 | + }), | |
1133 | + }, | |
1134 | + }, | |
1135 | + { | |
1136 | + BlockHeader: types.BlockHeader{ | |
1137 | + Height: 1, | |
1138 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1139 | + }, | |
1140 | + Transactions: []*types.Tx{ | |
1141 | + types.NewTx(types.TxData{ | |
1142 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1143 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1144 | + }), | |
1145 | + types.NewTx(types.TxData{ | |
1146 | + Inputs: []*types.TxInput{ | |
1147 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
1148 | + }, | |
1149 | + Outputs: []*types.TxOutput{ | |
1150 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1151 | + }, | |
1152 | + }), | |
1153 | + }, | |
1154 | + }, | |
1155 | + { | |
1156 | + BlockHeader: types.BlockHeader{ | |
1157 | + Height: 2, | |
1158 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
1159 | + }, | |
1160 | + Transactions: []*types.Tx{ | |
1161 | + types.NewTx(types.TxData{ | |
1162 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1163 | + Outputs: []*types.TxOutput{ | |
1164 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1165 | + }, | |
1166 | + }), | |
1167 | + types.NewTx(types.TxData{ | |
1168 | + Inputs: []*types.TxInput{ | |
1169 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 300000000, 0, []byte{0, 1}), | |
1170 | + }, | |
1171 | + Outputs: []*types.TxOutput{ | |
1172 | + types.NewVoteOutput(*consensus.BTMAssetID, 250000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1173 | + }, | |
1174 | + }), | |
1175 | + }, | |
1176 | + }, | |
1177 | + { | |
1178 | + BlockHeader: types.BlockHeader{ | |
1179 | + Height: 3, | |
1180 | + Timestamp: uint64(1528945000), | |
1181 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
1182 | + }, | |
1183 | + Transactions: []*types.Tx{ | |
1184 | + types.NewTx(types.TxData{ | |
1185 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1186 | + Outputs: []*types.TxOutput{ | |
1187 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1188 | + types.NewIntraChainOutput(bc.AssetID{}, consensus.BlockSubsidy(1)+consensus.BlockSubsidy(2)+50002000, []byte{0x51}), | |
1189 | + }, | |
1190 | + }), | |
1191 | + types.NewTx(types.TxData{ | |
1192 | + Inputs: []*types.TxInput{ | |
1193 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 440000000, 0, []byte{0, 1}), | |
1194 | + }, | |
1195 | + Outputs: []*types.TxOutput{ | |
1196 | + types.NewVoteOutput(*consensus.BTMAssetID, 160000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1197 | + }, | |
1198 | + }), | |
1199 | + }, | |
1200 | + }, | |
1201 | + { | |
1202 | + BlockHeader: types.BlockHeader{ | |
1203 | + Height: 4, | |
1204 | + Timestamp: uint64(1528945005), | |
1205 | + PreviousBlockHash: testutil.MustDecodeHash("bec3dd0d6fecb80a6f3a0373ec2ae676cc1ce72af83546f3d4672231c9b080e6"), | |
1206 | + }, | |
1207 | + Transactions: []*types.Tx{ | |
1208 | + types.NewTx(types.TxData{ | |
1209 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1210 | + Outputs: []*types.TxOutput{ | |
1211 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1212 | + }, | |
1213 | + }), | |
1214 | + types.NewTx(types.TxData{ | |
1215 | + Inputs: []*types.TxInput{ | |
1216 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 500000000, 0, []byte{0, 1}), | |
1217 | + }, | |
1218 | + Outputs: []*types.TxOutput{ | |
1219 | + types.NewVoteOutput(*consensus.BTMAssetID, 160000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1220 | + }, | |
1221 | + }), | |
1222 | + }, | |
1223 | + }, | |
1224 | + { | |
1225 | + BlockHeader: types.BlockHeader{ | |
1226 | + Height: 3, | |
1227 | + Timestamp: uint64(1528945001), | |
1228 | + PreviousBlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
1229 | + }, | |
1230 | + Transactions: []*types.Tx{ | |
1231 | + types.NewTx(types.TxData{ | |
1232 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1233 | + Outputs: []*types.TxOutput{ | |
1234 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1235 | + types.NewIntraChainOutput(bc.AssetID{}, consensus.BlockSubsidy(1)+consensus.BlockSubsidy(2)+50002000, []byte{0x51}), | |
1236 | + }, | |
1237 | + }), | |
1238 | + types.NewTx(types.TxData{ | |
1239 | + Inputs: []*types.TxInput{ | |
1240 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 402000000, 0, []byte{0, 1}), | |
1241 | + }, | |
1242 | + Outputs: []*types.TxOutput{ | |
1243 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1244 | + }, | |
1245 | + }), | |
1246 | + }, | |
1247 | + }, | |
1248 | + { | |
1249 | + BlockHeader: types.BlockHeader{ | |
1250 | + Height: 4, | |
1251 | + Timestamp: uint64(1528945006), | |
1252 | + PreviousBlockHash: testutil.MustDecodeHash("1d2d01a97d1239de51b4e7d0fb522f71771d2d4f9a0a559154519859cc44a230"), | |
1253 | + }, | |
1254 | + Transactions: []*types.Tx{ | |
1255 | + types.NewTx(types.TxData{ | |
1256 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1257 | + Outputs: []*types.TxOutput{ | |
1258 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1259 | + }, | |
1260 | + }), | |
1261 | + types.NewTx(types.TxData{ | |
1262 | + Inputs: []*types.TxInput{ | |
1263 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 410000000, 0, []byte{0, 1}), | |
1264 | + }, | |
1265 | + Outputs: []*types.TxOutput{ | |
1266 | + types.NewVoteOutput(*consensus.BTMAssetID, 170000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1267 | + }, | |
1268 | + }), | |
1269 | + }, | |
1270 | + }, | |
1271 | + { | |
1272 | + BlockHeader: types.BlockHeader{ | |
1273 | + Height: 5, | |
1274 | + Timestamp: uint64(1528945008), | |
1275 | + PreviousBlockHash: testutil.MustDecodeHash("64a41230412f26a5c0a1734515d9e177bd3573be2ae1d55c4533509a7c9cce8e"), | |
1276 | + }, | |
1277 | + Transactions: []*types.Tx{ | |
1278 | + types.NewTx(types.TxData{ | |
1279 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1280 | + Outputs: []*types.TxOutput{ | |
1281 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1282 | + types.NewIntraChainOutput(bc.AssetID{}, consensus.BlockSubsidy(3)+consensus.BlockSubsidy(4)+520000000, []byte{0x51}), | |
1283 | + }, | |
1284 | + }), | |
1285 | + types.NewTx(types.TxData{ | |
1286 | + Inputs: []*types.TxInput{ | |
1287 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 400004000, 0, []byte{0, 1}), | |
1288 | + }, | |
1289 | + Outputs: []*types.TxOutput{ | |
1290 | + types.NewVoteOutput(*consensus.BTMAssetID, 160004000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1291 | + }, | |
1292 | + }), | |
1293 | + }, | |
1294 | + }, | |
1295 | + }, | |
1296 | + consensusResults: []*state.ConsensusResult{ | |
1297 | + { | |
1298 | + Seq: 3, | |
1299 | + NumOfVote: map[string]uint64{ | |
1300 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 980002000, | |
1301 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1302 | + }, | |
1303 | + BlockHash: testutil.MustDecodeHash("075ce54f7d4c1b524474265219be52238beec98138f0c0a4d21f1a6b0047914a"), | |
1304 | + BlockHeight: 5, | |
1305 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(5) + 240000000}, | |
1306 | + }, | |
1307 | + { | |
1308 | + Seq: 2, | |
1309 | + NumOfVote: map[string]uint64{ | |
1310 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 819998000, | |
1311 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1312 | + }, | |
1313 | + BlockHash: testutil.MustDecodeHash("64a41230412f26a5c0a1734515d9e177bd3573be2ae1d55c4533509a7c9cce8e"), | |
1314 | + BlockHeight: 4, | |
1315 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(3) + consensus.BlockSubsidy(4) + 442000000}, | |
1316 | + }, | |
1317 | + { | |
1318 | + Seq: 1, | |
1319 | + NumOfVote: map[string]uint64{ | |
1320 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 449998000, | |
1321 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1322 | + }, | |
1323 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
1324 | + BlockHeight: 2, | |
1325 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 50002000}, | |
1326 | + }, | |
1327 | + { | |
1328 | + Seq: 0, | |
1329 | + NumOfVote: map[string]uint64{ | |
1330 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
1331 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1332 | + }, | |
1333 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1334 | + BlockHeight: 0, | |
1335 | + CoinbaseReward: map[string]uint64{}, | |
1336 | + }, | |
1337 | + }, | |
1338 | + }, | |
1339 | + wantChainData: &chainData{ | |
1340 | + bestBlockHeader: &types.BlockHeader{ | |
1341 | + Height: 2, | |
1342 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
1343 | + }, | |
1344 | + lastIrrBlockHeader: &types.BlockHeader{ | |
1345 | + Height: 2, | |
1346 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
1347 | + }, | |
1348 | + storedBlocks: []*types.Block{ | |
1349 | + { | |
1350 | + BlockHeader: types.BlockHeader{ | |
1351 | + Height: 0, | |
1352 | + }, | |
1353 | + Transactions: []*types.Tx{ | |
1354 | + types.NewTx(types.TxData{ | |
1355 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1356 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1357 | + }), | |
1358 | + types.NewTx(types.TxData{ | |
1359 | + Inputs: []*types.TxInput{ | |
1360 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 100000000, 0, []byte{0, 1}), | |
1361 | + }, | |
1362 | + Outputs: []*types.TxOutput{ | |
1363 | + types.NewVoteOutput(*consensus.BTMAssetID, 100000000, []byte{0, 1}, testutil.MustDecodeHexString("36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67")), | |
1364 | + }, | |
1365 | + }), | |
1366 | + }, | |
1367 | + }, | |
1368 | + { | |
1369 | + BlockHeader: types.BlockHeader{ | |
1370 | + Height: 1, | |
1371 | + PreviousBlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1372 | + }, | |
1373 | + Transactions: []*types.Tx{ | |
1374 | + types.NewTx(types.TxData{ | |
1375 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1376 | + Outputs: []*types.TxOutput{types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51})}, | |
1377 | + }), | |
1378 | + types.NewTx(types.TxData{ | |
1379 | + Inputs: []*types.TxInput{ | |
1380 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 200000000, 0, []byte{0, 1}), | |
1381 | + }, | |
1382 | + Outputs: []*types.TxOutput{ | |
1383 | + types.NewVoteOutput(*consensus.BTMAssetID, 200000000-2000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1384 | + }, | |
1385 | + }), | |
1386 | + }, | |
1387 | + }, | |
1388 | + { | |
1389 | + BlockHeader: types.BlockHeader{ | |
1390 | + Height: 2, | |
1391 | + PreviousBlockHash: testutil.MustDecodeHash("52463075c66259098f2a1fa711288cf3b866d7c57b4a7a78cd22a1dcd69a0514"), | |
1392 | + }, | |
1393 | + Transactions: []*types.Tx{ | |
1394 | + types.NewTx(types.TxData{ | |
1395 | + Inputs: []*types.TxInput{types.NewCoinbaseInput([]byte{0x01})}, | |
1396 | + Outputs: []*types.TxOutput{ | |
1397 | + types.NewIntraChainOutput(bc.AssetID{}, 0, []byte{0x51}), | |
1398 | + }, | |
1399 | + }), | |
1400 | + types.NewTx(types.TxData{ | |
1401 | + Inputs: []*types.TxInput{ | |
1402 | + types.NewSpendInput(nil, bc.NewHash([32]byte{8}), *consensus.BTMAssetID, 300000000, 0, []byte{0, 1}), | |
1403 | + }, | |
1404 | + Outputs: []*types.TxOutput{ | |
1405 | + types.NewVoteOutput(*consensus.BTMAssetID, 250000000, []byte{0, 1}, testutil.MustDecodeHexString("b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9")), | |
1406 | + }, | |
1407 | + }), | |
1408 | + }, | |
1409 | + }, | |
1410 | + }, | |
1411 | + consensusResults: []*state.ConsensusResult{ | |
1412 | + { | |
1413 | + Seq: 1, | |
1414 | + NumOfVote: map[string]uint64{ | |
1415 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000 + 100000000 - 2000 + 250000000, | |
1416 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1417 | + }, | |
1418 | + BlockHash: testutil.MustDecodeHash("699d3f59d4afe7eea85df31814628d7d34ace7f5e76d6c9ebf4c54482d2cd333"), | |
1419 | + BlockHeight: 2, | |
1420 | + CoinbaseReward: map[string]uint64{"51": consensus.BlockSubsidy(1) + consensus.BlockSubsidy(2) + 50002000}, | |
1421 | + }, | |
1422 | + { | |
1423 | + Seq: 0, | |
1424 | + NumOfVote: map[string]uint64{ | |
1425 | + "b7f463446a31b3792cd168d52b7a89b3657bca3e25d6854db1488c389ab6fc8d538155c25c1ee6975cc7def19710908c7d9b7463ca34a22058b456b45e498db9": 100000000, | |
1426 | + "36695997983028c279c3360ca345a90e3af1f9e3df2506119fca31cdc844be31630f9a421f4d1658e15d67a15ce29c36332dd45020d2a0147fcce4949ccd9a67": 200000000, | |
1427 | + }, | |
1428 | + BlockHash: testutil.MustDecodeHash("39dee75363127a2857f554d2ad2706eb876407a2e09fbe0338683ca4ad4c2f90"), | |
1429 | + BlockHeight: 0, | |
1430 | + CoinbaseReward: map[string]uint64{}, | |
1431 | + }, | |
1432 | + }, | |
1433 | + utxoViewPoint: &state.UtxoViewpoint{ | |
1434 | + Entries: map[bc.Hash]*storage.UtxoEntry{ | |
1435 | + testutil.MustDecodeHash("9fb6f213e3130810e755675707d0e9870c79a91c575638a580fae65568ca9e99"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 1, Spent: true}, | |
1436 | + testutil.MustDecodeHash("3d1617908e624a2042c23be4f671b261d5b8a2a61b8421ee6a702c6e071428a8"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 0, Spent: true}, | |
1437 | + testutil.MustDecodeHash("4c2b719d10fc6b9c2a7c343491ddd8c0d6bd57f9c6680bfda557689c182cf685"): &storage.UtxoEntry{Type: storage.VoteUTXOType, BlockHeight: 2, Spent: true}, | |
1438 | + }, | |
1439 | + }, | |
1440 | + }, | |
1441 | + }, | |
1442 | + } | |
1443 | + | |
1444 | + for i, c := range cases { | |
1445 | + consensus.ActiveNetParams.RoundVoteBlockNums = c.RoundVoteBlockNums | |
1446 | + | |
1447 | + movDB := dbm.NewDB("mov_db", "leveldb", "mov_db") | |
1448 | + movCore := mov.NewCoreWithDB(movDatabase.NewLevelDBMovStore(movDB), c.movStartHeight) | |
1449 | + | |
1450 | + blockDB := dbm.NewDB("block_db", "leveldb", "block_db") | |
1451 | + store := database.NewStore(blockDB) | |
1452 | + | |
1453 | + mustSaveBlocks(c.beforeChainData.storedBlocks, store) | |
1454 | + | |
1455 | + var mainChainBlockHeaders []*types.BlockHeader | |
1456 | + for _, block := range c.beforeChainData.storedBlocks { | |
1457 | + mainChainBlockHeaders = append(mainChainBlockHeaders, &block.BlockHeader) | |
1458 | + } | |
1459 | + if err := store.SaveChainStatus(c.beforeChainData.bestBlockHeader, c.beforeChainData.lastIrrBlockHeader, mainChainBlockHeaders, c.beforeChainData.utxoViewPoint, c.beforeChainData.consensusResults); err != nil { | |
1460 | + t.Fatal(err) | |
1461 | + } | |
1462 | + | |
1463 | + chain, err := protocol.NewChain(store, nil, []protocol.Protocoler{movCore}, nil) | |
1464 | + if err != nil { | |
1465 | + t.Fatal(err) | |
1466 | + } | |
1467 | + | |
1468 | + if err := chain.Rollback(c.rollbackToTargetHeight); err != nil { | |
1469 | + t.Fatal(err) | |
1470 | + } | |
1471 | + | |
1472 | + if !testutil.DeepEqual(chain.LastIrreversibleHeader(), c.wantChainData.lastIrrBlockHeader) { | |
1473 | + t.Errorf("lastIrrBlockHeader is not right!") | |
1474 | + } | |
1475 | + | |
1476 | + if !testutil.DeepEqual(chain.BestBlockHeader(), c.wantChainData.bestBlockHeader) { | |
1477 | + t.Errorf("wantBestBlockHeader is not right!") | |
1478 | + } | |
1479 | + | |
1480 | + gotConsensusResults := mustGetConsensusResultFromStore(store, chain) | |
1481 | + if !testutil.DeepEqual(gotConsensusResults, c.wantChainData.consensusResults) { | |
1482 | + t.Errorf("cases#%d(%s) wantBestConsensusResult is not right!", i, c.desc) | |
1483 | + } | |
1484 | + | |
1485 | + gotBlocks := mustGetBlocksFromStore(chain) | |
1486 | + if !blocksEquals(gotBlocks, c.wantChainData.storedBlocks) { | |
1487 | + t.Errorf("cases#%d(%s) the blocks is not same!", i, c.desc) | |
1488 | + } | |
1489 | + | |
1490 | + gotTransactions := getBcTransactions(gotBlocks) | |
1491 | + gotUtxoViewPoint := state.NewUtxoViewpoint() | |
1492 | + if err = store.GetTransactionsUtxo(gotUtxoViewPoint, gotTransactions); err != nil { | |
1493 | + t.Fatal(err) | |
1494 | + } | |
1495 | + | |
1496 | + if !testutil.DeepEqual(gotUtxoViewPoint, c.wantChainData.utxoViewPoint) { | |
1497 | + t.Fatal(err) | |
1498 | + } | |
1499 | + | |
1500 | + blockDB.Close() | |
1501 | + os.RemoveAll("block_db") | |
1502 | + movDB.Close() | |
1503 | + os.RemoveAll("mov_db") | |
1504 | + | |
1505 | + } | |
1506 | +} | |
1507 | + | |
1508 | +func blocksEquals(blocks1 []*types.Block, blocks2 []*types.Block) bool { | |
1509 | + blockHashMap1 := make(map[string]interface{}) | |
1510 | + for _, block := range blocks1 { | |
1511 | + hash := block.Hash() | |
1512 | + blockHashMap1[hash.String()] = nil | |
1513 | + } | |
1514 | + | |
1515 | + blockHashMap2 := make(map[string]interface{}) | |
1516 | + for _, block := range blocks2 { | |
1517 | + hash := block.Hash() | |
1518 | + blockHashMap2[hash.String()] = nil | |
1519 | + } | |
1520 | + return testutil.DeepEqual(blockHashMap1, blockHashMap2) | |
1521 | +} | |
1522 | + | |
1523 | +func getBcTransactions(blocks []*types.Block) []*bc.Tx { | |
1524 | + var txs []*bc.Tx | |
1525 | + for _, block := range blocks { | |
1526 | + for _, tx := range block.Transactions { | |
1527 | + txs = append(txs, tx.Tx) | |
1528 | + } | |
1529 | + } | |
1530 | + return txs | |
1531 | +} | |
1532 | + | |
1533 | +func mustSaveBlocks(blocks []*types.Block, store *database.Store) { | |
1534 | + for _, block := range blocks { | |
1535 | + status := bc.NewTransactionStatus() | |
1536 | + for index := range block.Transactions { | |
1537 | + if err := status.SetStatus(index, false); err != nil { | |
1538 | + panic(err) | |
1539 | + } | |
1540 | + } | |
1541 | + if err := store.SaveBlock(block, status); err != nil { | |
1542 | + panic(err) | |
1543 | + } | |
1544 | + } | |
1545 | +} | |
1546 | + | |
1547 | +func mustGetBlocksFromStore(chain *protocol.Chain) []*types.Block { | |
1548 | + var blocks []*types.Block | |
1549 | + for height := int64(chain.BestBlockHeight()); height >= 0; height-- { | |
1550 | + block, err := chain.GetBlockByHeight(uint64(height)) | |
1551 | + if err != nil { | |
1552 | + panic(err) | |
1553 | + } | |
1554 | + | |
1555 | + blocks = append(blocks, block) | |
1556 | + } | |
1557 | + return blocks | |
1558 | +} | |
1559 | + | |
1560 | +func mustGetConsensusResultFromStore(store *database.Store, chain *protocol.Chain) []*state.ConsensusResult { | |
1561 | + var consensusResults []*state.ConsensusResult | |
1562 | + for seq := int64(state.CalcVoteSeq(chain.BestBlockHeight())); seq >= 0; seq-- { | |
1563 | + consensusResult, err := store.GetConsensusResult(uint64(seq)) | |
1564 | + if err != nil { | |
1565 | + panic(err) | |
1566 | + } | |
1567 | + | |
1568 | + consensusResults = append(consensusResults, consensusResult) | |
1569 | + } | |
1570 | + return consensusResults | |
1571 | +} |
@@ -47,7 +47,7 @@ const ( | ||
47 | 47 | |
48 | 48 | var ( |
49 | 49 | // The full version string |
50 | - Version = "1.0.4" | |
50 | + Version = "1.1.0" | |
51 | 51 | // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)" |
52 | 52 | GitCommit string |
53 | 53 | Status *UpdateStatus |