go-ethereum/XDCx/tradingstate/state_orderbook.go

816 lines
27 KiB
Go

// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package tradingstate
import (
"fmt"
"io"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
// stateObject represents an Ethereum orderId which is being modified.
//
// The usage pattern is as follows:
// First you need to obtain a state object.
// tradingExchangeObject values can be accessed and modified through the object.
// Finally, call CommitAskTrie to write the modified storage trie into a database.
type tradingExchanges struct {
orderBookHash common.Hash
data tradingExchangeObject
db *TradingStateDB
// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
// during a database read is memoized here and will eventually be returned
// by TradingStateDB.Commit.
dbErr error
// Write caches.
asksTrie Trie // storage trie, which becomes non-nil on first access
bidsTrie Trie // storage trie, which becomes non-nil on first access
ordersTrie Trie // storage trie, which becomes non-nil on first access
liquidationPriceTrie Trie
stateAskObjects map[common.Hash]*stateOrderList
stateAskObjectsDirty map[common.Hash]struct{}
stateBidObjects map[common.Hash]*stateOrderList
stateBidObjectsDirty map[common.Hash]struct{}
stateOrderObjects map[common.Hash]*stateOrderItem
stateOrderObjectsDirty map[common.Hash]struct{}
liquidationPriceStates map[common.Hash]*liquidationPriceState
liquidationPriceStatesDirty map[common.Hash]struct{}
onDirty func(hash common.Hash) // Callback method to mark a state object newly dirty
}
// empty returns whether the orderId is considered empty.
func (te *tradingExchanges) empty() bool {
if te.data.Nonce != 0 {
return false
}
if te.data.LendingCount != nil && te.data.LendingCount.Sign() > 0 {
return false
}
if te.data.LastPrice != nil && te.data.LastPrice.Sign() > 0 {
return false
}
if te.data.MediumPrice != nil && te.data.MediumPrice.Sign() > 0 {
return false
}
if te.data.MediumPriceBeforeEpoch != nil && te.data.MediumPriceBeforeEpoch.Sign() > 0 {
return false
}
if te.data.TotalQuantity != nil && te.data.TotalQuantity.Sign() > 0 {
return false
}
if !te.data.AskRoot.IsZero() {
return false
}
if !te.data.BidRoot.IsZero() {
return false
}
if !te.data.OrderRoot.IsZero() {
return false
}
if !te.data.LiquidationPriceRoot.IsZero() {
return false
}
return true
}
// newObject creates a state object.
func newStateExchanges(db *TradingStateDB, hash common.Hash, data tradingExchangeObject, onDirty func(addr common.Hash)) *tradingExchanges {
return &tradingExchanges{
db: db,
orderBookHash: hash,
data: data,
stateAskObjects: make(map[common.Hash]*stateOrderList),
stateBidObjects: make(map[common.Hash]*stateOrderList),
stateOrderObjects: make(map[common.Hash]*stateOrderItem),
liquidationPriceStates: make(map[common.Hash]*liquidationPriceState),
stateAskObjectsDirty: make(map[common.Hash]struct{}),
stateBidObjectsDirty: make(map[common.Hash]struct{}),
stateOrderObjectsDirty: make(map[common.Hash]struct{}),
liquidationPriceStatesDirty: make(map[common.Hash]struct{}),
onDirty: onDirty,
}
}
// EncodeRLP implements rlp.Encoder.
func (te *tradingExchanges) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, te.data)
}
// setError remembers the first non-nil error it is called with.
func (te *tradingExchanges) setError(err error) {
if te.dbErr == nil {
te.dbErr = err
}
}
func (te *tradingExchanges) getAsksTrie(db Database) Trie {
if te.asksTrie == nil {
var err error
te.asksTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.AskRoot)
if err != nil {
te.asksTrie, _ = db.OpenStorageTrie(te.orderBookHash, types.EmptyRootHash)
te.setError(fmt.Errorf("can't create asks trie: %v", err))
}
}
return te.asksTrie
}
func (te *tradingExchanges) getOrdersTrie(db Database) Trie {
if te.ordersTrie == nil {
var err error
te.ordersTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.OrderRoot)
if err != nil {
te.ordersTrie, _ = db.OpenStorageTrie(te.orderBookHash, types.EmptyRootHash)
te.setError(fmt.Errorf("can't create asks trie: %v", err))
}
}
return te.ordersTrie
}
func (te *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash {
trie := te.getAsksTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
log.Error("Failed find best price ask trie ", "orderbook", te.orderBookHash.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
log.Debug("Not found get best ask trie", "encKey", encKey, "encValue", encValue)
return EmptyHash
}
price := common.BytesToHash(encKey)
if _, exit := te.stateAskObjects[price]; !exit {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash
}
obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty)
te.stateAskObjects[price] = obj
}
return common.BytesToHash(encKey)
}
func (te *tradingExchanges) getBestBidsTrie(db Database) common.Hash {
trie := te.getBidsTrie(db)
encKey, encValue, err := trie.TryGetBestRightKeyAndValue()
if err != nil {
log.Error("Failed find best price bid trie ", "orderbook", te.orderBookHash.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
log.Debug("Not found get best bid trie", "encKey", encKey, "encValue", encValue)
return EmptyHash
}
price := common.BytesToHash(encKey)
if _, exit := te.stateBidObjects[price]; !exit {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best bid trie", "err", err)
return EmptyHash
}
// Insert into the live set.
obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty)
te.stateBidObjects[price] = obj
}
return common.BytesToHash(encKey)
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
func (te *tradingExchanges) updateAsksTrie(db Database) Trie {
tr := te.getAsksTrie(db)
for price, orderList := range te.stateAskObjects {
if _, isDirty := te.stateAskObjectsDirty[price]; isDirty {
delete(te.stateAskObjectsDirty, price)
if orderList.empty() {
te.setError(tr.TryDelete(price[:]))
continue
}
err := orderList.updateRoot(db)
if err != nil {
log.Warn("updateAsksTrie updateRoot", "err", err, "price", price, "orderList", *orderList)
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
te.setError(tr.TryUpdate(price[:], v))
}
}
return tr
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
func (te *tradingExchanges) updateAsksRoot(db Database) error {
te.updateAsksTrie(db)
if te.dbErr != nil {
return te.dbErr
}
te.data.AskRoot = te.asksTrie.Hash()
return nil
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
func (te *tradingExchanges) CommitAsksTrie(db Database) error {
te.updateAsksTrie(db)
if te.dbErr != nil {
return te.dbErr
}
root, err := te.asksTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
}
if orderList.Root != EmptyRoot {
db.TrieDB().Reference(orderList.Root, parent)
}
return nil
})
if err == nil {
te.data.AskRoot = root
}
return err
}
func (te *tradingExchanges) getBidsTrie(db Database) Trie {
if te.bidsTrie == nil {
var err error
te.bidsTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.BidRoot)
if err != nil {
te.bidsTrie, _ = db.OpenStorageTrie(te.orderBookHash, types.EmptyRootHash)
te.setError(fmt.Errorf("can't create bids trie: %v", err))
}
}
return te.bidsTrie
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
func (te *tradingExchanges) updateBidsTrie(db Database) Trie {
tr := te.getBidsTrie(db)
for price, orderList := range te.stateBidObjects {
if _, isDirty := te.stateBidObjectsDirty[price]; isDirty {
delete(te.stateBidObjectsDirty, price)
if orderList.empty() {
te.setError(tr.TryDelete(price[:]))
continue
}
err := orderList.updateRoot(db)
if err != nil {
log.Warn("updateBidsTrie updateRoot", "err", err, "price", price, "orderList", *orderList)
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
te.setError(tr.TryUpdate(price[:], v))
}
}
return tr
}
func (te *tradingExchanges) updateBidsRoot(db Database) {
te.updateBidsTrie(db)
te.data.BidRoot = te.bidsTrie.Hash()
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
func (te *tradingExchanges) CommitBidsTrie(db Database) error {
te.updateBidsTrie(db)
if te.dbErr != nil {
return te.dbErr
}
root, err := te.bidsTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
}
if orderList.Root != EmptyRoot {
db.TrieDB().Reference(orderList.Root, parent)
}
return nil
})
if err == nil {
te.data.BidRoot = root
}
return err
}
func (te *tradingExchanges) deepCopy(db *TradingStateDB, onDirty func(hash common.Hash)) *tradingExchanges {
stateExchanges := newStateExchanges(db, te.orderBookHash, te.data, onDirty)
if te.asksTrie != nil {
stateExchanges.asksTrie = db.db.CopyTrie(te.asksTrie)
}
if te.bidsTrie != nil {
stateExchanges.bidsTrie = db.db.CopyTrie(te.bidsTrie)
}
if te.ordersTrie != nil {
stateExchanges.ordersTrie = db.db.CopyTrie(te.ordersTrie)
}
for price, bidObject := range te.stateBidObjects {
stateExchanges.stateBidObjects[price] = bidObject.deepCopy(db, te.MarkStateBidObjectDirty)
}
for price := range te.stateBidObjectsDirty {
stateExchanges.stateBidObjectsDirty[price] = struct{}{}
}
for price, askObject := range te.stateAskObjects {
stateExchanges.stateAskObjects[price] = askObject.deepCopy(db, te.MarkStateAskObjectDirty)
}
for price := range te.stateAskObjectsDirty {
stateExchanges.stateAskObjectsDirty[price] = struct{}{}
}
for orderId, orderItem := range te.stateOrderObjects {
stateExchanges.stateOrderObjects[orderId] = orderItem.deepCopy(te.MarkStateOrderObjectDirty)
}
for orderId := range te.stateOrderObjectsDirty {
stateExchanges.stateOrderObjectsDirty[orderId] = struct{}{}
}
for price, liquidationPrice := range te.liquidationPriceStates {
stateExchanges.liquidationPriceStates[price] = liquidationPrice.deepCopy(db, te.MarkStateLiquidationPriceDirty)
}
for price := range te.liquidationPriceStatesDirty {
stateExchanges.liquidationPriceStatesDirty[price] = struct{}{}
}
return stateExchanges
}
// Returns the address of the contract/orderId
func (te *tradingExchanges) Hash() common.Hash {
return te.orderBookHash
}
func (te *tradingExchanges) SetNonce(nonce uint64) {
te.setNonce(nonce)
}
func (te *tradingExchanges) setNonce(nonce uint64) {
te.data.Nonce = nonce
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
func (te *tradingExchanges) Nonce() uint64 {
return te.data.Nonce
}
func (te *tradingExchanges) setLastPrice(price *big.Int) {
te.data.LastPrice = price
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
func (te *tradingExchanges) setMediumPriceBeforeEpoch(price *big.Int) {
te.data.MediumPriceBeforeEpoch = price
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
func (te *tradingExchanges) setMediumPrice(price *big.Int, quantity *big.Int) {
te.data.MediumPrice = price
te.data.TotalQuantity = quantity
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
// updateStateExchangeObject writes the given object to the trie.
func (te *tradingExchanges) removeStateOrderListAskObject(db Database, stateOrderList *stateOrderList) {
te.setError(te.asksTrie.TryDelete(stateOrderList.price[:]))
}
// updateStateExchangeObject writes the given object to the trie.
func (te *tradingExchanges) removeStateOrderListBidObject(db Database, stateOrderList *stateOrderList) {
te.setError(te.bidsTrie.TryDelete(stateOrderList.price[:]))
}
// Retrieve a state object given my the address. Returns nil if not found.
func (te *tradingExchanges) getStateOrderListAskObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
// Prefer 'live' objects.
if obj := te.stateAskObjects[price]; obj != nil {
return obj
}
// Load the object from the database.
enc, err := te.getAsksTrie(db).TryGet(price[:])
if len(enc) == 0 {
te.setError(err)
return nil
}
var data orderList
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state order list object", "orderId", price, "err", err)
return nil
}
// Insert into the live set.
obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty)
te.stateAskObjects[price] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (te *tradingExchanges) MarkStateAskObjectDirty(price common.Hash) {
te.stateAskObjectsDirty[price] = struct{}{}
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
func (te *tradingExchanges) createStateOrderListAskObject(db Database, price common.Hash) (newobj *stateOrderList) {
newobj = newStateOrderList(te.db, Ask, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateAskObjectDirty)
te.stateAskObjects[price] = newobj
te.stateAskObjectsDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
te.setError(te.asksTrie.TryUpdate(price[:], data))
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
return newobj
}
// Retrieve a state object given my the address. Returns nil if not found.
func (te *tradingExchanges) getStateBidOrderListObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
// Prefer 'live' objects.
if obj := te.stateBidObjects[price]; obj != nil {
return obj
}
// Load the object from the database.
enc, err := te.getBidsTrie(db).TryGet(price[:])
if len(enc) == 0 {
te.setError(err)
return nil
}
var data orderList
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state order list object", "orderId", price, "err", err)
return nil
}
// Insert into the live set.
obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty)
te.stateBidObjects[price] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (te *tradingExchanges) MarkStateBidObjectDirty(price common.Hash) {
te.stateBidObjectsDirty[price] = struct{}{}
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
func (te *tradingExchanges) createStateBidOrderListObject(db Database, price common.Hash) (newobj *stateOrderList) {
newobj = newStateOrderList(te.db, Bid, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateBidObjectDirty)
te.stateBidObjects[price] = newobj
te.stateBidObjectsDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
te.setError(te.bidsTrie.TryUpdate(price[:], data))
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
return newobj
}
// Retrieve a state object given my the address. Returns nil if not found.
func (te *tradingExchanges) getStateOrderObject(db Database, orderId common.Hash) (stateOrderItem *stateOrderItem) {
// Prefer 'live' objects.
if obj := te.stateOrderObjects[orderId]; obj != nil {
return obj
}
// Load the object from the database.
enc, err := te.getOrdersTrie(db).TryGet(orderId[:])
if len(enc) == 0 {
te.setError(err)
return nil
}
var data OrderItem
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state order object", "orderId", orderId, "err", err)
return nil
}
// Insert into the live set.
obj := newStateOrderItem(te.orderBookHash, orderId, data, te.MarkStateOrderObjectDirty)
te.stateOrderObjects[orderId] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (te *tradingExchanges) MarkStateOrderObjectDirty(orderId common.Hash) {
te.stateOrderObjectsDirty[orderId] = struct{}{}
if te.onDirty != nil {
te.onDirty(te.Hash())
te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
func (t *tradingExchanges) createStateOrderObject(db Database, orderId common.Hash, order OrderItem) (newobj *stateOrderItem) {
newobj = newStateOrderItem(t.orderBookHash, orderId, order, t.MarkStateOrderObjectDirty)
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID))
t.stateOrderObjects[orderIdHash] = newobj
t.stateOrderObjectsDirty[orderIdHash] = struct{}{}
if t.onDirty != nil {
t.onDirty(t.orderBookHash)
t.onDirty = nil
}
return newobj
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
func (t *tradingExchanges) updateOrdersTrie(db Database) Trie {
tr := t.getOrdersTrie(db)
for orderId, orderItem := range t.stateOrderObjects {
if _, isDirty := t.stateOrderObjectsDirty[orderId]; isDirty {
delete(t.stateOrderObjectsDirty, orderId)
if orderItem.empty() {
t.setError(tr.TryDelete(orderId[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderItem)
t.setError(tr.TryUpdate(orderId[:], v))
}
}
return tr
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
func (t *tradingExchanges) updateOrdersRoot(db Database) {
t.updateOrdersTrie(db)
t.data.OrderRoot = t.ordersTrie.Hash()
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
func (t *tradingExchanges) CommitOrdersTrie(db Database) error {
t.updateOrdersTrie(db)
if t.dbErr != nil {
return t.dbErr
}
root, err := t.ordersTrie.Commit(nil)
if err == nil {
t.data.OrderRoot = root
}
return err
}
func (t *tradingExchanges) MarkStateLiquidationPriceDirty(price common.Hash) {
t.liquidationPriceStatesDirty[price] = struct{}{}
if t.onDirty != nil {
t.onDirty(t.Hash())
t.onDirty = nil
}
}
func (t *tradingExchanges) createStateLiquidationPrice(db Database, liquidationPrice common.Hash) (newobj *liquidationPriceState) {
newobj = newLiquidationPriceState(t.db, t.orderBookHash, liquidationPrice, orderList{Volume: Zero}, t.MarkStateLiquidationPriceDirty)
t.liquidationPriceStates[liquidationPrice] = newobj
t.liquidationPriceStatesDirty[liquidationPrice] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode liquidation price object at %x: %v", liquidationPrice[:], err))
}
t.setError(t.getLiquidationPriceTrie(db).TryUpdate(liquidationPrice[:], data))
if t.onDirty != nil {
t.onDirty(t.Hash())
t.onDirty = nil
}
return newobj
}
func (t *tradingExchanges) getLiquidationPriceTrie(db Database) Trie {
if t.liquidationPriceTrie == nil {
var err error
t.liquidationPriceTrie, err = db.OpenStorageTrie(t.orderBookHash, t.data.LiquidationPriceRoot)
if err != nil {
t.liquidationPriceTrie, _ = db.OpenStorageTrie(t.orderBookHash, types.EmptyRootHash)
t.setError(fmt.Errorf("can't create liquidation liquidationPrice trie: %v", err))
}
}
return t.liquidationPriceTrie
}
func (t *tradingExchanges) getStateLiquidationPrice(db Database, price common.Hash) (stateObject *liquidationPriceState) {
// Prefer 'live' objects.
if obj := t.liquidationPriceStates[price]; obj != nil {
return obj
}
// Load the object from the database.
enc, err := t.getLiquidationPriceTrie(db).TryGet(price[:])
if len(enc) == 0 {
t.setError(err)
return nil
}
var data orderList
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state liquidation liquidationPrice", "liquidationPrice", price, "err", err)
return nil
}
// Insert into the live set.
obj := newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
t.liquidationPriceStates[price] = obj
return obj
}
func (t *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
trie := t.getLiquidationPriceTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex())
return EmptyHash, nil
}
if len(encKey) == 0 || len(encValue) == 0 {
log.Debug("Not found get best ask trie", "encKey", encKey, "encValue", encValue)
return EmptyHash, nil
}
price := common.BytesToHash(encKey)
obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash, nil
}
obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
t.liquidationPriceStates[price] = obj
}
return price, obj
}
func (t *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit common.Hash) map[common.Hash]*liquidationPriceState {
trie := t.getLiquidationPriceTrie(db)
encKeys, encValues, err := trie.TryGetAllLeftKeyAndValue(limit.Bytes())
result := map[common.Hash]*liquidationPriceState{}
if err != nil || len(encKeys) != len(encValues) {
log.Error("Failed get lower liquidation price trie ", "orderbook", t.orderBookHash.Hex(), "encKeys", len(encKeys), "encValues", len(encValues))
return result
}
if len(encKeys) == 0 || len(encValues) == 0 {
log.Debug("Not found get lower liquidation price trie ", "limit", limit)
return result
}
for i := range encKeys {
price := common.BytesToHash(encKeys[i])
obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValues[i], &data); err != nil {
log.Error("Failed to decode state get all lower liquidation price trie", "price", price, "encValues", encValues[i], "err", err)
return result
}
obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
t.liquidationPriceStates[price] = obj
}
if obj.empty() {
continue
}
result[price] = obj
}
return result
}
func (t *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
trie := t.getLiquidationPriceTrie(db)
encKey, encValue, err := trie.TryGetBestRightKeyAndValue()
if err != nil {
log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex())
return EmptyHash, nil
}
if len(encKey) == 0 || len(encValue) == 0 {
log.Debug("Not found get best ask trie", "encKey", encKey, "encValue", encValue)
return EmptyHash, nil
}
price := common.BytesToHash(encKey)
obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash, nil
}
obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
t.liquidationPriceStates[price] = obj
}
if obj.empty() {
return EmptyHash, nil
}
return price, obj
}
func (t *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie {
tr := t.getLiquidationPriceTrie(db)
for price, stateObject := range t.liquidationPriceStates {
if _, isDirty := t.liquidationPriceStatesDirty[price]; isDirty {
delete(t.liquidationPriceStatesDirty, price)
if stateObject.empty() {
t.setError(tr.TryDelete(price[:]))
continue
}
err := stateObject.updateRoot(db)
if err != nil {
log.Warn("updateLiquidationPriceTrie updateRoot", "err", err, "price", price, "stateObject", *stateObject)
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(stateObject)
t.setError(tr.TryUpdate(price[:], v))
}
}
return tr
}
func (t *tradingExchanges) updateLiquidationPriceRoot(db Database) {
t.updateLiquidationPriceTrie(db)
t.data.LiquidationPriceRoot = t.liquidationPriceTrie.Hash()
}
func (t *tradingExchanges) CommitLiquidationPriceTrie(db Database) error {
t.updateLiquidationPriceTrie(db)
if t.dbErr != nil {
return t.dbErr
}
root, err := t.liquidationPriceTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
}
if orderList.Root != EmptyRoot {
db.TrieDB().Reference(orderList.Root, parent)
}
return nil
})
if err == nil {
t.data.LiquidationPriceRoot = root
}
return err
}
func (t *tradingExchanges) addLendingCount(amount *big.Int) {
t.setLendingCount(new(big.Int).Add(t.data.LendingCount, amount))
}
func (t *tradingExchanges) subLendingCount(amount *big.Int) {
t.setLendingCount(new(big.Int).Sub(t.data.LendingCount, amount))
}
func (t *tradingExchanges) setLendingCount(volume *big.Int) {
t.data.LendingCount = volume
if t.onDirty != nil {
t.onDirty(t.orderBookHash)
t.onDirty = nil
}
}