Unverified Commit 754b2b90 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #770 from linj-disanbo/trade-query-use-localdb2

Trade query use localdb2
parents 232133ee bd5de82f
......@@ -3,7 +3,7 @@ module github.com/33cn/plugin
go 1.12
require (
github.com/33cn/chain33 v0.0.0-20200114070319-ef01c7b69d82
github.com/33cn/chain33 v0.0.0-20200115085731-38f06ce8411c
github.com/BurntSushi/toml v0.3.1
github.com/NebulousLabs/Sia v1.3.7
github.com/beorn7/perks v1.0.1 // indirect
......
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/33cn/chain33 v0.0.0-20200114070319-ef01c7b69d82 h1:iTNQWEYAek9YmLcGScdBo2mODy+JOe7gOJl+PgpKjO8=
github.com/33cn/chain33 v0.0.0-20200114070319-ef01c7b69d82/go.mod h1:lhZbNbCnCGsiiapxGZcSxfxKdIAdCK0UzHgpp65XMlM=
github.com/33cn/chain33 v0.0.0-20200115085731-38f06ce8411c h1:ePkHv1GhUGsEubK1mxtrCe0ricOc66QNAN11hOWH6ps=
github.com/33cn/chain33 v0.0.0-20200115085731-38f06ce8411c/go.mod h1:lhZbNbCnCGsiiapxGZcSxfxKdIAdCK0UzHgpp65XMlM=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
......
......@@ -231,7 +231,7 @@ func (store *privacyStore) getWalletPrivacyTxDetails(param *privacytypes.ReqPriv
} else {
keyPrefix = calcRecvPrivacyTxKey(param.Tokenname, param.Address, "")
}
txkeybytes := list.IteratorScanFromLast(keyPrefix, param.Count)
txkeybytes := list.IteratorScanFromLast(keyPrefix, param.Count, db.ListDESC)
for _, keybyte := range txkeybytes {
value, err := store.Get(keybyte)
if err != nil {
......
......@@ -36,7 +36,7 @@ func (t *trade) ExecDelLocal_RevokeBuy(revoke *pty.TradeForRevokeBuy, tx *types.
func (t *trade) localDelLog(tx *types.Transaction, receipt *types.ReceiptData, index int, tradedBoardlot int64) (*types.LocalDBSet, error) {
var set types.LocalDBSet
table := NewOrderTable(t.GetLocalDB())
table := NewOrderTableV2(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
for i := 0; i < len(receipt.Logs); i++ {
......@@ -47,51 +47,46 @@ func (t *trade) localDelLog(tx *types.Transaction, receipt *types.ReceiptData, i
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteSell(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
set.KV = append(set.KV, kv...)
t.deleteSell(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
} else if item.Ty == pty.TyLogTradeSellRevoke {
var receipt pty.ReceiptTradeSellRevoke
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteSell(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
set.KV = append(set.KV, kv...)
t.deleteSell(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
} else if item.Ty == pty.TyLogTradeBuyMarket {
var receipt pty.ReceiptTradeBuyMarket
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteBuy(receipt.Base, txIndex, table)
set.KV = append(set.KV, kv...)
t.deleteBuy(receipt.Base, txIndex, table)
} else if item.Ty == pty.TyLogTradeBuyRevoke {
var receipt pty.ReceiptTradeBuyRevoke
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteBuyLimit(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
set.KV = append(set.KV, kv...)
t.deleteBuyLimit(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
} else if item.Ty == pty.TyLogTradeBuyLimit {
var receipt pty.ReceiptTradeBuyLimit
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteBuyLimit(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
set.KV = append(set.KV, kv...)
t.deleteBuyLimit(receipt.Base, item.Ty, tx, txIndex, table, tradedBoardlot)
} else if item.Ty == pty.TyLogTradeSellMarket {
var receipt pty.ReceiptSellMarket
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.deleteSellMarket(receipt.Base, txIndex, table)
set.KV = append(set.KV, kv...)
t.deleteSellMarket(receipt.Base, txIndex, table)
}
}
newKvs, err := table.Save()
debugTableKV(newKvs, "exec_del_local orderV2 kvs")
if err != nil {
tradelog.Error("trade table.Save failed", "error", err)
return nil, err
......
......@@ -36,9 +36,8 @@ func (t *trade) ExecLocal_RevokeBuy(revoke *pty.TradeForRevokeBuy, tx *types.Tra
func (t *trade) localAddLog(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
var set types.LocalDBSet
table := NewOrderTable(t.GetLocalDB())
table := NewOrderTableV2(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
for i := 0; i < len(receipt.Logs); i++ {
item := receipt.Logs[i]
if item.Ty == pty.TyLogTradeSellLimit {
......@@ -47,24 +46,21 @@ func (t *trade) localAddLog(tx *types.Transaction, receipt *types.ReceiptData, i
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.saveSell(receipt.Base, item.Ty, tx, txIndex, table)
set.KV = append(set.KV, kv...)
t.saveSell(receipt.Base, item.Ty, tx, txIndex, table)
} else if item.Ty == pty.TyLogTradeSellRevoke {
var receipt pty.ReceiptTradeSellRevoke
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.saveSell(receipt.Base, item.Ty, tx, txIndex, table)
set.KV = append(set.KV, kv...)
t.saveSell(receipt.Base, item.Ty, tx, txIndex, table)
} else if item.Ty == pty.TyLogTradeBuyMarket {
var receipt pty.ReceiptTradeBuyMarket
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.saveBuy(receipt.Base, tx, txIndex, table)
set.KV = append(set.KV, kv...)
t.saveBuy(receipt.Base, tx, txIndex, table)
} else if item.Ty == pty.TyLogTradeBuyRevoke {
var receipt pty.ReceiptTradeBuyRevoke
err := types.Decode(item.Log, &receipt)
......@@ -72,8 +68,7 @@ func (t *trade) localAddLog(tx *types.Transaction, receipt *types.ReceiptData, i
panic(err) //数据错误了,已经被修改了
}
kv := t.saveBuyLimit(receipt.Base, item.Ty, tx, txIndex, table)
set.KV = append(set.KV, kv...)
t.saveBuyLimit(receipt.Base, item.Ty, tx, txIndex, table)
} else if item.Ty == pty.TyLogTradeBuyLimit {
var receipt pty.ReceiptTradeBuyLimit
err := types.Decode(item.Log, &receipt)
......@@ -81,20 +76,18 @@ func (t *trade) localAddLog(tx *types.Transaction, receipt *types.ReceiptData, i
panic(err) //数据错误了,已经被修改了
}
kv := t.saveBuyLimit(receipt.Base, item.Ty, tx, txIndex, table)
set.KV = append(set.KV, kv...)
t.saveBuyLimit(receipt.Base, item.Ty, tx, txIndex, table)
} else if item.Ty == pty.TyLogTradeSellMarket {
var receipt pty.ReceiptSellMarket
err := types.Decode(item.Log, &receipt)
if err != nil {
panic(err) //数据错误了,已经被修改了
}
kv := t.saveSellMarket(receipt.Base, tx, txIndex, table)
//tradelog.Info("saveSellMarket", "kv", kv)
set.KV = append(set.KV, kv...)
t.saveSellMarket(receipt.Base, tx, txIndex, table)
}
}
newKvs, err := table.Save()
debugTableKV(newKvs, "exec_local orderV2 kvs")
if err != nil {
tradelog.Error("trade table.Save failed", "error", err)
return nil, err
......@@ -106,3 +99,10 @@ func (t *trade) localAddLog(tx *types.Transaction, receipt *types.ReceiptData, i
}
return &set, nil
}
func debugTableKV(kvs []*types.KeyValue, msg string) {
tradelog.Debug("table save debug:"+msg, "count", len(kvs))
for i, kv := range kvs {
tradelog.Debug("table save debug:"+msg, "i", i, "key", string(kv.Key), "value", string(kv.Value))
}
}
......@@ -4,74 +4,11 @@
package executor
import (
"fmt"
"strconv"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/trade/types"
)
const (
sellOrderSHTAS = "LODB-trade-sellorder-shtas:"
sellOrderASTS = "LODB-trade-sellorder-asts:"
sellOrderATSS = "LODB-trade-sellorder-atss:"
sellOrderTSPAS = "LODB-trade-sellorder-tspas:"
buyOrderSHTAS = "LODB-trade-buyorder-shtas:"
buyOrderASTS = "LODB-trade-buyorder-asts:"
buyOrderATSS = "LODB-trade-buyorder-atss:"
buyOrderTSPAS = "LODB-trade-buyorder-tspas:"
sellIDPrefix = "mavl-trade-sell-"
buyIDPrefix = "mavl-trade-buy-"
// Addr-Status-Type-Height-Key
orderASTHK = "LODB-trade-order-asthk:"
sellIDPrefix = "mavl-trade-sell-"
buyIDPrefix = "mavl-trade-buy-"
)
// sell order 4 key, 4prefix
// 特定状态下的卖单
func calcTokenSellOrderKey(token string, addr string, status int32, sellOrderID string, height int64) []byte {
key := fmt.Sprintf(sellOrderSHTAS+"%d:%d:%s:%s:%s", status, height, token, addr, sellOrderID)
return []byte(key)
}
// 特定账户下特定状态的卖单
func calcOnesSellOrderKeyStatus(token string, addr string, status int32, sellOrderID string) []byte {
key := fmt.Sprintf(sellOrderASTS+"%s:%d:%s:%s", addr, status, token, sellOrderID)
return []byte(key)
}
// 特定账户下特定token的卖单
func calcOnesSellOrderKeyToken(token string, addr string, status int32, sellOrderID string) []byte {
key := fmt.Sprintf(sellOrderATSS+"%s:%s:%d:%s", addr, token, status, sellOrderID)
return []byte(key)
}
// 指定token的卖单, 带上价格方便排序
func calcTokensSellOrderKeyStatus(token string, status int32, price int64, addr string, sellOrderID string) []byte {
key := fmt.Sprintf(sellOrderTSPAS+"%s:%d:%016d:%s:%s", token, status, price, addr, sellOrderID)
return []byte(key)
}
func calcTokensSellOrderPrefixStatus(token string, status int32) []byte {
prefix := fmt.Sprintf(sellOrderTSPAS+"%s:%d:", token, status)
return []byte(prefix)
}
// 特定账户下指定token的卖单
func calcOnesSellOrderPrefixToken(token string, addr string) []byte {
key := fmt.Sprintf(sellOrderATSS+"%s:%s", addr, token)
return []byte(key)
}
// 特定账户下的卖单
func calcOnesSellOrderPrefixAddr(addr string) []byte {
return []byte(fmt.Sprintf(sellOrderASTS+"%s", addr))
}
func calcOnesSellOrderPrefixStatus(addr string, status int32) []byte {
return []byte(fmt.Sprintf(sellOrderASTS+"%s:%d", addr, status))
}
// ids
func calcTokenSellID(hash string) string {
return sellIDPrefix + hash
......@@ -81,104 +18,6 @@ func calcTokenBuyID(hash string) string {
return buyIDPrefix + hash
}
// buy limit order 4 key, 4prefix
// 特定状态下的买单
func calcTokenBuyOrderKey(token string, addr string, status int32, orderID string, height int64) []byte {
key := fmt.Sprintf(buyOrderSHTAS+"%d:%d:%s:%s:%s", status, height, token, addr, orderID)
return []byte(key)
}
// 特定账户下特定状态的买单
func calcOnesBuyOrderKeyStatus(token string, addr string, status int32, orderID string) []byte {
key := fmt.Sprintf(buyOrderASTS+"%s:%d:%s:%s", addr, status, token, orderID)
return []byte(key)
}
// 特定账户下特定token的买单
func calcOnesBuyOrderKeyToken(token string, addr string, status int32, orderID string) []byte {
key := fmt.Sprintf(buyOrderATSS+"%s:%s:%d:%s", addr, token, status, orderID)
return []byte(key)
}
// 指定token的卖单, 带上价格方便排序
func calcTokensBuyOrderKeyStatus(token string, status int32, price int64, addr string, orderID string) []byte {
key := fmt.Sprintf(buyOrderTSPAS+"%s:%d:%016d:%s:%s", token, status, price, addr, orderID)
return []byte(key)
}
func calcTokensBuyOrderPrefixStatus(token string, status int32) []byte {
prefix := fmt.Sprintf(buyOrderTSPAS+"%s:%d:", token, status)
return []byte(prefix)
}
// 特定账户下指定token的买单
func calcOnesBuyOrderPrefixToken(token string, addr string) []byte {
key := fmt.Sprintf(buyOrderATSS+"%s:%s", addr, token)
return []byte(key)
}
// 特定账户下的买单
func calcOnesBuyOrderPrefixAddr(addr string) []byte {
return []byte(fmt.Sprintf(buyOrderASTS+"%s", addr))
}
func calcOnesBuyOrderPrefixStatus(addr string, status int32) []byte {
return []byte(fmt.Sprintf(buyOrderASTS+"%s:%d", addr, status))
}
// 特定帐号下的订单
// 这里状态进行转化, 分成 状态和类型, 状态三种, 类型 两种
// on: OnSale OnBuy
// done: Soldout boughtOut
// revoke: RevokeSell RevokeBuy
// buy/sell 两种类型
// 目前页面是按addr, 状态来
func calcOnesOrderKey(addr string, status int32, ty int32, height int64, key string) []byte {
return []byte(fmt.Sprintf(orderASTHK+"%s:%d:%010d:%d:%s", addr, status, height, ty, key))
}
// 特定状态下的买单
//func calcTokenBuyOrderPrefixStatus(status int32) []byte {
// return []byte(fmt.Sprintf(buyOrderSHTAS+"%d", status))
//}
func genSellMarketOrderKeyValue(kv []*types.KeyValue, receipt *pty.ReceiptSellBase, status int32,
height int64, value []byte) []*types.KeyValue {
keyID := receipt.TxHash
newkey := calcTokenSellOrderKey(receipt.TokenSymbol, receipt.Owner, status, keyID, height)
kv = append(kv, &types.KeyValue{Key: newkey, Value: value})
newkey = calcOnesSellOrderKeyStatus(receipt.TokenSymbol, receipt.Owner, status, keyID)
kv = append(kv, &types.KeyValue{Key: newkey, Value: value})
newkey = calcOnesSellOrderKeyToken(receipt.TokenSymbol, receipt.Owner, status, keyID)
kv = append(kv, &types.KeyValue{Key: newkey, Value: value})
priceBoardlot, err := strconv.ParseFloat(receipt.PricePerBoardlot, 64)
if err != nil {
panic(err)
}
priceBoardlotInt64 := int64(priceBoardlot * float64(types.TokenPrecision))
AmountPerBoardlot, err := strconv.ParseFloat(receipt.AmountPerBoardlot, 64)
if err != nil {
panic(err)
}
AmountPerBoardlotInt64 := int64(AmountPerBoardlot * float64(types.Coin))
price := calcPriceOfToken(priceBoardlotInt64, AmountPerBoardlotInt64)
newkey = calcTokensSellOrderKeyStatus(receipt.TokenSymbol, status,
price, receipt.Owner, keyID)
kv = append(kv, &types.KeyValue{Key: newkey, Value: value})
st, ty := fromStatus(status)
newkey = calcOnesOrderKey(receipt.Owner, st, ty, height, keyID)
kv = append(kv, &types.KeyValue{Key: newkey, Value: value})
return kv
}
// make a number as token's price whether cheap or dear
// support 1e8 bty pre token or 1/1e8 bty pre token, [1Coins, 1e16Coins]
// the number in key is used to sort buy orders and pages
......
......@@ -171,6 +171,7 @@ func NewOrderTable(kvdb dbm.KV) *table.Table {
return t
}
// gen order from tx and receipt
func (t *trade) genSellLimit(tx *types.Transaction, sell *pty.ReceiptSellBase,
sellorder *pty.SellOrder, txIndex string) *pty.LocalOrder {
......@@ -272,7 +273,6 @@ func parseOrderPriceFloat(s string) int64 {
}
func (t *trade) genSellMarket(tx *types.Transaction, sell *pty.ReceiptSellBase, txIndex string) *pty.LocalOrder {
order := &pty.LocalOrder{
AssetSymbol: sell.TokenSymbol,
TxIndex: txIndex,
......@@ -300,7 +300,6 @@ func (t *trade) genSellMarket(tx *types.Transaction, sell *pty.ReceiptSellBase,
}
func (t *trade) genBuyLimit(tx *types.Transaction, buy *pty.ReceiptBuyBase, txIndex string) *pty.LocalOrder {
order := &pty.LocalOrder{
AssetSymbol: buy.TokenSymbol,
TxIndex: txIndex,
......@@ -375,7 +374,6 @@ func (t *trade) rollbackBuyLimit(tx *types.Transaction, buy *pty.ReceiptBuyBase,
}
func (t *trade) genBuyMarket(tx *types.Transaction, buy *pty.ReceiptBuyBase, txIndex string) *pty.LocalOrder {
order := &pty.LocalOrder{
AssetSymbol: buy.TokenSymbol,
TxIndex: txIndex,
......@@ -392,7 +390,7 @@ func (t *trade) genBuyMarket(tx *types.Transaction, buy *pty.ReceiptBuyBase, txI
Height: buy.Height,
Key: calcTokenBuyID(hex.EncodeToString(tx.Hash())),
BlockTime: t.GetBlockTime(),
IsSellOrder: true,
IsSellOrder: false,
AssetExec: buy.AssetExec,
IsFinished: true,
PriceExec: buy.PriceExec,
......@@ -400,63 +398,3 @@ func (t *trade) genBuyMarket(tx *types.Transaction, buy *pty.ReceiptBuyBase, txI
}
return order
}
func list(db dbm.KVDB, indexName string, data *pty.LocalOrder, count, direction int32) ([]*table.Row, error) {
query := NewOrderTable(db).GetQuery(db)
var primary []byte
if len(data.TxIndex) > 0 {
primary = []byte(data.TxIndex)
}
cur := &OrderRow{LocalOrder: data}
index, err := cur.Get(indexName)
if err != nil {
tradelog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
tradelog.Debug("query List dbg", "indexName", indexName, "index", string(index), "primary", primary, "count", count, "direction", direction)
rows, err := query.ListIndex(indexName, index, primary, count, direction)
if err != nil {
tradelog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
if len(rows) == 0 {
return nil, types.ErrNotFound
}
return rows, nil
}
/*
按 资产 查询 :
按 资产 & 地址 查询
按 地址
排序和分类
1. 时间顺序 txindex
1. 分类, 不同的状态 & 不同的性质: 买/卖
交易 -> 订单 按订单来 (交易和订单是多对多的关系,不适合joinTable)
交易 T1 Create -> T2 part-take -> T3 Revoke
订单左为进行中, 右为完成,
订单 (C1) | () -> (C1m) | (C2) -> () | (C2, C1r)
查询交易 / 查询订单
C -> C/M -> C/D
\
\ ->R
状态 1, TradeOrderStatusOnSale, 在售
状态 2: TradeOrderStatusSoldOut,售完
状态 3: TradeOrderStatusRevoked, 卖单被撤回
状态 4: TradeOrderStatusExpired, 订单超时(目前不支持订单超时)
状态 5: TradeOrderStatusOnBuy, 求购
状态 6: TradeOrderStatusBoughtOut, 购买完成
状态 7: TradeOrderStatusBuyRevoked, 买单被撤回
*/
......@@ -13,7 +13,7 @@ import (
)
var order1 = &pty.LocalOrder{
AssetSymbol: "A",
AssetSymbol: "bty",
Owner: "O1",
AmountPerBoardlot: 1,
MinBoardlot: 1,
......@@ -28,13 +28,15 @@ var order1 = &pty.LocalOrder{
Key: "B1",
BlockTime: 1,
IsSellOrder: false,
AssetExec: "a",
AssetExec: "coins",
TxIndex: dapp.HeightIndexStr(1, 1),
IsFinished: false,
PriceExec: "token",
PriceSymbol: "CCNY",
}
var order2 = &pty.LocalOrder{
AssetSymbol: "A",
AssetSymbol: "bty",
Owner: "O1",
AmountPerBoardlot: 1,
MinBoardlot: 1,
......@@ -49,9 +51,32 @@ var order2 = &pty.LocalOrder{
Key: "B2",
BlockTime: 2,
IsSellOrder: false,
AssetExec: "a",
AssetExec: "coins",
TxIndex: dapp.HeightIndexStr(2, 1),
IsFinished: false,
PriceExec: "token",
PriceSymbol: "CCNY",
}
// 两个fork前的数据
var order3 = &pty.LocalOrder{
AssetSymbol: "CCNY",
Owner: "O1",
AmountPerBoardlot: 1,
MinBoardlot: 1,
PricePerBoardlot: 1,
TotalBoardlot: 10,
TradedBoardlot: 0,
BuyID: "B2",
Status: pty.TradeOrderStatusOnBuy,
SellID: "",
TxHash: nil,
Height: 3,
Key: "B2",
BlockTime: 3,
IsSellOrder: false,
TxIndex: dapp.HeightIndexStr(3, 1),
IsFinished: false,
}
func TestListAll(t *testing.T) {
......@@ -65,3 +90,15 @@ func TestListAll(t *testing.T) {
t.Log(kvs)
ldb.Close()
}
func TestListV2All(t *testing.T) {
dir, ldb, tdb := util.CreateTestDB()
t.Log(dir, ldb, tdb)
odb := NewOrderTableV2(tdb)
odb.Add(order1)
odb.Add(order2)
kvs, err := odb.Save()
assert.Nil(t, err)
t.Log(kvs)
ldb.Close()
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"fmt"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/trade/types"
)
// 本地数据版本
// 1. v0 手动生成
// 2. v1 用table生成 LocalOrder
// 3. v2 用table生成 LocalOrderV2 比v1 用更多的索引,支持v0需要的数据索引,即将v2 包含 v0 v1 数据
// 预期在6.4升级后 v0 格式将不存在
/*
现有接口
1. 查询地址对应的买单 (无分页)
1.1 只指定地址 -> owner
1.2 同时指定地址和token -> owner_asset
1.3 显示一个用户成交的所有买单 -> owner
1.4 显示一个用户成交的指定一个或者多个token所有买单 -> owner_asset 不支持多个
2. 分状态查询地址的买单: 状态 地址 (无分页) -> owner_status
3. 显示一个token 指定数量的买单 GetTokenBuyOrderByStatus -> asset_inBuy_status
4. 显示指定token出售者的一个或多个token 或 不指定token 的卖单 (无分页) -> owner_asset/owner_asset_isSell 不支持多个
5. 显示指定状态下的某地址卖单 (无分页) -> owner_isSell_status
6. 显示一个token 指定数量的卖单 -> asset_isSell
7. 根据状态分页列出某地址的订单(包括买单卖单) owner_status
*/
//
var optV2 = &table.Option{
Prefix: "LODB-trade",
Name: "order_v2",
Primary: "txIndex",
// asset 指定交易对 price_exec + price_symbol + asset_exec+asset_symbol
// status: 设计为可以同时查询几种的并集 , 存储为前缀, 需要提前设计需要合并的, 用前缀表示
// 进行中, 撤销, 部分成交 , 全部成交, 完成状态统一前缀. 数字和原来不一样
// 00 10 11 12 1*
// 排序特点: 在不用key排序时,需要生成排序用的组合索引, 前面n个索引用来区分前缀, 后一个索引用来排序
Index: []string{
"key", // 内部查询用
"asset", // 按资产统计订单
"asset_isSell_status", // 接口 3
// "asset_status", 可能需求, 用于资产的交易历史
// "asset_isSell",
"owner", // 接口 1.1, 1.3
"owner_asset", // 接口 1.2, 1.4, 4, 7
"owner_asset_isSell", // 接口 4
"owner_asset_status", // 新需求, 在
"owner_isSell", // 接口 6
// "owner_isSell_statusPrefix", // 状态可以定制组合, 成交历史需求
"owner_status", // 接口 2
"assset_isSell_isFinished", // 用 isFinish, 进行订单是否完成的列表功能
"owner_asset_isFinished",
"owner_isFinished",
// "owner_statusPrefix", // 状态可以定制组合 , 成交历史需求
// 增加更多的key, 把老接口的数据的key 也生成,可以去掉老接口的实现
// https://chain.33.cn/document/105 文档1.8 sell & asset-price & status, order by price
// https://chain.33.cn/document/105 文档1.3 buy & asset-price & status, order by price
"asset_isSell_status_price",
// 文档1.2 文档1.5 按 用户状态来 addr-status buy or sell
"owner_isSell_status",
},
}
// OrderV2Row order row
type OrderV2Row struct {
*pty.LocalOrder
}
// NewOrderV2Row create row
func NewOrderV2Row() *OrderV2Row {
return &OrderV2Row{LocalOrder: nil}
}
// CreateRow create row
func (r *OrderV2Row) CreateRow() *table.Row {
return &table.Row{Data: &pty.LocalOrder{}}
}
// SetPayload set payload
func (r *OrderV2Row) SetPayload(data types.Message) error {
if d, ok := data.(*pty.LocalOrder); ok {
r.LocalOrder = d
return nil
}
return types.ErrTypeAsset
}
// Get get index key
func (r *OrderV2Row) Get(key string) ([]byte, error) {
switch key {
case "txIndex":
return []byte(r.TxIndex), nil
case "key":
return []byte(r.Key), nil
case "asset":
return []byte(r.asset()), nil
case "asset_isSell_status":
return []byte(fmt.Sprintf("%s_%d_%s", r.asset(), r.isSell(), r.status())), nil
case "owner":
return []byte(r.Owner), nil
case "owner_asset":
return []byte(fmt.Sprintf("%s_%s", r.Owner, r.asset())), nil
case "owner_asset_isSell":
return []byte(fmt.Sprintf("%s_%s_%d", r.Owner, r.asset(), r.isSell())), nil
case "owner_asset_status":
return []byte(fmt.Sprintf("%s_%s_%s", r.Owner, r.asset(), r.status())), nil
case "owner_isSell":
return []byte(fmt.Sprintf("%s_%d", r.Owner, r.isSell())), nil
case "owner_isSell_status":
return []byte(fmt.Sprintf("%s_%d_%s", r.Owner, r.isSell(), r.status())), nil
case "owner_status":
return []byte(fmt.Sprintf("%s_%s", r.Owner, r.status())), nil
//case "owner_statusPrefix":
// return []byte(fmt.Sprintf("%s_%d", r.Owner, r.isSell())), nil
case "assset_isSell_isFinished":
return []byte(fmt.Sprintf("%s_%d_%d", r.Owner, r.isSell(), r.isFinished())), nil
case "owner_asset_isFinished":
return []byte(fmt.Sprintf("%s_%s_%d", r.Owner, r.asset(), r.isFinished())), nil
case "owner_isFinished":
return []byte(fmt.Sprintf("%s_%d", r.Owner, r.isFinished())), nil
case "asset_isSell_status_price":
return []byte(fmt.Sprintf("%s_%d_%s_%s", r.asset(), r.isSell(), r.status(), r.price())), nil
default:
return nil, types.ErrNotFound
}
}
// 老接口查询参数: Price 用主币
func (r *OrderV2Row) asset() string {
return r.LocalOrder.PriceExec + "." + r.LocalOrder.PriceSymbol + "_" + r.LocalOrder.AssetExec + "." + r.LocalOrder.AssetSymbol
}
func (r *OrderV2Row) isSell() int {
if r.IsSellOrder {
return 1
}
return 0
}
func (r *OrderV2Row) isFinished() int {
if r.IsFinished {
return 1
}
return 0
}
func (r *OrderV2Row) price() string {
// 在计算前缀时,返回空
if r.AmountPerBoardlot == 0 {
return ""
}
p := calcPriceOfToken(r.PricePerBoardlot, r.AmountPerBoardlot)
return fmt.Sprintf("%018d", p)
}
// status: 设计为可以同时查询几种的并集 , 存储为前缀, 需要提前设计需要合并的, 用前缀表示
// 进行中, 撤销, 部分成交 , 全部成交, 完成状态统一前缀. 数字和原来不一样
// 01 10 11 12 19 -> 1*
func (r *OrderV2Row) status() string {
if r.Status == pty.TradeOrderStatusOnBuy || r.Status == pty.TradeOrderStatusOnSale {
return "01" // 试图用1 可以匹配所有完成的
} else if r.Status == pty.TradeOrderStatusSoldOut || r.Status == pty.TradeOrderStatusBoughtOut {
return "12"
} else if r.Status == pty.TradeOrderStatusRevoked || r.Status == pty.TradeOrderStatusBuyRevoked {
return "10"
} else if r.Status == pty.TradeOrderStatusSellHalfRevoked || r.Status == pty.TradeOrderStatusBuyHalfRevoked {
return "11"
} else if r.Status == pty.TradeOrderStatusGroupComplete {
return "1" // 1* match complete
}
return "XX"
}
// NewOrderTableV2 create order table
func NewOrderTableV2(kvdb dbm.KV) *table.Table {
rowMeta := NewOrderV2Row()
rowMeta.SetPayload(&pty.LocalOrder{})
t, err := table.NewTable(rowMeta, kvdb, optV2)
if err != nil {
panic(err)
}
return t
}
func listV2(db dbm.KVDB, indexName string, data *pty.LocalOrder, count, direction int32) ([]*table.Row, error) {
query := NewOrderTableV2(db).GetQuery(db)
var primary []byte
if len(data.TxIndex) > 0 {
primary = []byte(data.TxIndex)
}
cur := &OrderV2Row{LocalOrder: data}
index, err := cur.Get(indexName)
if err != nil {
tradelog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
tradelog.Debug("query List dbg", "indexName", indexName, "index", string(index), "primary", primary, "count", count, "direction", direction)
rows, err := query.ListIndex(indexName, index, primary, count, direction)
if err != nil {
tradelog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
if len(rows) == 0 {
return nil, types.ErrNotFound
}
return rows, nil
}
This diff is collapsed.
......@@ -81,23 +81,12 @@ func (t *trade) GetOnesOrderWithStatus(req *pty.ReqAddrAssets) (types.Message, e
if len(req.FromKey) > 0 {
order.TxIndex = req.FromKey
}
rows, err := list(t.GetLocalDB(), "owner_isFinished", &order, req.Count, req.Direction)
rows, err := listV2(t.GetLocalDB(), "owner_isFinished", &order, req.Count, req.Direction)
if err != nil {
tradelog.Error("GetOnesOrderWithStatus", "err", err)
return nil, err
}
var replys pty.ReplyTradeOrders
cfg := t.GetAPI().GetConfig()
for _, row := range rows {
o, ok := row.Data.(*pty.LocalOrder)
if !ok {
tradelog.Error("GetOnesOrderWithStatus", "err", "bad row type")
return nil, types.ErrTypeAsset
}
reply := fmtReply(cfg, o)
replys.Orders = append(replys.Orders, reply)
}
return &replys, nil
return t.toTradeOrders(rows)
}
func fmtReply(cfg *types.Chain33Config, order *pty.LocalOrder) *pty.ReplyTradeOrder {
......@@ -131,7 +120,7 @@ func fmtReply(cfg *types.Chain33Config, order *pty.LocalOrder) *pty.ReplyTradeOr
}
func (t *trade) GetOneOrder(req *pty.ReqAddrAssets) (types.Message, error) {
query := NewOrderTable(t.GetLocalDB())
query := NewOrderTableV2(t.GetLocalDB())
tradelog.Debug("query GetData dbg", "primary", req.FromKey)
row, err := query.GetData([]byte(req.FromKey))
if err != nil {
......
This diff is collapsed.
......@@ -73,7 +73,8 @@ func (t *trade) getSellOrderFromDb(sellID []byte) *pty.SellOrder {
return &sellorder
}
func (t *trade) saveSell(base *pty.ReceiptSellBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table) []*types.KeyValue {
// sell limit
func (t *trade) saveSell(base *pty.ReceiptSellBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table) {
sellorder := t.getSellOrderFromDb([]byte(base.SellID))
if ty == pty.TyLogTradeSellLimit && sellorder.SoldBoardlot == 0 {
......@@ -83,33 +84,25 @@ func (t *trade) saveSell(base *pty.ReceiptSellBase, ty int32, tx *types.Transact
} else {
t.updateSellLimit(tx, base, sellorder, txIndex, ldb)
}
return genSaveSellKv(sellorder)
}
func (t *trade) deleteSell(base *pty.ReceiptSellBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table, tradedBoardlot int64) []*types.KeyValue {
func (t *trade) deleteSell(base *pty.ReceiptSellBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table, tradedBoardlot int64) {
sellorder := t.getSellOrderFromDb([]byte(base.SellID))
if ty == pty.TyLogTradeSellLimit && sellorder.SoldBoardlot == 0 {
ldb.Del([]byte(txIndex))
} else {
t.rollBackSellLimit(tx, base, sellorder, txIndex, ldb, tradedBoardlot)
}
return genDeleteSellKv(sellorder)
}
func (t *trade) saveBuy(receiptTradeBuy *pty.ReceiptBuyBase, tx *types.Transaction, txIndex string, ldb *table.Table) []*types.KeyValue {
//tradelog.Info("save", "buy", receiptTradeBuy)
var kv []*types.KeyValue
func (t *trade) saveBuy(receiptTradeBuy *pty.ReceiptBuyBase, tx *types.Transaction, txIndex string, ldb *table.Table) {
order := t.genBuyMarket(tx, receiptTradeBuy, txIndex)
tradelog.Debug("trade BuyMarket save local", "order", order)
ldb.Add(order)
return saveBuyMarketOrderKeyValue(kv, receiptTradeBuy, pty.TradeOrderStatusBoughtOut, t.GetHeight())
}
func (t *trade) deleteBuy(receiptTradeBuy *pty.ReceiptBuyBase, txIndex string, ldb *table.Table) []*types.KeyValue {
var kv []*types.KeyValue
func (t *trade) deleteBuy(receiptTradeBuy *pty.ReceiptBuyBase, txIndex string, ldb *table.Table) {
ldb.Del([]byte(txIndex))
return deleteBuyMarketOrderKeyValue(kv, receiptTradeBuy, pty.TradeOrderStatusBoughtOut, t.GetHeight())
}
// BuyLimit Local
......@@ -123,7 +116,7 @@ func (t *trade) getBuyOrderFromDb(buyID []byte) *pty.BuyLimitOrder {
return &buyOrder
}
func (t *trade) saveBuyLimit(buy *pty.ReceiptBuyBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table) []*types.KeyValue {
func (t *trade) saveBuyLimit(buy *pty.ReceiptBuyBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table) {
buyOrder := t.getBuyOrderFromDb([]byte(buy.BuyID))
tradelog.Debug("Table", "buy-add", buyOrder)
if buyOrder.Status == pty.TradeOrderStatusOnBuy && buy.BoughtBoardlot == 0 {
......@@ -133,31 +126,25 @@ func (t *trade) saveBuyLimit(buy *pty.ReceiptBuyBase, ty int32, tx *types.Transa
} else {
t.updateBuyLimit(tx, buy, buyOrder, txIndex, ldb)
}
return genSaveBuyLimitKv(buyOrder)
}
func (t *trade) deleteBuyLimit(buy *pty.ReceiptBuyBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table, traded int64) []*types.KeyValue {
func (t *trade) deleteBuyLimit(buy *pty.ReceiptBuyBase, ty int32, tx *types.Transaction, txIndex string, ldb *table.Table, traded int64) {
buyOrder := t.getBuyOrderFromDb([]byte(buy.BuyID))
if ty == pty.TyLogTradeBuyLimit && buy.BoughtBoardlot == 0 {
ldb.Del([]byte(txIndex))
} else {
t.rollbackBuyLimit(tx, buy, buyOrder, txIndex, ldb, traded)
}
return genDeleteBuyLimitKv(buyOrder)
}
func (t *trade) saveSellMarket(receiptTradeBuy *pty.ReceiptSellBase, tx *types.Transaction, txIndex string, ldb *table.Table) []*types.KeyValue {
var kv []*types.KeyValue
func (t *trade) saveSellMarket(receiptTradeBuy *pty.ReceiptSellBase, tx *types.Transaction, txIndex string, ldb *table.Table) {
order := t.genSellMarket(tx, receiptTradeBuy, txIndex)
ldb.Add(order)
return saveSellMarketOrderKeyValue(kv, receiptTradeBuy, pty.TradeOrderStatusSoldOut, t.GetHeight())
}
func (t *trade) deleteSellMarket(receiptTradeBuy *pty.ReceiptSellBase, txIndex string, ldb *table.Table) []*types.KeyValue {
var kv []*types.KeyValue
func (t *trade) deleteSellMarket(receiptTradeBuy *pty.ReceiptSellBase, txIndex string, ldb *table.Table) {
ldb.Del([]byte(txIndex))
return deleteSellMarketOrderKeyValue(kv, receiptTradeBuy, pty.TradeOrderStatusSoldOut, t.GetHeight())
}
// CheckReceiptExecOk return true to check if receipt ty is ok
......
......@@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/trade/types"
)
......@@ -68,51 +67,11 @@ var (
}
)
func init() {
}
// 分叉不好构造, 直接生成对应的kv 记录进行对比
// 在save时有值的, 需要在del 时设置为空;或save 时设置为空, 在del 时有值的
func check(t *testing.T, kvSave, kvDel []*types.KeyValue) {
kvmapSave := map[string]string{}
for _, kv := range kvSave {
if string(kv.Value) != "IAMSELLID" && kv.Value != nil {
t.Error("onsale error")
}
kvmapSave[string(kv.Key)] = string(kv.Value)
}
for _, kv := range kvDel {
v, ok := kvmapSave[string(kv.Key)]
if !ok {
t.Error("error 1")
}
if len(v) == 0 && len(kv.Value) == 0 {
t.Error("error 2")
}
if len(v) != 0 && len(kv.Value) != 0 {
t.Error("error 3")
}
}
}
func TestOnsaleSaveDel(t *testing.T) {
kvOnsale := genSaveSellKv(&sellorderOnsale)
kvOnsaleDel := genDeleteSellKv(&sellorderOnsale)
check(t, kvOnsale, kvOnsaleDel)
}
func TestSoldOutSaveDel(t *testing.T) {
kv := genSaveSellKv(&sellorderSoldOut)
kvDel := genDeleteSellKv(&sellorderSoldOut)
check(t, kv, kvDel)
}
func TestRevokeSaveDel(t *testing.T) {
kv := genSaveSellKv(&sellorderRevoked)
kvDel := genDeleteSellKv(&sellorderRevoked)
check(t, kv, kvDel)
// TODO 几个测试数据 linter 不报错, 修改好后写测试可能需要用
func Test_Order(t *testing.T) {
assert.NotNil(t, &sellorderOnsale)
assert.NotNil(t, &sellorderSoldOut)
assert.NotNil(t, &sellorderRevoked)
}
func TestPriceCheck(t *testing.T) {
......
......@@ -12,7 +12,6 @@ import (
"strings"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
......@@ -116,27 +115,6 @@ func getSellOrderFromID(sellID []byte, db dbm.KV) (*pty.SellOrder, error) {
return &sellOrder, nil
}
func getTx(txHash []byte, db dbm.KV, api client.QueueProtocolAPI) (*types.TxResult, error) {
hash, err := common.FromHex(string(txHash))
if err != nil {
return nil, err
}
value, err := api.QueryTx(&types.ReqHash{Hash: hash})
if err != nil {
tradelog.Error("getTx", "Failed to get value from db with getTx", string(txHash))
return nil, err
}
txResult := types.TxResult{
Height: value.Height,
Index: int32(value.Index),
Tx: value.Tx,
Receiptdate: value.Receipt,
Blocktime: value.Blocktime,
ActionName: value.ActionName,
}
return &txResult, nil
}
func (selldb *sellDB) getKVSet() (kvset []*types.KeyValue) {
value := types.Encode(&selldb.SellOrder)
key := []byte(selldb.SellID)
......
package executor
import (
"bytes"
"testing"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/stretchr/testify/assert"
)
func Test_Upgrade(t *testing.T) {
dir, db, localdb := util.CreateTestDB()
defer util.CloseTestDB(dir, db)
assert.NotNil(t, localdb)
// test empty db
err := callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
// test again
setVersion(localdb, 1)
err = callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
// test with data
// create for test
prefixes := []string{
sellOrderSHTAS,
sellOrderASTS,
sellOrderATSS,
sellOrderTSPAS,
buyOrderSHTAS,
buyOrderASTS,
buyOrderATSS,
buyOrderTSPAS,
orderASTHK,
}
localdb.Set([]byte(prefixes[0]+"xxxx1"), []byte("xx1"))
localdb.Set([]byte(prefixes[0]+"xxxx2"), []byte("xx2"))
localdb.Set([]byte(prefixes[0]+"xxxx3"), []byte("xx3"))
localdb.Set([]byte(prefixes[1]+"xxxx3"), []byte("xx3"))
//tabV2 := NewOrderTableV2(localdb)
tabV1 := NewOrderTable(localdb)
tabV1.Add(order1)
tabV1.Add(order2)
tabV1.Add(order3)
kvs, err := tabV1.Save()
assert.Nil(t, err)
for _, kv := range kvs {
localdb.Set(kv.Key, kv.Value)
}
// 初次升级
setVersion(localdb, 1)
err = callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
// 已经是升级后的版本了, 不需要再升级
err = callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
// 先修改版本去升级,但数据已经升级了, 所以处理数据量为0
setVersion(localdb, 1)
err = callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
// just print log
//assert.NotNil(t, nil)
}
func callUpgradeLocalDBV2(localdb dbm.KVDB) error {
return UpgradeLocalDBV2(localdb, "bty")
}
// 测试更新后是否删除完全, asset 设置
func Test_UpgradeOrderAsset(t *testing.T) {
dir, db, localdb := util.CreateTestDB()
defer util.CloseTestDB(dir, db)
assert.NotNil(t, localdb)
tabV1 := NewOrderTable(localdb)
tabV1.Add(order3)
kvs, err := tabV1.Save()
assert.Nil(t, err)
for _, kv := range kvs {
localdb.Set(kv.Key, kv.Value)
}
err = callUpgradeLocalDBV2(localdb)
assert.Nil(t, err)
v1, err := localdb.List([]byte("LODB-trade-order"), nil, 0, dbm.ListASC|dbm.ListWithKey)
assert.Nil(t, err)
assert.NotNil(t, v1)
primaryKey := "000000000000300001"
prefix := "LODB-trade-order_v2"
for _, v := range v1 {
var kv types.KeyValue
err := types.Decode(v, &kv)
assert.Nil(t, err)
// 前缀都是v2, 删除完成测试
if !bytes.Equal([]byte("LODB-trade-order_v2-d-000000000000300001"), kv.Key) {
assert.Equal(t, []byte(primaryKey), kv.Value)
assert.True(t, bytes.HasPrefix(kv.Key, []byte(prefix)))
}
}
// assert 前缀测试
v, err := localdb.Get([]byte("LODB-trade-order_v2-m-asset-coins.bty_token.CCNY-000000000000300001"))
assert.Nil(t, err)
assert.Equal(t, primaryKey, string(v))
// just print log
//assert.NotNil(t, nil)
}
......@@ -437,7 +437,7 @@ func (mvccs *KVMVCCStore) GetHashRdm(hash []byte, height int64) ([]byte, error)
func (mvccs *KVMVCCStore) GetFirstHashRdm(hash []byte) ([]byte, error) {
prefix := append(rdmHashPrefix, hash...)
list := dbm.NewListHelper(mvccs.db)
values := list.IteratorScanFromFirst(prefix, 1)
values := list.IteratorScanFromFirst(prefix, 1, dbm.ListASC)
if len(values) == 1 {
return values[0], nil
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment