import (
"encoding/hex"
"fmt"
+ "math/big"
"github.com/bytom/vapor/consensus/segwit"
"github.com/bytom/vapor/errors"
// Order store all the order information
type Order struct {
- FromAssetID *bc.AssetID
- ToAssetID *bc.AssetID
- Utxo *MovUtxo
- Rate float64
+ FromAssetID *bc.AssetID
+ ToAssetID *bc.AssetID
+ Utxo *MovUtxo
+ RatioNumerator int64
+ RatioDenominator int64
+}
+
+func (o *Order) Rate() float64 {
+ if o.RatioDenominator == 0 {
+ return 0
+ }
+ rate := big.NewFloat(0).SetInt64(o.RatioNumerator)
+ rate.Quo(rate, big.NewFloat(0).SetInt64(o.RatioDenominator))
+ result, _ := rate.Float64()
+ return result
}
// OrderSlice is define for order's sort
func (o OrderSlice) Len() int { return len(o) }
func (o OrderSlice) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func (o OrderSlice) Less(i, j int) bool {
- if o[i].Rate == o[j].Rate {
+ if o[i].Rate() == o[j].Rate() {
return hex.EncodeToString(o[i].UTXOHash().Bytes()) < hex.EncodeToString(o[j].UTXOHash().Bytes())
}
- return o[i].Rate < o[j].Rate
+ return o[i].Rate() < o[j].Rate()
}
// NewOrderFromOutput convert txinput to order
assetAmount := output.Source.Value
return &Order{
- FromAssetID: assetAmount.AssetId,
- ToAssetID: &contractArgs.RequestedAsset,
- Rate: float64(contractArgs.RatioNumerator) / float64(contractArgs.RatioDenominator),
+ FromAssetID: assetAmount.AssetId,
+ ToAssetID: &contractArgs.RequestedAsset,
+ RatioNumerator: contractArgs.RatioNumerator,
+ RatioDenominator: contractArgs.RatioDenominator,
Utxo: &MovUtxo{
SourceID: output.Source.Ref,
Amount: assetAmount.Amount,
}
return &Order{
- FromAssetID: input.AssetId,
- ToAssetID: &contractArgs.RequestedAsset,
- Rate: float64(contractArgs.RatioNumerator) / float64(contractArgs.RatioDenominator),
+ FromAssetID: input.AssetId,
+ ToAssetID: &contractArgs.RequestedAsset,
+ RatioNumerator: contractArgs.RatioNumerator,
+ RatioDenominator: contractArgs.RatioDenominator,
Utxo: &MovUtxo{
SourceID: &input.SourceID,
Amount: input.Amount,
asset3 = bc.NewAssetID([32]byte{3})
asset4 = bc.NewAssetID([32]byte{4})
- order1 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.1}
- order2 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.2}
- order3 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.3}
- order4 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.4}
- order5 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0.5}
+ order1 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 1, RatioDenominator: 10}
+ order2 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 2, RatioDenominator: 10}
+ order3 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 3, RatioDenominator: 10}
+ order4 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 4, RatioDenominator: 10}
+ order5 = &common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 5, RatioDenominator: 10}
)
func TestTradePairIterator(t *testing.T) {
bestMatchStore = append(movStore, matchStatus)
)
+type orderData struct {
+ Utxo *common.MovUtxo
+ RatioNumerator int64
+ RatioDenominator int64
+}
+
func calcOrderKey(fromAssetID, toAssetID *bc.AssetID, utxoHash *bc.Hash, rate float64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, math.Float64bits(rate))
var startKey []byte
- if orderAfter.Rate > 0 {
- startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate)
+ if orderAfter.Rate() > 0 {
+ startKey = calcOrderKey(orderAfter.FromAssetID, orderAfter.ToAssetID, orderAfter.UTXOHash(), orderAfter.Rate())
}
itr := m.db.IteratorPrefixWithStart(orderPrefix, startKey, false)
var orders []*common.Order
for txNum := 0; txNum < ordersNum && itr.Next(); txNum++ {
- movUtxo := &common.MovUtxo{}
- if err := json.Unmarshal(itr.Value(), movUtxo); err != nil {
+ orderData := &orderData{}
+ if err := json.Unmarshal(itr.Value(), orderData); err != nil {
return nil, err
}
orders = append(orders, &common.Order{
- FromAssetID: orderAfter.FromAssetID,
- ToAssetID: orderAfter.ToAssetID,
- Rate: getRateFromOrderKey(itr.Key()),
- Utxo: movUtxo,
+ FromAssetID: orderAfter.FromAssetID,
+ ToAssetID: orderAfter.ToAssetID,
+ Utxo: orderData.Utxo,
+ RatioNumerator: orderData.RatioNumerator,
+ RatioDenominator: orderData.RatioDenominator,
})
}
return orders, nil
func (m *LevelDBMovStore) addOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) error {
for _, order := range orders {
- data, err := json.Marshal(order.Utxo)
+ orderData := &orderData{
+ Utxo: order.Utxo,
+ RatioNumerator: order.RatioNumerator,
+ RatioDenominator: order.RatioDenominator,
+ }
+ data, err := json.Marshal(orderData)
if err != nil {
return err
}
- key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate)
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
batch.Set(key, data)
tradePair := &common.TradePair{
func (m *LevelDBMovStore) deleteOrders(batch dbm.Batch, orders []*common.Order, tradePairsCnt map[string]*common.TradePair) {
for _, order := range orders {
- key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate)
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
batch.Delete(key)
tradePair := &common.TradePair{
mockOrders = []*common.Order{
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 1.00090,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 100090,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 21},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00090,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 90,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 22},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00097,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 97,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 23},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00098,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 13},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00098,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 24},
Amount: 10,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00099,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 99,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 24},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00096,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 96,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 25},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00095,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 95,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 26},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 1.00090,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 90,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 1},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID1,
- ToAssetID: assetID2,
- Rate: 0.00090,
+ FromAssetID: assetID1,
+ ToAssetID: assetID2,
+ RatioNumerator: 90,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 2},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID3,
- ToAssetID: assetID2,
- Rate: 0.00096,
+ FromAssetID: assetID3,
+ ToAssetID: assetID2,
+ RatioNumerator: 96,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 33},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID4,
- ToAssetID: assetID2,
- Rate: 0.00095,
+ FromAssetID: assetID4,
+ ToAssetID: assetID2,
+ RatioNumerator: 95,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 34},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID4,
- ToAssetID: assetID2,
- Rate: 0.00096,
+ FromAssetID: assetID4,
+ ToAssetID: assetID2,
+ RatioNumerator: 96,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 36},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID5,
- ToAssetID: assetID2,
- Rate: 0.00096,
+ FromAssetID: assetID5,
+ ToAssetID: assetID2,
+ RatioNumerator: 96,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 37},
Amount: 1,
},
},
&common.Order{
- FromAssetID: assetID6,
- ToAssetID: assetID2,
- Rate: 0.00098,
+ FromAssetID: assetID6,
+ ToAssetID: assetID2,
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 38},
Amount: 1,
{
orders: []*common.Order{
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 1.00090,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 100090,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 21},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00090,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 90,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 22},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00097,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 97,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 23},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00098,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 13},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00098,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 24},
Amount: 10,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00098,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 25},
Amount: 10,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00098,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 26},
Amount: 10,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00098,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 98,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 27},
Amount: 10,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00099,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 99,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 24},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00096,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 96,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 25},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00095,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 95,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 26},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00091,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 91,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 26},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00092,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 92,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 27},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00093,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 93,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 28},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00094,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 94,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 29},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00077,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 77,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 30},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 0.00088,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 88,
+ RatioDenominator: 100000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 31},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 999999.9521,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 9999999521,
+ RatioDenominator: 10000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 32},
Amount: 1,
},
},
&common.Order{
- FromAssetID: &bc.AssetID{V0: 1},
- ToAssetID: &bc.AssetID{V0: 0},
- Rate: 888888.7954,
+ FromAssetID: &bc.AssetID{V0: 1},
+ ToAssetID: &bc.AssetID{V0: 0},
+ RatioNumerator: 8888887954,
+ RatioDenominator: 10000,
Utxo: &common.MovUtxo{
SourceID: &bc.Hash{V0: 33},
Amount: 1,
for i, c := range cases {
for _, order := range c.orders {
- key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate)
+ key := calcOrderKey(order.FromAssetID, order.ToAssetID, order.UTXOHash(), order.Rate())
data, err := json.Marshal(order.Utxo)
if err != nil {
t.Fatal(err)
wantDBState: &common.MovDatabaseState{Height: 2, Hash: &bc.Hash{V0: 3724755213446347384, V1: 158878632373345042, V2: 18283800951484248781, V3: 7520797730449067221}},
},
{
- desc: "Add and delete the same trade pair", //Add and delete different transaction pairs
+ desc: "Add and delete the same trade pair", // Add and delete different transaction pairs
beforeOrders: []*common.Order{
mockOrders[0],
mockOrders[1],
var gotOrders []*common.Order
- tmp, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2, Rate: 0})
+ tmp, err := movStore.ListOrders(&common.Order{FromAssetID: assetID1, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
if err != nil {
t.Fatalf("case %d: ListOrders(assetID1 and assetID2) error %v.", i, err)
}
gotOrders = append(gotOrders, tmp...)
- tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID3, ToAssetID: assetID2, Rate: 0})
+ tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID3, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
if err != nil {
t.Fatalf("case %d: ListOrders(assetID3 and assetID2) error %v.", i, err)
}
gotOrders = append(gotOrders, tmp...)
- tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID4, ToAssetID: assetID2, Rate: 0})
+ tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID4, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
if err != nil {
t.Fatalf("case %d: ListOrders(assetID4 and assetID2) error %v.", i, err)
}
gotOrders = append(gotOrders, tmp...)
- tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID5, ToAssetID: assetID2, Rate: 0})
+ tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID5, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
if err != nil {
t.Fatalf("case %d: ListOrders(assetID5 and assetID2) error %v.", i, err)
}
gotOrders = append(gotOrders, tmp...)
- tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID6, ToAssetID: assetID2, Rate: 0})
+ tmp, err = movStore.ListOrders(&common.Order{FromAssetID: assetID6, ToAssetID: assetID2, RatioNumerator: 0, RatioDenominator: 1})
if err != nil {
t.Fatalf("case %d: ListOrders(assetID6 and assetID2) error %v.", i, err)
}
return false
}
- orders := e.peekOrders(tradePairs)
+ orders := e.orderBook.PeekOrders(tradePairs)
if len(orders) == 0 {
return false
}
- return isMatched(orders)
+ return IsMatched(orders)
}
// NextMatchedTx return the next matchable transaction by the specified trade pairs
return nil, errors.New("the specified trade pairs can not be matched")
}
- tx, err := e.buildMatchTx(e.peekOrders(tradePairs))
+ tx, err := e.buildMatchTx(sortOrders(e.orderBook.PeekOrders(tradePairs)))
if err != nil {
return nil, err
}
return types.NewTx(*txData), nil
}
-func (e *Engine) peekOrders(tradePairs []*common.TradePair) []*common.Order {
- var orders []*common.Order
- for _, tradePair := range tradePairs {
- order := e.orderBook.PeekOrder(tradePair)
- if order == nil {
- return nil
- }
-
- orders = append(orders, order)
- }
- return orders
-}
-
// MatchedTxFee is object to record the mov tx's fee information
type MatchedTxFee struct {
MaxFeeAmount int64
return (selfIdx + 1) % size
}
-func isMatched(orders []*common.Order) bool {
- rate := orders[0].Rate
- oppositeRate := 1.0
- for i := 1; i < len(orders); i++ {
- oppositeRate *= orders[i].Rate
+func IsMatched(orders []*common.Order) bool {
+ sortedOrders := sortOrders(orders)
+ if len(sortedOrders) == 0 {
+ return false
+ }
+
+ rate := orderRatio(sortedOrders[0])
+ oppositeRate := big.NewFloat(0).SetInt64(1)
+ for i := 1; i < len(sortedOrders); i++ {
+ oppositeRate.Mul(oppositeRate, orderRatio(sortedOrders[i]))
}
- return 1/rate >= oppositeRate
+
+ one := big.NewFloat(0).SetInt64(1)
+ return one.Quo(one, rate).Cmp(oppositeRate) >= 0
+}
+
+func orderRatio(order *common.Order) *big.Float {
+ ratio := big.NewFloat(0).SetInt64(order.RatioNumerator)
+ ratio.Quo(ratio, big.NewFloat(0).SetInt64(order.RatioDenominator))
+ return ratio
}
func setMatchTxArguments(txInput *types.TxInput, isPartialTrade bool, position int, receiveAmounts uint64) {
return errors.New("size of trade pairs at least 2")
}
- for i, tradePair := range tradePairs {
+ assetMap := make(map[string]bool)
+ for _, tradePair := range tradePairs {
+ assetMap[tradePair.FromAssetID.String()] = true
if *tradePair.FromAssetID == *tradePair.ToAssetID {
return errors.New("from asset id can't equal to asset id")
}
+ }
- oppositeTradePair := tradePairs[calcOppositeIndex(len(tradePairs), i)]
- if *tradePair.ToAssetID != *oppositeTradePair.FromAssetID {
- return errors.New("specified trade pairs is invalid")
+ for _, tradePair := range tradePairs {
+ key := tradePair.ToAssetID.String()
+ if _, ok := assetMap[key]; !ok {
+ return errors.New("invalid trade pairs")
}
+ delete(assetMap, key)
}
return nil
}
+
+func sortOrders(orders []*common.Order) []*common.Order {
+ orderMap := make(map[bc.AssetID]*common.Order)
+ firstOrder := orders[0]
+ for i := 1; i < len(orders); i++ {
+ orderMap[*orders[i].FromAssetID] = orders[i]
+ }
+
+ sortedOrders := []*common.Order{firstOrder}
+ for order := firstOrder; *order.ToAssetID != *firstOrder.FromAssetID; {
+ nextOrder, ok := orderMap[*order.ToAssetID]
+ if !ok {
+ return nil
+ }
+
+ sortedOrders = append(sortedOrders, nextOrder)
+ order = nextOrder
+ }
+ return sortedOrders
+}
wantError: true,
},
{
- desc: "invalid case 2 of three trade pairs",
+ desc: "valid case 2 of three trade pairs",
tradePairs: []*common.TradePair{
{
FromAssetID: &mock.BTC,
ToAssetID: &mock.EOS,
},
},
- wantError: true,
+ wantError: false,
},
}
func (o *OrderBook) AddOrder(order *common.Order) error {
tradePairKey := order.TradePair().Key()
orders := o.arrivalAddOrders[tradePairKey]
- if len(orders) > 0 && order.Rate > orders[len(orders)-1].Rate {
+ if len(orders) > 0 && order.Rate() > orders[len(orders)-1].Rate() {
return errors.New("rate of order must less than the min order in order table")
}
}
arrivalOrder := o.peekArrivalOrder(tradePair)
- if nextOrder == nil || (arrivalOrder != nil && arrivalOrder.Rate < nextOrder.Rate) {
+ if nextOrder == nil || (arrivalOrder != nil && arrivalOrder.Rate() < nextOrder.Rate()) {
nextOrder = arrivalOrder
}
return nextOrder
}
+// PeekOrders return the next lowest orders by given array of trade pairs
+func (o *OrderBook) PeekOrders(tradePairs []*common.TradePair) []*common.Order {
+ var orders []*common.Order
+ for _, tradePair := range tradePairs {
+ order := o.PeekOrder(tradePair)
+ if order == nil {
+ return nil
+ }
+
+ orders = append(orders, order)
+ }
+ return orders
+}
+
// PopOrder delete the next lowest order of given trade pair
func (o *OrderBook) PopOrder(tradePair *common.TradePair) {
order := o.PeekOrder(tradePair)
}
}
+// PopOrders delete the next lowest orders by given trade pairs
+func (o *OrderBook) PopOrders(tradePairs []*common.TradePair) []*common.Order {
+ var orders []*common.Order
+ for _, tradePair := range tradePairs {
+ o.PopOrder(tradePair)
+ }
+ return orders
+}
+
func arrangeArrivalAddOrders(orders []*common.Order) map[string][]*common.Order {
arrivalAddOrderMap := make(map[string][]*common.Order)
for _, order := range orders {
Btc2EthOrders = []*common.Order{
{
- FromAssetID: &BTC,
- ToAssetID: Ð,
- Rate: 50,
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ RatioNumerator: 50,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("37b8edf656e45a7addf47f5626e114a8c394d918a36f61b5a2905675a09b40ae")),
SourcePos: 0,
},
},
{
- FromAssetID: &BTC,
- ToAssetID: Ð,
- Rate: 53,
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ RatioNumerator: 53,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("3ec2bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
SourcePos: 0,
},
},
{
- FromAssetID: &BTC,
- ToAssetID: Ð,
- Rate: 52,
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ RatioNumerator: 52,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("1232bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
SourcePos: 0,
},
},
{
- FromAssetID: &BTC,
- ToAssetID: Ð,
- Rate: 49,
+ FromAssetID: &BTC,
+ ToAssetID: Ð,
+ RatioNumerator: 49,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("7872bbfb499a8736d377b547eee5392bcddf7ec2b287e9ed20b5938c3d84e7cd")),
SourcePos: 0,
Eth2BtcOrders = []*common.Order{
{
- FromAssetID: Ð,
- ToAssetID: &BTC,
- Rate: 1 / 51.0,
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ RatioNumerator: 1,
+ RatioDenominator: 51,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("fba43ff5155209cb1769e2ec0e1d4a33accf899c740865edfc6d1de39b873b29")),
SourcePos: 0,
},
},
{
- FromAssetID: Ð,
- ToAssetID: &BTC,
- Rate: 1 / 52.0,
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ RatioNumerator: 1,
+ RatioDenominator: 52,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("05f24bb847db823075d81786aa270748e02602199cd009c0284f928503846a5a")),
SourcePos: 0,
},
},
{
- FromAssetID: Ð,
- ToAssetID: &BTC,
- Rate: 1 / 54.0,
+ FromAssetID: Ð,
+ ToAssetID: &BTC,
+ RatioNumerator: 1,
+ RatioDenominator: 54,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d201")),
SourcePos: 0,
Eos2EtcOrders = []*common.Order{
{
- FromAssetID: &EOS,
- ToAssetID: &ETC,
- Rate: 1 / 2.0,
+ FromAssetID: &EOS,
+ ToAssetID: &ETC,
+ RatioNumerator: 1,
+ RatioDenominator: 2,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d202")),
SourcePos: 0,
Etc2EosOrders = []*common.Order{
{
- FromAssetID: &ETC,
- ToAssetID: &EOS,
- Rate: 2.0,
+ FromAssetID: &ETC,
+ ToAssetID: &EOS,
+ RatioNumerator: 2,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("119a02980796dc352cf6475457463aef5666f66622088de3551fa73a65f0d203")),
SourcePos: 0,
Eth2EosOrders = []*common.Order{
{
- FromAssetID: Ð,
- ToAssetID: &EOS,
- Rate: 2.0,
+ FromAssetID: Ð,
+ ToAssetID: &EOS,
+ RatioNumerator: 2,
+ RatioDenominator: 1,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("c1502d03946e4ea92abdb33f51638b181839bd0d8767acc2ee5c665b659c4b13")),
SourcePos: 0,
Eos2BtcOrders = []*common.Order{
{
- FromAssetID: &EOS,
- ToAssetID: &BTC,
- Rate: 1 / 100.0,
+ FromAssetID: &EOS,
+ ToAssetID: &BTC,
+ RatioNumerator: 1,
+ RatioDenominator: 100,
Utxo: &common.MovUtxo{
SourceID: hashPtr(testutil.MustDecodeHash("27cf8a0877dc858968cc06396fe6aa9e02d15f3e44c862fe29fa5fd50497cf20")),
SourcePos: 0,
tradePair := &common.TradePair{FromAssetID: orderAfter.FromAssetID, ToAssetID: orderAfter.ToAssetID}
orders := m.orderMap[tradePair.Key()]
begin := len(orders)
- if orderAfter.Rate == 0 {
+ if orderAfter.Rate() == 0 {
begin = 0
} else {
for i, order := range orders {
- if order.Rate == orderAfter.Rate {
+ if order.Rate() == orderAfter.Rate() {
begin = i + 1
break
}
errAmountOfFeeGreaterThanMaximum = errors.New("amount of fee greater than max fee amount")
errAssetIDMustUniqueInMatchedTx = errors.New("asset id must unique in matched transaction")
errRatioOfTradeLessThanZero = errors.New("ratio arguments must greater than zero")
- errLengthOfInputIsIncorrect = errors.New("length of matched tx input is not equals to actual matched tx input")
errSpendOutputIDIsIncorrect = errors.New("spend output id of matched tx is not equals to actual matched tx")
errRequestAmountMath = errors.New("request amount of order less than one or big than max of int64")
+ errNotMatchedOrder = errors.New("order in matched tx is not matched")
)
// MovCore represent the core logic of the match module, which include generate match transactions before packing the block,
return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
}
-/*
- @issue: I have two orders A and B, order A's seller program is order B and order B's seller program is order A.
- Assume consensus node accept 0% fee and This two orders are the only two order of this trading pair, will this
- become an infinite loop and DDoS attacks the whole network?
-*/
// BeforeProposalBlock return all transactions than can be matched, and the number of transactions cannot exceed the given capacity.
func (m *MovCore) BeforeProposalBlock(txs []*types.Tx, nodeProgram []byte, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error) {
if blockHeight <= m.startBlockHeight {
return nil
}
-/*
- @issue: the match package didn't support circle yet
-*/
func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
orderBook, err := buildOrderBook(m.movStore, txs)
if err != nil {
return err
}
- matchEngine := match.NewEngine(orderBook, maxFeeRate, nil)
for _, matchedTx := range txs {
if !common.IsMatchedTx(matchedTx) {
continue
}
- tradePairs, err := getSortedTradePairsFromMatchedTx(matchedTx)
+ tradePairs, err := getTradePairsFromMatchedTx(matchedTx)
if err != nil {
return err
}
- actualMatchedTx, err := matchEngine.NextMatchedTx(tradePairs...)
- if err != nil {
- return err
+ orders := orderBook.PeekOrders(tradePairs)
+ if !match.IsMatched(orders) {
+ return errNotMatchedOrder
}
- if len(matchedTx.Inputs) != len(actualMatchedTx.Inputs) {
- return errLengthOfInputIsIncorrect
+ if err := validateSpendOrders(matchedTx, orders); err != nil {
+ return err
}
- spendOutputIDs := make(map[string]bool)
- for _, input := range matchedTx.Inputs {
- spendOutputID, err := input.SpentOutputID()
- if err != nil {
- return err
- }
+ orderBook.PopOrders(tradePairs)
- spendOutputIDs[spendOutputID.String()] = true
- }
+ for i, output := range matchedTx.Outputs {
+ if !segwit.IsP2WMCScript(output.ControlProgram()) {
+ continue
+ }
- for _, input := range actualMatchedTx.Inputs {
- spendOutputID, err := input.SpentOutputID()
+ order, err := common.NewOrderFromOutput(matchedTx, i)
if err != nil {
return err
}
- if _, ok := spendOutputIDs[spendOutputID.String()]; !ok {
- return errSpendOutputIDIsIncorrect
+ if err := orderBook.AddOrder(order); err != nil {
+ return err
}
}
}
return nil
}
+
+func validateSpendOrders(matchedTx *types.Tx, orders []*common.Order) error {
+ spendOutputIDs := make(map[string]bool)
+ for _, input := range matchedTx.Inputs {
+ spendOutputID, err := input.SpentOutputID()
+ if err != nil {
+ return err
+ }
+
+ spendOutputIDs[spendOutputID.String()] = true
+ }
+
+ for _, order := range orders {
+ outputID := order.UTXOHash().String()
+ if _, ok := spendOutputIDs[outputID]; !ok {
+ return errSpendOutputIDIsIncorrect
+ }
+ }
+ return nil
+}
+
func applyTransactions(txs []*types.Tx) ([]*common.Order, []*common.Order, error) {
deleteOrderMap := make(map[string]*common.Order)
addOrderMap := make(map[string]*common.Order)
return addOrders, deleteOrders, nil
}
-/*
- @issue: if consensus node packed match transaction first then packed regular tx, this function's logic may make a valid block invalid
-*/
func buildOrderBook(store database.MovStore, txs []*types.Tx) (*match.OrderBook, error) {
var nonMatchedTxs []*types.Tx
for _, tx := range txs {
return orders, nil
}
-func getSortedTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
- assetMap := make(map[bc.AssetID]bc.AssetID)
- var firstTradePair *common.TradePair
+func getTradePairsFromMatchedTx(tx *types.Tx) ([]*common.TradePair, error) {
+ var tradePairs []*common.TradePair
for _, tx := range tx.Inputs {
contractArgs, err := segwit.DecodeP2WMCProgram(tx.ControlProgram())
if err != nil {
return nil, err
}
- assetMap[tx.AssetID()] = contractArgs.RequestedAsset
- if firstTradePair == nil {
- firstTradePair = &common.TradePair{FromAssetID: tx.AssetAmount().AssetId, ToAssetID: &contractArgs.RequestedAsset}
- }
- }
-
- tradePairs := []*common.TradePair{firstTradePair}
- for tradePair := firstTradePair; *tradePair.ToAssetID != *firstTradePair.FromAssetID; {
- nextTradePairToAssetID, ok := assetMap[*tradePair.ToAssetID]
- if !ok {
- return nil, errInvalidTradePairs
- }
-
- tradePair = &common.TradePair{FromAssetID: tradePair.ToAssetID, ToAssetID: &nextTradePairToAssetID}
- tradePairs = append(tradePairs, tradePair)
- }
-
- if len(tradePairs) != len(tx.Inputs) {
- return nil, errInvalidTradePairs
+ tradePairs = append(tradePairs, &common.TradePair{FromAssetID: tx.AssetAmount().AssetId, ToAssetID: &contractArgs.RequestedAsset})
}
return tradePairs, nil
}