go-ethereum/XDCx/tradingstate/statedb.go

734 lines
26 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 state provides a caching layer atop the Ethereum state trie.
package tradingstate
import (
"fmt"
"math/big"
"sort"
"sync"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
type revision struct {
id int
journalIndex int
}
// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
// * Contracts
// * Accounts
type TradingStateDB struct {
db Database
trie Trie
// This map holds 'live' objects, which will get modified while processing a state transition.
stateExhangeObjects map[common.Hash]*tradingExchanges
stateExhangeObjectsDirty map[common.Hash]struct{}
// 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
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal journal
validRevisions []revision
nextRevisionId int
lock sync.Mutex
}
// Create a new state from a given trie.
func New(root common.Hash, db Database) (*TradingStateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
log.Error("Error when init new trading state trie ", "root", root.Hex(), "err", err)
return nil, err
}
return &TradingStateDB{
db: db,
trie: tr,
stateExhangeObjects: make(map[common.Hash]*tradingExchanges),
stateExhangeObjectsDirty: make(map[common.Hash]struct{}),
}, nil
}
// setError remembers the first non-nil error it is called with.
func (t *TradingStateDB) setError(err error) {
if t.dbErr == nil {
t.dbErr = err
}
}
func (t *TradingStateDB) Error() error {
return t.dbErr
}
// Exist reports whether the given orderId address exists in the state.
// Notably this also returns true for self-destructed exchanges.
func (t *TradingStateDB) Exist(addr common.Hash) bool {
return t.getStateExchangeObject(addr) != nil
}
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
func (t *TradingStateDB) Empty(addr common.Hash) bool {
so := t.getStateExchangeObject(addr)
return so == nil || so.empty()
}
func (t *TradingStateDB) GetNonce(addr common.Hash) uint64 {
stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.Nonce()
}
return 0
}
func (t *TradingStateDB) GetLastPrice(addr common.Hash) *big.Int {
stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.LastPrice
}
return nil
}
func (t *TradingStateDB) GetMediumPriceBeforeEpoch(addr common.Hash) *big.Int {
stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.MediumPriceBeforeEpoch
}
return Zero
}
func (t *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big.Int, *big.Int) {
stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.MediumPrice, stateObject.data.TotalQuantity
}
return Zero, Zero
}
// Database retrieves the low level database supporting the lower level trie ops.
func (t *TradingStateDB) Database() Database {
return t.db
}
func (t *TradingStateDB) SetNonce(addr common.Hash, nonce uint64) {
stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
t.journal = append(t.journal, nonceChange{
hash: addr,
prev: t.GetNonce(addr),
})
stateObject.SetNonce(nonce)
}
}
func (t *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) {
stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
t.journal = append(t.journal, lastPriceChange{
hash: addr,
prev: stateObject.data.LastPrice,
})
stateObject.setLastPrice(price)
}
}
func (t *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, quantity *big.Int) {
stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
t.journal = append(t.journal, mediumPriceChange{
hash: addr,
prevPrice: stateObject.data.MediumPrice,
prevQuantity: stateObject.data.TotalQuantity,
})
stateObject.setMediumPrice(price, quantity)
}
}
func (t *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *big.Int) {
stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
t.journal = append(t.journal, mediumPriceBeforeEpochChange{
hash: addr,
prevPrice: stateObject.data.MediumPriceBeforeEpoch,
})
stateObject.setMediumPriceBeforeEpoch(price)
}
}
func (t *TradingStateDB) InsertOrderItem(orderBook common.Hash, orderId common.Hash, order OrderItem) {
priceHash := common.BigToHash(order.Price)
stateExchange := t.getStateExchangeObject(orderBook)
if stateExchange == nil {
stateExchange = t.createExchangeObject(orderBook)
}
var stateOrderList *stateOrderList
switch order.Side {
case Ask:
stateOrderList = stateExchange.getStateOrderListAskObject(t.db, priceHash)
if stateOrderList == nil {
stateOrderList = stateExchange.createStateOrderListAskObject(t.db, priceHash)
}
case Bid:
stateOrderList = stateExchange.getStateBidOrderListObject(t.db, priceHash)
if stateOrderList == nil {
stateOrderList = stateExchange.createStateBidOrderListObject(t.db, priceHash)
}
default:
return
}
t.journal = append(t.journal, insertOrder{
orderBook: orderBook,
orderId: orderId,
order: &order,
})
stateExchange.createStateOrderObject(t.db, orderId, order)
stateOrderList.insertOrderItem(t.db, orderId, common.BigToHash(order.Quantity))
stateOrderList.AddVolume(order.Quantity)
}
func (t *TradingStateDB) GetOrder(orderBook common.Hash, orderId common.Hash) OrderItem {
stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
return EmptyOrder
}
stateOrderItem := stateObject.getStateOrderObject(t.db, orderId)
if stateOrderItem == nil {
return EmptyOrder
}
return stateOrderItem.data
}
func (t *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
priceHash := common.BigToHash(price)
stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
return fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
var stateOrderList *stateOrderList
switch side {
case Ask:
stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash)
case Bid:
stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash)
default:
return fmt.Errorf("not found order type: %s", side)
}
if stateOrderList == nil || stateOrderList.empty() {
return fmt.Errorf("empty Orderlist: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
stateOrderItem := stateObject.getStateOrderObject(t.db, orderId)
if stateOrderItem == nil || stateOrderItem.empty() {
return fmt.Errorf("empty OrderItem: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderId).Bytes()[:])
if currentAmount.Cmp(amount) < 0 {
return fmt.Errorf("not enough order amount: %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount)
}
t.journal = append(t.journal, subAmountOrder{
orderBook: orderBook,
orderId: orderId,
order: t.GetOrder(orderBook, orderId),
amount: amount,
})
newAmount := new(big.Int).Sub(currentAmount, amount)
log.Debug("SubAmountOrderItem", "orderId", orderId.Hex(), "side", side, "price", price.Uint64(), "amount", amount.Uint64(), "new amount", newAmount.Uint64())
stateOrderList.subVolume(amount)
stateOrderItem.setVolume(newAmount)
if newAmount.Sign() == 0 {
stateOrderList.removeOrderItem(t.db, orderId)
} else {
stateOrderList.setOrderItem(orderId, common.BigToHash(newAmount))
}
if stateOrderList.empty() {
switch side {
case Ask:
stateObject.removeStateOrderListAskObject(t.db, stateOrderList)
case Bid:
stateObject.removeStateOrderListBidObject(t.db, stateOrderList)
default:
}
}
return nil
}
func (t *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) error {
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID))
stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
return fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
stateOrderItem := stateObject.getStateOrderObject(t.db, orderIdHash)
if stateOrderItem == nil || stateOrderItem.empty() {
return fmt.Errorf("empty OrderItem: order book: %s , order id : %s", orderBook, orderIdHash.Hex())
}
priceHash := common.BigToHash(stateOrderItem.data.Price)
var stateOrderList *stateOrderList
switch stateOrderItem.data.Side {
case Ask:
stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash)
case Bid:
stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash)
default:
return fmt.Errorf("not found order.Side: %s", order.Side)
}
if stateOrderList == nil || stateOrderList.empty() {
return fmt.Errorf("empty OrderList: order book: %s , order id : %s , price : %s", orderBook, orderIdHash.Hex(), priceHash.Hex())
}
if stateOrderItem.data.UserAddress != order.UserAddress {
return fmt.Errorf("error Order UserAddress mismatch when cancel: order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), stateOrderItem.data.UserAddress.Hex(), order.UserAddress.Hex())
}
if stateOrderItem.data.Hash != order.Hash {
return fmt.Errorf("invalid order hash: got : %s , expect : %s", order.Hash.Hex(), stateOrderItem.data.Hash.Hex())
}
if stateOrderItem.data.ExchangeAddress != order.ExchangeAddress {
return fmt.Errorf("mismatch ExchangeAddress when cancel: order book : %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex())
}
t.journal = append(t.journal, cancelOrder{
orderBook: orderBook,
orderId: orderIdHash,
order: stateOrderItem.data,
})
currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderIdHash).Bytes()[:])
stateOrderItem.setVolume(big.NewInt(0))
stateOrderList.subVolume(currentAmount)
stateOrderList.removeOrderItem(t.db, orderIdHash)
if stateOrderList.empty() {
switch stateOrderItem.data.Side {
case Ask:
stateObject.removeStateOrderListAskObject(t.db, stateOrderList)
case Bid:
stateObject.removeStateOrderListBidObject(t.db, stateOrderList)
default:
}
}
return nil
}
func (t *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, orderType string) *big.Int {
stateObject := t.GetOrNewStateExchangeObject(orderBook)
var volume *big.Int = nil
if stateObject != nil {
var stateOrderList *stateOrderList
switch orderType {
case Ask:
stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price))
case Bid:
stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price))
default:
return Zero
}
if stateOrderList == nil || stateOrderList.empty() {
return Zero
}
volume = stateOrderList.Volume()
}
return volume
}
func (t *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big.Int) {
stateObject := t.getStateExchangeObject(orderBook)
if stateObject != nil {
priceHash := stateObject.getBestPriceAsksTrie(t.db)
if priceHash.IsZero() {
return Zero, Zero
}
orderList := stateObject.getStateOrderListAskObject(t.db, priceHash)
if orderList == nil {
log.Error("order list ask not found", "price", priceHash.Hex())
return Zero, Zero
}
return new(big.Int).SetBytes(priceHash.Bytes()), orderList.Volume()
}
return Zero, Zero
}
func (t *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big.Int) {
stateObject := t.getStateExchangeObject(orderBook)
if stateObject != nil {
priceHash := stateObject.getBestBidsTrie(t.db)
if priceHash.IsZero() {
return Zero, Zero
}
orderList := stateObject.getStateBidOrderListObject(t.db, priceHash)
if orderList == nil {
log.Error("order list bid not found", "price", priceHash.Hex())
return Zero, Zero
}
return new(big.Int).SetBytes(priceHash.Bytes()), orderList.Volume()
}
return Zero, Zero
}
func (t *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject != nil {
var stateOrderList *stateOrderList
switch side {
case Ask:
stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price))
case Bid:
stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price))
default:
return EmptyHash, Zero, fmt.Errorf("not found side: %s", side)
}
if stateOrderList != nil {
key, _, err := stateOrderList.getTrie(t.db).TryGetBestLeftKeyAndValue()
if err != nil {
return EmptyHash, Zero, err
}
orderId := common.BytesToHash(key)
amount := stateOrderList.GetOrderAmount(t.db, orderId)
return orderId, new(big.Int).SetBytes(amount.Bytes()), nil
}
return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , price : %d , side : %s", orderBook.Hex(), price, side)
}
return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
// updateStateExchangeObject writes the given object to the trie.
func (t *TradingStateDB) updateStateExchangeObject(stateObject *tradingExchanges) {
addr := stateObject.Hash()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
t.setError(t.trie.TryUpdate(addr[:], data))
}
// Retrieve a state object given my the address. Returns nil if not found.
func (t *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObject *tradingExchanges) {
// Prefer 'live' objects.
if obj := t.stateExhangeObjects[addr]; obj != nil {
return obj
}
// Load the object from the database.
enc, err := t.trie.TryGet(addr[:])
if len(enc) == 0 {
t.setError(err)
return nil
}
var data tradingExchangeObject
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
}
// Insert into the live set.
obj := newStateExchanges(t, addr, data, t.MarkStateExchangeObjectDirty)
t.stateExhangeObjects[addr] = obj
return obj
}
func (t *TradingStateDB) setStateExchangeObject(object *tradingExchanges) {
t.stateExhangeObjects[object.Hash()] = object
t.stateExhangeObjectsDirty[object.Hash()] = struct{}{}
}
// Retrieve a state object or create a new state object if nil.
func (t *TradingStateDB) GetOrNewStateExchangeObject(addr common.Hash) *tradingExchanges {
stateExchangeObject := t.getStateExchangeObject(addr)
if stateExchangeObject == nil {
stateExchangeObject = t.createExchangeObject(addr)
}
return stateExchangeObject
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (t *TradingStateDB) MarkStateExchangeObjectDirty(addr common.Hash) {
t.stateExhangeObjectsDirty[addr] = struct{}{}
}
// 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 *TradingStateDB) createExchangeObject(hash common.Hash) (newobj *tradingExchanges) {
newobj = newStateExchanges(t, hash, tradingExchangeObject{LendingCount: Zero, MediumPrice: Zero, MediumPriceBeforeEpoch: Zero, TotalQuantity: Zero}, t.MarkStateExchangeObjectDirty)
newobj.setNonce(0) // sets the object to dirty
t.setStateExchangeObject(newobj)
return newobj
}
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
func (t *TradingStateDB) Copy() *TradingStateDB {
t.lock.Lock()
defer t.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &TradingStateDB{
db: t.db,
trie: t.db.CopyTrie(t.trie),
stateExhangeObjects: make(map[common.Hash]*tradingExchanges, len(t.stateExhangeObjectsDirty)),
stateExhangeObjectsDirty: make(map[common.Hash]struct{}, len(t.stateExhangeObjectsDirty)),
}
// Copy the dirty states, logs, and preimages
for addr := range t.stateExhangeObjectsDirty {
state.stateExhangeObjectsDirty[addr] = struct{}{}
}
for addr, exchangeObject := range t.stateExhangeObjects {
state.stateExhangeObjects[addr] = exchangeObject.deepCopy(state, state.MarkStateExchangeObjectDirty)
}
return state
}
func (t *TradingStateDB) clearJournalAndRefund() {
t.journal = nil
t.validRevisions = t.validRevisions[:0]
}
// Snapshot returns an identifier for the current revision of the state.
func (t *TradingStateDB) Snapshot() int {
id := t.nextRevisionId
t.nextRevisionId++
t.validRevisions = append(t.validRevisions, revision{id, len(t.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
func (t *TradingStateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
idx := sort.Search(len(t.validRevisions), func(i int) bool {
return t.validRevisions[i].id >= revid
})
if idx == len(t.validRevisions) || t.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
snapshot := t.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
for i := len(t.journal) - 1; i >= snapshot; i-- {
t.journal[i].undo(t)
}
t.journal = t.journal[:snapshot]
// Remove invalidated snapshots from the stack.
t.validRevisions = t.validRevisions[:idx]
}
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
func (t *TradingStateDB) Finalise() {
// Commit objects to the trie.
for addr, stateObject := range t.stateExhangeObjects {
if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
err := stateObject.updateAsksRoot(t.db)
if err != nil {
log.Warn("Finalise updateAsksRoot", "err", err, "addr", addr, "stateObject", *stateObject)
}
stateObject.updateBidsRoot(t.db)
stateObject.updateOrdersRoot(t.db)
stateObject.updateLiquidationPriceRoot(t.db)
// Update the object in the main orderId trie.
t.updateStateExchangeObject(stateObject)
//delete(s.stateExhangeObjectsDirty, addr)
}
}
t.clearJournalAndRefund()
}
// IntermediateRoot computes the current root orderBookHash of the state trie.
// It is called in between transactions to get the root orderBookHash that
// goes into transaction receipts.
func (t *TradingStateDB) IntermediateRoot() common.Hash {
t.Finalise()
return t.trie.Hash()
}
// Commit writes the state to the underlying in-memory trie database.
func (t *TradingStateDB) Commit() (root common.Hash, err error) {
defer t.clearJournalAndRefund()
// Commit objects to the trie.
for addr, stateObject := range t.stateExhangeObjects {
if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
if err := stateObject.CommitAsksTrie(t.db); err != nil {
return EmptyHash, err
}
if err := stateObject.CommitBidsTrie(t.db); err != nil {
return EmptyHash, err
}
if err := stateObject.CommitOrdersTrie(t.db); err != nil {
return EmptyHash, err
}
if err := stateObject.CommitLiquidationPriceTrie(t.db); err != nil {
return EmptyHash, err
}
// Update the object in the main orderId trie.
t.updateStateExchangeObject(stateObject)
delete(t.stateExhangeObjectsDirty, addr)
}
}
// Write trie changes.
root, err = t.trie.Commit(func(leaf []byte, parent common.Hash) error {
var exchange tradingExchangeObject
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
return nil
}
if exchange.AskRoot != EmptyRoot {
t.db.TrieDB().Reference(exchange.AskRoot, parent)
}
if exchange.BidRoot != EmptyRoot {
t.db.TrieDB().Reference(exchange.BidRoot, parent)
}
if exchange.OrderRoot != EmptyRoot {
t.db.TrieDB().Reference(exchange.OrderRoot, parent)
}
if exchange.LiquidationPriceRoot != EmptyRoot {
t.db.TrieDB().Reference(exchange.LiquidationPriceRoot, parent)
}
return nil
})
log.Debug("Trading State Trie cache stats after commit", "root", root.Hex())
return root, err
}
func (t *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Hash, limit *big.Int) map[*big.Int]map[common.Hash][]common.Hash {
result := map[*big.Int]map[common.Hash][]common.Hash{}
orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
return result
}
mapPrices := orderbookState.getAllLowerLiquidationPrice(t.db, common.BigToHash(limit))
for priceHash, liquidationState := range mapPrices {
price := new(big.Int).SetBytes(priceHash[:])
log.Debug("GetAllLowerLiquidationPriceData", "price", price, "limit", limit)
if liquidationState != nil && price.Sign() > 0 && price.Cmp(limit) < 0 {
liquidationData := map[common.Hash][]common.Hash{}
priceLiquidationData := liquidationState.getAllLiquidationData(t.db)
for lendingBook, data := range priceLiquidationData {
if len(data) == 0 {
continue
}
oldData := liquidationData[lendingBook]
if len(oldData) == 0 {
oldData = data
} else {
oldData = append(oldData, data...)
}
liquidationData[lendingBook] = oldData
}
result[price] = liquidationData
}
}
return result
}
func (t *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash, price *big.Int) (*big.Int, map[common.Hash][]common.Hash) {
liquidationData := map[common.Hash][]common.Hash{}
orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
return common.Big0, liquidationData
}
highestPriceHash, liquidationState := orderbookState.getHighestLiquidationPrice(t.db)
highestPrice := new(big.Int).SetBytes(highestPriceHash[:])
if liquidationState != nil && highestPrice.Sign() > 0 && price.Cmp(highestPrice) < 0 {
priceLiquidationData := liquidationState.getAllLiquidationData(t.db)
for lendingBook, data := range priceLiquidationData {
if len(data) == 0 {
continue
}
oldData := liquidationData[lendingBook]
if len(oldData) == 0 {
oldData = data
} else {
oldData = append(oldData, data...)
}
liquidationData[lendingBook] = oldData
}
}
return highestPrice, liquidationData
}
func (t *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) {
tradIdHash := common.Uint64ToHash(tradeId)
priceHash := common.BigToHash(price)
orderBookState := t.getStateExchangeObject(orderBook)
if orderBookState == nil {
orderBookState = t.createExchangeObject(orderBook)
}
liquidationPriceState := orderBookState.getStateLiquidationPrice(t.db, priceHash)
if liquidationPriceState == nil {
liquidationPriceState = orderBookState.createStateLiquidationPrice(t.db, priceHash)
}
lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook)
if lendingBookState == nil {
lendingBookState = liquidationPriceState.createLendingBook(t.db, lendingBook)
}
lendingBookState.insertTradingId(t.db, tradIdHash)
lendingBookState.AddVolume(One)
liquidationPriceState.AddVolume(One)
orderBookState.addLendingCount(One)
t.journal = append(t.journal, insertLiquidationPrice{
orderBook: orderBook,
price: price,
lendingBook: lendingBook,
tradeId: tradeId,
})
}
func (t *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) error {
tradeIdHash := common.Uint64ToHash(tradeId)
priceHash := common.BigToHash(price)
orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
return fmt.Errorf("not found order book: %s", orderBook.Hex())
}
liquidationPriceState := orderbookState.getStateLiquidationPrice(t.db, priceHash)
if liquidationPriceState == nil {
return fmt.Errorf("not found liquidation price: %s , %s", orderBook.Hex(), priceHash.Hex())
}
lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook)
if lendingBookState == nil {
return fmt.Errorf("not found lending book: %s , %s ,%s", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex())
}
if !lendingBookState.Exist(t.db, tradeIdHash) {
return fmt.Errorf("not found trade id: %s, %s ,%s , %d", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId)
}
lendingBookState.removeTradingId(t.db, tradeIdHash)
lendingBookState.subVolume(One)
liquidationPriceState.subVolume(One)
if liquidationPriceState.Volume().Sign() == 0 {
err := orderbookState.getLiquidationPriceTrie(t.db).TryDelete(priceHash[:])
if err != nil {
log.Warn("RemoveLiquidationPrice getLiquidationPriceTrie.TryDelete", "err", err, "priceHash", priceHash[:])
}
}
orderbookState.subLendingCount(One)
t.journal = append(t.journal, removeLiquidationPrice{
orderBook: orderBook,
price: price,
lendingBook: lendingBook,
tradeId: tradeId,
})
return nil
}