Skip to content

Commit

Permalink
Merge pull request ethereum#597 from ngtuna/nil-node
Browse files Browse the repository at this point in the history
Nil node
  • Loading branch information
ngtuna committed Jul 30, 2019
2 parents a1f9df1 + 58ddd2f commit 66c9e64
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 22 deletions.
1 change: 1 addition & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ func (self *worker) commitNewWork() {
}
}

log.Debug("Start processing order pending")
txMatches := tomoX.ProcessOrderPending()
if len(txMatches) > 0 {
log.Debug("transaction matches found", "txMatches", txMatches)
Expand Down
19 changes: 19 additions & 0 deletions tomox/orderbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,19 +253,24 @@ func (orderBook *OrderBook) processLimitOrder(order *OrderItem, verbose bool, dr

if side == Bid {
minPrice := orderBook.Asks.MinPrice(dryrun)
log.Debug("Min price in asks tree", "price", minPrice.String())
for quantityToTrade.Cmp(zero) > 0 && orderBook.Asks.NotEmpty() && price.Cmp(minPrice) >= 0 {
bestPriceAsks := orderBook.Asks.MinPriceList(dryrun)
log.Debug("Orderlist at min price", "orderlist", bestPriceAsks.Item)
quantityToTrade, newTrades, orderInBook, err = orderBook.processOrderList(Ask, bestPriceAsks, quantityToTrade, order, verbose, dryrun)
if err != nil {
return nil, nil, err
}
trades = append(trades, newTrades...)
log.Debug("New trade found", "newTrades", newTrades, "orderInBook", orderInBook, "quantityToTrade", quantityToTrade)
minPrice = orderBook.Asks.MinPrice(dryrun)
log.Debug("New min price in asks tree", "price", minPrice.String())
}

if quantityToTrade.Cmp(zero) > 0 {
order.OrderID = orderBook.Item.NextOrderID
order.Quantity = quantityToTrade
log.Debug("After matching, order (unmatched part) is now added to bids tree", "order", order)
if err := orderBook.Bids.InsertOrder(order, dryrun); err != nil {
log.Error("Failed to insert order to bidTree", "pairName", order.PairName, "orderItem", order, "err", err)
return nil, nil, err
Expand All @@ -275,19 +280,24 @@ func (orderBook *OrderBook) processLimitOrder(order *OrderItem, verbose bool, dr

} else {
maxPrice := orderBook.Bids.MaxPrice(dryrun)
log.Debug("Max price in bids tree", "price", maxPrice.String())
for quantityToTrade.Cmp(zero) > 0 && orderBook.Bids.NotEmpty() && price.Cmp(maxPrice) <= 0 {
bestPriceBids := orderBook.Bids.MaxPriceList(dryrun)
log.Debug("Orderlist at max price", "orderlist", bestPriceBids.Item)
quantityToTrade, newTrades, orderInBook, err = orderBook.processOrderList(Bid, bestPriceBids, quantityToTrade, order, verbose, dryrun)
if err != nil {
return nil, nil, err
}
trades = append(trades, newTrades...)
log.Debug("New trade found", "newTrades", newTrades, "orderInBook", orderInBook, "quantityToTrade", quantityToTrade)
maxPrice = orderBook.Bids.MaxPrice(dryrun)
log.Debug("New max price in bids tree", "price", maxPrice.String())
}

if quantityToTrade.Cmp(zero) > 0 {
order.OrderID = orderBook.Item.NextOrderID
order.Quantity = quantityToTrade
log.Debug("After matching, order (unmatched part) is now back to asks tree", "order", order)
if err := orderBook.Asks.InsertOrder(order, dryrun); err != nil {
log.Error("Failed to insert order to askTree", "pairName", order.PairName, "orderItem", order, "err", err)
return nil, nil, err
Expand All @@ -311,11 +321,13 @@ func (orderBook *OrderBook) ProcessOrder(order *OrderItem, verbose bool, dryrun
orderBook.Item.NextOrderID++

if orderType == Market {
log.Debug("Process market order", "order", order)
trades, orderInBook, err = orderBook.processMarketOrder(order, verbose, dryrun)
if err != nil {
return nil, nil, err
}
} else {
log.Debug("Process limit order", "order", order)
trades, orderInBook, err = orderBook.processLimitOrder(order, verbose, dryrun)
if err != nil {
return nil, nil, err
Expand All @@ -332,6 +344,7 @@ func (orderBook *OrderBook) ProcessOrder(order *OrderItem, verbose bool, dryrun

// processOrderList : process the order list
func (orderBook *OrderBook) processOrderList(side string, orderList *OrderList, quantityStillToTrade *big.Int, order *OrderItem, verbose bool, dryrun bool) (*big.Int, []map[string]string, *OrderItem, error) {
log.Debug("Process matching between order and orderlist")
quantityToTrade := CloneBigInt(quantityStillToTrade)
var (
trades []map[string]string
Expand All @@ -345,6 +358,7 @@ func (orderBook *OrderBook) processOrderList(side string, orderList *OrderList,
if headOrder == nil {
return nil, nil, nil, fmt.Errorf("headOrder is null")
}
log.Debug("Get head order in the orderlist", "headOrder", headOrder.Item)

tradedPrice := CloneBigInt(headOrder.Item.Price)

Expand All @@ -360,6 +374,7 @@ func (orderBook *OrderBook) processOrderList(side string, orderList *OrderList,
if err := headOrder.UpdateQuantity(orderList, newBookQuantity, headOrder.Item.UpdatedAt, dryrun); err != nil {
return nil, nil, nil, err
}
log.Debug("Update quantity for head order", "headOrder", headOrder.Item)
quantityToTrade = Zero()
orderInBook = headOrder.Item
} else if IsEqual(quantityToTrade, headOrder.Item.Quantity) {
Expand All @@ -368,10 +383,12 @@ func (orderBook *OrderBook) processOrderList(side string, orderList *OrderList,
if err := orderBook.Bids.RemoveOrderFromOrderList(headOrder, orderList, dryrun); err != nil {
return nil, nil, nil, err
}
log.Debug("Removed headOrder from bids orderlist", "headOrder", headOrder.Item, "orderlist", orderList.Item, "side", side)
} else {
if err := orderBook.Asks.RemoveOrderFromOrderList(headOrder, orderList, dryrun); err != nil {
return nil, nil, nil, err
}
log.Debug("Removed headOrder from asks orderlist", "headOrder", headOrder.Item, "orderlist", orderList.Item, "side", side)
}
quantityToTrade = Zero()

Expand All @@ -381,10 +398,12 @@ func (orderBook *OrderBook) processOrderList(side string, orderList *OrderList,
if err := orderBook.Bids.RemoveOrderFromOrderList(headOrder, orderList, dryrun); err != nil {
return nil, nil, nil, err
}
log.Debug("Removed headOrder from bids orderlist", "headOrder", headOrder.Item, "orderlist", orderList.Item, "side", side)
} else {
if err := orderBook.Asks.RemoveOrderFromOrderList(headOrder, orderList, dryrun); err != nil {
return nil, nil, nil, err
}
log.Debug("Removed headOrder from asks orderlist", "headOrder", headOrder.Item, "orderlist", orderList.Item, "side", side)
}
quantityToTrade = Sub(quantityToTrade, tradedQuantity)
}
Expand Down
5 changes: 3 additions & 2 deletions tomox/orderlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func NewOrderListWithItem(item *OrderListItem, orderTree *OrderTree) *OrderList
func (orderList *OrderList) GetOrder(key []byte, dryrun bool) *Order {
// re-use method from orderbook, because orderlist has the same slot as orderbook
storedKey := orderList.GetOrderIDFromKey(key)
log.Debug("Get order from key", "storedKey", storedKey)
log.Debug("Get order from key", "storedKey", hex.EncodeToString(storedKey))
return orderList.orderTree.orderBook.GetOrder(storedKey, key, dryrun)
}

Expand Down Expand Up @@ -263,7 +263,9 @@ func (orderList *OrderList) RemoveOrder(order *Order, dryrun bool) error {
}

nextOrder := orderList.GetOrder(order.Item.NextOrder, dryrun)
log.Debug("Orderlist remove order debug", "nextOrder", nextOrder)
prevOrder := orderList.GetOrder(order.Item.PrevOrder, dryrun)
log.Debug("Orderlist remove order debug", "prevOrder", prevOrder)

orderList.Item.Volume = Sub(orderList.Item.Volume, order.Item.Quantity)
orderList.Item.Length--
Expand Down Expand Up @@ -298,7 +300,6 @@ func (orderList *OrderList) RemoveOrder(order *Order, dryrun bool) error {
orderList.Item.HeadOrder = EmptyKey()
orderList.Item.TailOrder = EmptyKey()
}

return nil
}

Expand Down
13 changes: 11 additions & 2 deletions tomox/ordertree.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,16 @@ func (orderTree *OrderTree) Depth() uint64 {
func (orderTree *OrderTree) RemovePrice(price *big.Int, dryrun bool) error {
if orderTree.Depth() > 0 {
priceKey := orderTree.getKeyFromPrice(price)
log.Debug("Remove price", "price", price.String(), "priceKey", hex.EncodeToString(priceKey))
orderListKey := GetOrderListCommonKey(priceKey)
log.Debug("Remove price", "price", price.String(), "orderListKey", hex.EncodeToString(orderListKey))
orderTree.PriceTree.Remove(orderListKey, dryrun)

log.Debug("Removed price from price tree", "price", price.String(), "orderListKey", hex.EncodeToString(orderListKey))
// should use batch to optimize the performance
if err := orderTree.Save(dryrun); err != nil {
return err
}
log.Debug("Remove price - saved ordertree")
}
return nil
}
Expand Down Expand Up @@ -313,18 +316,20 @@ func (orderTree *OrderTree) RemoveOrderFromOrderList(order *Order, orderList *Or
if err := orderList.RemoveOrder(order, dryrun); err != nil {
return err
}
log.Debug("Removed order from orderlist", "order", order.Item, "orderlist", orderList.Item)

// snapshot order list
if err := orderList.Save(dryrun); err != nil {
return err
}
log.Debug("Saved orderlist", "orderlist", orderList.Item)

// no items left than safety remove
if orderList.Item.Length == uint64(0) {
if err := orderTree.RemovePrice(order.Item.Price, dryrun); err != nil {
return err
}
log.Debug("remove price list", "price", order.Item.Price.String())
log.Debug("Removed price list", "price", order.Item.Price.String())
}

// update orderTree
Expand Down Expand Up @@ -398,14 +403,18 @@ func (orderTree *OrderTree) MaxPrice(dryrun bool) *big.Int {
// MinPrice : get the min price
func (orderTree *OrderTree) MinPrice(dryrun bool) *big.Int {
if orderTree.Depth() > 0 {
log.Debug("ordertree isn't empty")
if bytes, found := orderTree.PriceTree.GetMin(dryrun); found {
item, err := orderTree.getOrderListItem(bytes)
if err != nil {
log.Error("Failed to get orderlist with min price", "err", err)
return Zero()
}
if item != nil {
return CloneBigInt(item.Price)
}
} else {
log.Debug("Min not found")
}
}
return Zero()
Expand Down
53 changes: 36 additions & 17 deletions tomox/redblacktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"

"github.com/ethereum/go-ethereum/log"
"encoding/hex"
)

// Tree holds elements of the red-black tree
Expand Down Expand Up @@ -39,7 +40,11 @@ func NewWithBytesComparator(db OrderDao) *Tree {
}

func (tree *Tree) Root(dryrun bool) *Node {
root, _ := tree.GetNode(tree.rootKey, dryrun)
root, err := tree.GetNode(tree.rootKey, dryrun)
if err != nil {
log.Error("Can't get tree.Root", "rootKey", hex.EncodeToString(tree.rootKey), "err", err)
return nil
}
return root
}

Expand Down Expand Up @@ -150,10 +155,10 @@ func (tree *Tree) Get(key []byte, dryrun bool) (value []byte, found bool) {
func (tree *Tree) Remove(key []byte, dryrun bool) {
var child *Node
node, err := tree.GetNode(key, dryrun)

if err != nil || node == nil {
return
}
log.Debug("Get node", "node", node)

var left, right *Node = nil, nil
if !tree.IsEmptyKey(node.LeftKey()) {
Expand All @@ -173,19 +178,22 @@ func (tree *Tree) Remove(key []byte, dryrun bool) {
} else {
child = right
}
if child == nil {
tree.deleteNode(node, dryrun)
} else {
if node.Item.Color == black {
node.Item.Color = nodeColor(child)
tree.Save(node, dryrun)

if node.Item.Color == black {
node.Item.Color = nodeColor(child)
tree.Save(node, dryrun)

tree.deleteCase1(node, dryrun)
}
tree.deleteCase1(node, dryrun)
}

tree.replaceNode(node, child, dryrun)
tree.replaceNode(node, child, dryrun)

if tree.IsEmptyKey(node.ParentKey()) && child != nil {
child.Item.Color = black
tree.Save(child, dryrun)
if tree.IsEmptyKey(node.ParentKey()) && child != nil {
child.Item.Color = black
tree.Save(child, dryrun)
}
}
}

Expand Down Expand Up @@ -359,6 +367,7 @@ func output(tree *Tree, node *Node, prefix string, isTail bool, str *string, dry
}

func (tree *Tree) rotateLeft(node *Node, dryrun bool) {
log.Debug("Rotate left - before", "nodeRoot", hex.EncodeToString(tree.Root(dryrun).Value()))
right := node.Right(tree, dryrun)
tree.replaceNode(node, right, dryrun)
node.RightKey(right.LeftKey())
Expand All @@ -371,9 +380,11 @@ func (tree *Tree) rotateLeft(node *Node, dryrun bool) {
node.ParentKey(right.Key)
tree.Save(node, dryrun)
tree.Save(right, dryrun)
log.Debug("Rotate left - after", "nodeRoot", hex.EncodeToString(tree.Root(dryrun).Value()))
}

func (tree *Tree) rotateRight(node *Node, dryrun bool) {
log.Debug("Rotate right - before", "nodeRoot", hex.EncodeToString(tree.Root(dryrun).Value()))
left := node.Left(tree, dryrun)
tree.replaceNode(node, left, dryrun)
node.LeftKey(left.RightKey())
Expand All @@ -386,9 +397,11 @@ func (tree *Tree) rotateRight(node *Node, dryrun bool) {
node.ParentKey(left.Key)
tree.Save(node, dryrun)
tree.Save(left, dryrun)
log.Debug("Rotate right - after", "nodeRoot", hex.EncodeToString(tree.Root(dryrun).Value()))
}

func (tree *Tree) replaceNode(old *Node, new *Node, dryrun bool) {
log.Debug("Replace node", "old", old, "new", new)

// we do not change any byte of Key so we can copy the reference to save directly to db
var newKey []byte
Expand All @@ -410,8 +423,9 @@ func (tree *Tree) replaceNode(old *Node, new *Node, dryrun bool) {
oldParent.RightKey(newKey)
}
// we can have case like: remove a node, then add it again
tree.Save(oldParent, dryrun)
// }
if oldParent != nil {
tree.Save(oldParent, dryrun)
}
}
if new != nil {
// here is the swap, not update key
Expand Down Expand Up @@ -503,10 +517,12 @@ func (tree *Tree) insertCase5(node *Node, dryrun bool) {
}

func (tree *Tree) Save(node *Node, dryrun bool) error {
log.Debug("Save node", "node", node)
return tree.db.Put(node.Key, node.Item, dryrun)
}

func (tree *Tree) deleteCase1(node *Node, dryrun bool) {
log.Debug("delete case 1", "node value", hex.EncodeToString(node.Value()))
if tree.IsEmptyKey(node.ParentKey()) {
tree.deleteNode(node, dryrun)
return
Expand All @@ -516,6 +532,7 @@ func (tree *Tree) deleteCase1(node *Node, dryrun bool) {
}

func (tree *Tree) deleteCase2(node *Node, dryrun bool) {
log.Debug("delete case 2", "node value", hex.EncodeToString(node.Value()))
parent := node.Parent(tree, dryrun)
sibling := node.sibling(tree, dryrun)

Expand All @@ -535,7 +552,7 @@ func (tree *Tree) deleteCase2(node *Node, dryrun bool) {
}

func (tree *Tree) deleteCase3(node *Node, dryrun bool) {

log.Debug("delete case 3", "node value", hex.EncodeToString(node.Value()))
parent := node.Parent(tree, dryrun)
sibling := node.sibling(tree, dryrun)
siblingLeft := sibling.Left(tree, dryrun)
Expand All @@ -548,16 +565,15 @@ func (tree *Tree) deleteCase3(node *Node, dryrun bool) {
sibling.Item.Color = red
tree.Save(sibling, dryrun)
tree.deleteCase1(parent, dryrun)
log.Debug("delete node, key: %x, parentKey :%x\n", node.Key, parent.Key)
tree.deleteNode(node, dryrun)

} else {
tree.deleteCase4(node, dryrun)
}

}

func (tree *Tree) deleteCase4(node *Node, dryrun bool) {
log.Debug("delete case 4", "node value", hex.EncodeToString(node.Value()))
parent := node.Parent(tree, dryrun)
sibling := node.sibling(tree, dryrun)
siblingLeft := sibling.Left(tree, dryrun)
Expand All @@ -577,6 +593,7 @@ func (tree *Tree) deleteCase4(node *Node, dryrun bool) {
}

func (tree *Tree) deleteCase5(node *Node, dryrun bool) {
log.Debug("delete case 5", "node value", hex.EncodeToString(node.Value()))
parent := node.Parent(tree, dryrun)
sibling := node.sibling(tree, dryrun)
siblingLeft := sibling.Left(tree, dryrun)
Expand Down Expand Up @@ -612,6 +629,7 @@ func (tree *Tree) deleteCase5(node *Node, dryrun bool) {
}

func (tree *Tree) deleteCase6(node *Node, dryrun bool) {
log.Debug("delete case 6", "node value", hex.EncodeToString(node.Value()))
parent := node.Parent(tree, dryrun)
sibling := node.sibling(tree, dryrun)
siblingLeft := sibling.Left(tree, dryrun)
Expand Down Expand Up @@ -645,5 +663,6 @@ func nodeColor(node *Node) bool {
}

func (tree *Tree) deleteNode(node *Node, dryrun bool) {
log.Debug("Delete node", "node value", hex.EncodeToString(node.Value()))
tree.db.Delete(node.Key, dryrun)
}
2 changes: 1 addition & 1 deletion tomox/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func prepareOrderTreeData(tree *OrderTree) (*OrderTreeSnapshot, error) {
snap.OrderTreeItem = serializedTree

snap.OrderList = make(map[common.Hash]*OrderListSnapshot)
if !tree.NotEmpty() {
if !tree.NotEmpty() || tree.PriceTree.Size() == 0 {
return snap, nil
}
// foreach each price, snapshot its orderlist
Expand Down
Loading

0 comments on commit 66c9e64

Please sign in to comment.