mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-30 02:07:36 +00:00
The name of a method’s receiver should be a reflection of its identity; often a one or two letter abbreviation of its type suffices (such as “c” or “cl” for “Client”). Don’t use generic names such as “me”, “this” or “self”, identifiers typical of object-oriented languages that place more emphasis on methods as opposed to functions. The name need not be as descriptive as that of a method argument, as its role is obvious and serves no documentary purpose. It can be very short as it will appear on almost every line of every method of the type; familiarity admits brevity. Be consistent, too: if you call the receiver “c” in one method, don’t call it “cl” in another.
676 lines
24 KiB
Go
676 lines
24 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 lendingstate
|
|
|
|
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
|
|
}
|
|
|
|
type LendingStateDB struct {
|
|
db Database
|
|
trie Trie
|
|
|
|
// This map holds 'live' objects, which will get modified while processing a state transition.
|
|
lendingExchangeStates map[common.Hash]*lendingExchangeState
|
|
lendingExchangeStatesDirty 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 LendingStateDB.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) (*LendingStateDB, error) {
|
|
tr, err := db.OpenTrie(root)
|
|
if err != nil {
|
|
log.Error("Error when init new lending state trie ", "root", root.Hex(), "err", err)
|
|
return nil, err
|
|
}
|
|
return &LendingStateDB{
|
|
db: db,
|
|
trie: tr,
|
|
lendingExchangeStates: make(map[common.Hash]*lendingExchangeState),
|
|
lendingExchangeStatesDirty: make(map[common.Hash]struct{}),
|
|
}, nil
|
|
}
|
|
|
|
// setError remembers the first non-nil error it is called with.
|
|
func (ls *LendingStateDB) setError(err error) {
|
|
if ls.dbErr == nil {
|
|
ls.dbErr = err
|
|
}
|
|
}
|
|
|
|
func (ls *LendingStateDB) Error() error {
|
|
return ls.dbErr
|
|
}
|
|
|
|
// Exist reports whether the given tradeId address exists in the state.
|
|
// Notably this also returns true for suicided lenddinges.
|
|
func (ls *LendingStateDB) Exist(addr common.Hash) bool {
|
|
return ls.getLendingExchange(addr) != nil
|
|
}
|
|
|
|
// Empty returns whether the state object is either non-existent
|
|
// or empty according to the EIP161 specification (balance = nonce = code = 0)
|
|
func (ls *LendingStateDB) Empty(addr common.Hash) bool {
|
|
so := ls.getLendingExchange(addr)
|
|
return so == nil || so.empty()
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetNonce(addr common.Hash) uint64 {
|
|
stateObject := ls.getLendingExchange(addr)
|
|
if stateObject != nil {
|
|
return stateObject.Nonce()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 {
|
|
stateObject := ls.getLendingExchange(addr)
|
|
if stateObject != nil {
|
|
return stateObject.TradeNonce()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Database retrieves the low level database supporting the lower level trie ops.
|
|
func (ls *LendingStateDB) Database() Database {
|
|
return ls.db
|
|
}
|
|
|
|
func (ls *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) {
|
|
stateObject := ls.GetOrNewLendingExchangeObject(addr)
|
|
if stateObject != nil {
|
|
ls.journal = append(ls.journal, nonceChange{
|
|
hash: addr,
|
|
prev: stateObject.Nonce(),
|
|
})
|
|
stateObject.setNonce(nonce)
|
|
}
|
|
}
|
|
|
|
func (ls *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) {
|
|
stateObject := ls.GetOrNewLendingExchangeObject(addr)
|
|
if stateObject != nil {
|
|
ls.journal = append(ls.journal, tradeNonceChange{
|
|
hash: addr,
|
|
prev: stateObject.TradeNonce(),
|
|
})
|
|
stateObject.setTradeNonce(nonce)
|
|
}
|
|
}
|
|
|
|
func (ls *LendingStateDB) InsertLendingItem(orderBook common.Hash, orderId common.Hash, order LendingItem) {
|
|
interestHash := common.BigToHash(order.Interest)
|
|
stateExchange := ls.getLendingExchange(orderBook)
|
|
if stateExchange == nil {
|
|
stateExchange = ls.createLendingExchangeObject(orderBook)
|
|
}
|
|
var stateOrderList *itemListState
|
|
switch order.Side {
|
|
case Investing:
|
|
stateOrderList = stateExchange.getInvestingOrderList(ls.db, interestHash)
|
|
if stateOrderList == nil {
|
|
stateOrderList = stateExchange.createInvestingOrderList(ls.db, interestHash)
|
|
}
|
|
case Borrowing:
|
|
stateOrderList = stateExchange.getBorrowingOrderList(ls.db, interestHash)
|
|
if stateOrderList == nil {
|
|
stateOrderList = stateExchange.createBorrowingOrderList(ls.db, interestHash)
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
ls.journal = append(ls.journal, insertOrder{
|
|
orderBook: orderBook,
|
|
orderId: orderId,
|
|
order: &order,
|
|
})
|
|
stateExchange.createLendingItem(ls.db, orderId, order)
|
|
stateOrderList.insertLendingItem(ls.db, orderId, common.BigToHash(order.Quantity))
|
|
stateOrderList.AddVolume(order.Quantity)
|
|
}
|
|
|
|
func (ls *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uint64, order LendingTrade) {
|
|
tradeIdHash := common.Uint64ToHash(tradeId)
|
|
stateExchange := ls.getLendingExchange(orderBook)
|
|
if stateExchange == nil {
|
|
stateExchange = ls.createLendingExchangeObject(orderBook)
|
|
}
|
|
prvTrade := ls.GetLendingTrade(orderBook, tradeIdHash)
|
|
ls.journal = append(ls.journal, insertTrading{
|
|
orderBook: orderBook,
|
|
tradeId: tradeId,
|
|
prvTrade: &prvTrade,
|
|
})
|
|
stateExchange.insertLendingTrade(tradeIdHash, order)
|
|
}
|
|
|
|
func (ls *LendingStateDB) UpdateLiquidationPrice(orderBook common.Hash, tradeId uint64, price *big.Int) {
|
|
tradeIdHash := common.Uint64ToHash(tradeId)
|
|
stateExchange := ls.getLendingExchange(orderBook)
|
|
if stateExchange == nil {
|
|
stateExchange = ls.createLendingExchangeObject(orderBook)
|
|
}
|
|
stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash)
|
|
ls.journal = append(ls.journal, liquidationPriceChange{
|
|
orderBook: orderBook,
|
|
tradeId: tradeIdHash,
|
|
prev: stateLendingTrade.data.LiquidationPrice,
|
|
})
|
|
stateLendingTrade.SetLiquidationPrice(price)
|
|
}
|
|
|
|
func (ls *LendingStateDB) UpdateCollateralLockedAmount(orderBook common.Hash, tradeId uint64, amount *big.Int) {
|
|
tradeIdHash := common.Uint64ToHash(tradeId)
|
|
stateExchange := ls.getLendingExchange(orderBook)
|
|
if stateExchange == nil {
|
|
stateExchange = ls.createLendingExchangeObject(orderBook)
|
|
}
|
|
stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash)
|
|
ls.journal = append(ls.journal, collateralLockedAmount{
|
|
orderBook: orderBook,
|
|
tradeId: tradeIdHash,
|
|
prev: stateLendingTrade.data.CollateralLockedAmount,
|
|
})
|
|
stateLendingTrade.SetCollateralLockedAmount(amount)
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetLendingOrder(orderBook common.Hash, orderId common.Hash) LendingItem {
|
|
stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if stateObject == nil {
|
|
return EmptyLendingOrder
|
|
}
|
|
stateOrderItem := stateObject.getLendingItem(ls.db, orderId)
|
|
if stateOrderItem == nil {
|
|
return EmptyLendingOrder
|
|
}
|
|
return stateOrderItem.data
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetLendingTrade(orderBook common.Hash, tradeId common.Hash) LendingTrade {
|
|
stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if stateObject == nil {
|
|
return EmptyLendingTrade
|
|
}
|
|
stateOrderItem := stateObject.getLendingTrade(ls.db, tradeId)
|
|
if stateOrderItem == nil || stateOrderItem.empty() {
|
|
return EmptyLendingTrade
|
|
}
|
|
return stateOrderItem.data
|
|
}
|
|
|
|
func (ls *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
|
|
priceHash := common.BigToHash(price)
|
|
lendingExchange := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if lendingExchange == nil {
|
|
return fmt.Errorf("not found order book: %s", orderBook.Hex())
|
|
}
|
|
var orderList *itemListState
|
|
switch side {
|
|
case Investing:
|
|
orderList = lendingExchange.getInvestingOrderList(ls.db, priceHash)
|
|
case Borrowing:
|
|
orderList = lendingExchange.getBorrowingOrderList(ls.db, priceHash)
|
|
default:
|
|
return fmt.Errorf("not found order type: %s", side)
|
|
}
|
|
if orderList == nil || orderList.empty() {
|
|
return fmt.Errorf("empty orderList: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex())
|
|
}
|
|
lendingItem := lendingExchange.getLendingItem(ls.db, orderId)
|
|
if lendingItem == nil || lendingItem.empty() {
|
|
return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex())
|
|
}
|
|
currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderId).Bytes()[:])
|
|
if currentAmount.Cmp(amount) < 0 {
|
|
return fmt.Errorf("not enough order amount %s: have : %d , want : %d", orderId.Hex(), currentAmount, amount)
|
|
}
|
|
ls.journal = append(ls.journal, subAmountOrder{
|
|
orderBook: orderBook,
|
|
orderId: orderId,
|
|
order: ls.GetLendingOrder(orderBook, orderId),
|
|
amount: amount,
|
|
})
|
|
newAmount := new(big.Int).Sub(currentAmount, amount)
|
|
lendingItem.setVolume(newAmount)
|
|
log.Debug("SubAmountOrderItem", "tradeId", orderId.Hex(), "side", side, "key", price.Uint64(), "amount", amount.Uint64(), "new amount", newAmount.Uint64())
|
|
orderList.subVolume(amount)
|
|
if newAmount.Sign() == 0 {
|
|
orderList.removeOrderItem(ls.db, orderId)
|
|
} else {
|
|
orderList.setOrderItem(orderId, common.BigToHash(newAmount))
|
|
}
|
|
if orderList.empty() {
|
|
switch side {
|
|
case Investing:
|
|
lendingExchange.removeInvestingOrderList(ls.db, orderList)
|
|
case Borrowing:
|
|
lendingExchange.removeBorrowingOrderList(ls.db, orderList)
|
|
default:
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ls *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *LendingItem) error {
|
|
interestHash := common.BigToHash(order.Interest)
|
|
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId))
|
|
stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if stateObject == nil {
|
|
return fmt.Errorf("not found order book: %s", orderBook.Hex())
|
|
}
|
|
lendingItem := stateObject.getLendingItem(ls.db, orderIdHash)
|
|
var orderList *itemListState
|
|
switch lendingItem.data.Side {
|
|
case Investing:
|
|
orderList = stateObject.getInvestingOrderList(ls.db, interestHash)
|
|
case Borrowing:
|
|
orderList = stateObject.getBorrowingOrderList(ls.db, interestHash)
|
|
default:
|
|
return fmt.Errorf("not found order side: %s", order.Side)
|
|
}
|
|
if orderList == nil || orderList.empty() {
|
|
return fmt.Errorf("empty OrderList: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex())
|
|
}
|
|
if lendingItem == nil || lendingItem.empty() {
|
|
return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex())
|
|
}
|
|
if lendingItem.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(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex())
|
|
}
|
|
ls.journal = append(ls.journal, cancelOrder{
|
|
orderBook: orderBook,
|
|
orderId: orderIdHash,
|
|
order: ls.GetLendingOrder(orderBook, orderIdHash),
|
|
})
|
|
lendingItem.setVolume(big.NewInt(0))
|
|
currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderIdHash).Bytes()[:])
|
|
orderList.subVolume(currentAmount)
|
|
orderList.removeOrderItem(ls.db, orderIdHash)
|
|
if orderList.empty() {
|
|
switch order.Side {
|
|
case Investing:
|
|
stateObject.removeInvestingOrderList(ls.db, orderList)
|
|
case Borrowing:
|
|
stateObject.removeBorrowingOrderList(ls.db, orderList)
|
|
default:
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, *big.Int) {
|
|
stateObject := ls.getLendingExchange(orderBook)
|
|
if stateObject != nil {
|
|
investingHash := stateObject.getBestInvestingInterest(ls.db)
|
|
if common.EmptyHash(investingHash) {
|
|
return Zero, Zero
|
|
}
|
|
orderList := stateObject.getInvestingOrderList(ls.db, investingHash)
|
|
if orderList == nil {
|
|
log.Error("order list investing not found", "key", investingHash.Hex())
|
|
return Zero, Zero
|
|
}
|
|
return new(big.Int).SetBytes(investingHash.Bytes()), orderList.Volume()
|
|
}
|
|
return Zero, Zero
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *big.Int) {
|
|
stateObject := ls.getLendingExchange(orderBook)
|
|
if stateObject != nil {
|
|
priceHash := stateObject.getBestBorrowingInterest(ls.db)
|
|
if common.EmptyHash(priceHash) {
|
|
return Zero, Zero
|
|
}
|
|
orderList := stateObject.getBorrowingOrderList(ls.db, priceHash)
|
|
if orderList == nil {
|
|
log.Error("order list ask not found", "key", priceHash.Hex())
|
|
return Zero, Zero
|
|
}
|
|
return new(big.Int).SetBytes(priceHash.Bytes()), orderList.Volume()
|
|
}
|
|
return Zero, Zero
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
|
|
stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if stateObject != nil {
|
|
var stateOrderList *itemListState
|
|
switch side {
|
|
case Investing:
|
|
stateOrderList = stateObject.getInvestingOrderList(ls.db, common.BigToHash(price))
|
|
case Borrowing:
|
|
stateOrderList = stateObject.getBorrowingOrderList(ls.db, common.BigToHash(price))
|
|
default:
|
|
return EmptyHash, Zero, fmt.Errorf("not found side: %s", side)
|
|
}
|
|
if stateOrderList != nil {
|
|
key, _, err := stateOrderList.getTrie(ls.db).TryGetBestLeftKeyAndValue()
|
|
if err != nil {
|
|
return EmptyHash, Zero, err
|
|
}
|
|
orderId := common.BytesToHash(key)
|
|
amount := stateOrderList.GetOrderAmount(ls.db, orderId)
|
|
return orderId, new(big.Int).SetBytes(amount.Bytes()), nil
|
|
}
|
|
return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , key : %d , side : %s", orderBook.Hex(), price, side)
|
|
}
|
|
return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex())
|
|
}
|
|
|
|
// updateLendingExchange writes the given object to the trie.
|
|
func (ls *LendingStateDB) updateLendingExchange(stateObject *lendingExchangeState) {
|
|
addr := stateObject.Hash()
|
|
data, err := rlp.EncodeToBytes(stateObject)
|
|
if err != nil {
|
|
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
|
|
}
|
|
ls.setError(ls.trie.TryUpdate(addr[:], data))
|
|
}
|
|
|
|
// Retrieve a state object given my the address. Returns nil if not found.
|
|
func (ls *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *lendingExchangeState) {
|
|
// Prefer 'live' objects.
|
|
if obj := ls.lendingExchangeStates[addr]; obj != nil {
|
|
return obj
|
|
}
|
|
// Load the object from the database.
|
|
enc, err := ls.trie.TryGet(addr[:])
|
|
if len(enc) == 0 {
|
|
ls.setError(err)
|
|
return nil
|
|
}
|
|
var data lendingObject
|
|
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(ls, addr, data, ls.MarkLendingExchangeObjectDirty)
|
|
ls.lendingExchangeStates[addr] = obj
|
|
return obj
|
|
}
|
|
|
|
func (ls *LendingStateDB) setLendingExchangeObject(object *lendingExchangeState) {
|
|
ls.lendingExchangeStates[object.Hash()] = object
|
|
ls.lendingExchangeStatesDirty[object.Hash()] = struct{}{}
|
|
}
|
|
|
|
// Retrieve a state object or create a new state object if nil.
|
|
func (ls *LendingStateDB) GetOrNewLendingExchangeObject(addr common.Hash) *lendingExchangeState {
|
|
stateExchangeObject := ls.getLendingExchange(addr)
|
|
if stateExchangeObject == nil {
|
|
stateExchangeObject = ls.createLendingExchangeObject(addr)
|
|
}
|
|
return stateExchangeObject
|
|
}
|
|
|
|
// MarkStateLendObjectDirty adds the specified object to the dirty map to avoid costly
|
|
// state object cache iteration to find a handful of modified ones.
|
|
func (ls *LendingStateDB) MarkLendingExchangeObjectDirty(addr common.Hash) {
|
|
ls.lendingExchangeStatesDirty[addr] = struct{}{}
|
|
}
|
|
|
|
// createStateOrderListObject creates a new state object. If there is an existing tradeId with
|
|
// the given address, it is overwritten and returned as the second return value.
|
|
func (ls *LendingStateDB) createLendingExchangeObject(hash common.Hash) (newobj *lendingExchangeState) {
|
|
newobj = newStateExchanges(ls, hash, lendingObject{}, ls.MarkLendingExchangeObjectDirty)
|
|
newobj.setNonce(0) // sets the object to dirty
|
|
ls.setLendingExchangeObject(newobj)
|
|
return newobj
|
|
}
|
|
|
|
// Copy creates a deep, independent copy of the state.
|
|
// Snapshots of the copied state cannot be applied to the copy.
|
|
func (ls *LendingStateDB) Copy() *LendingStateDB {
|
|
ls.lock.Lock()
|
|
defer ls.lock.Unlock()
|
|
|
|
// Copy all the basic fields, initialize the memory ones
|
|
state := &LendingStateDB{
|
|
db: ls.db,
|
|
trie: ls.db.CopyTrie(ls.trie),
|
|
lendingExchangeStates: make(map[common.Hash]*lendingExchangeState, len(ls.lendingExchangeStatesDirty)),
|
|
lendingExchangeStatesDirty: make(map[common.Hash]struct{}, len(ls.lendingExchangeStatesDirty)),
|
|
}
|
|
// Copy the dirty states, logs, and preimages
|
|
for addr := range ls.lendingExchangeStatesDirty {
|
|
state.lendingExchangeStatesDirty[addr] = struct{}{}
|
|
}
|
|
for addr, exchangeObject := range ls.lendingExchangeStates {
|
|
state.lendingExchangeStates[addr] = exchangeObject.deepCopy(state, state.MarkLendingExchangeObjectDirty)
|
|
}
|
|
|
|
return state
|
|
}
|
|
|
|
func (ls *LendingStateDB) clearJournalAndRefund() {
|
|
ls.journal = nil
|
|
ls.validRevisions = ls.validRevisions[:0]
|
|
}
|
|
|
|
// Snapshot returns an identifier for the current revision of the state.
|
|
func (ls *LendingStateDB) Snapshot() int {
|
|
id := ls.nextRevisionId
|
|
ls.nextRevisionId++
|
|
ls.validRevisions = append(ls.validRevisions, revision{id, len(ls.journal)})
|
|
return id
|
|
}
|
|
|
|
// RevertToSnapshot reverts all state changes made since the given revision.
|
|
func (ls *LendingStateDB) RevertToSnapshot(revid int) {
|
|
// Find the snapshot in the stack of valid snapshots.
|
|
idx := sort.Search(len(ls.validRevisions), func(i int) bool {
|
|
return ls.validRevisions[i].id >= revid
|
|
})
|
|
if idx == len(ls.validRevisions) || ls.validRevisions[idx].id != revid {
|
|
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
|
}
|
|
snapshot := ls.validRevisions[idx].journalIndex
|
|
|
|
// Replay the journal to undo changes.
|
|
for i := len(ls.journal) - 1; i >= snapshot; i-- {
|
|
ls.journal[i].undo(ls)
|
|
}
|
|
ls.journal = ls.journal[:snapshot]
|
|
|
|
// Remove invalidated snapshots from the stack.
|
|
ls.validRevisions = ls.validRevisions[:idx]
|
|
}
|
|
|
|
// Finalise finalises the state by removing the self destructed objects
|
|
// and clears the journal as well as the refunds.
|
|
func (ls *LendingStateDB) Finalise() {
|
|
// Commit objects to the trie.
|
|
for addr, stateObject := range ls.lendingExchangeStates {
|
|
if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty {
|
|
// Write any storage changes in the state object to its storage trie.
|
|
err := stateObject.updateInvestingRoot(ls.db)
|
|
if err != nil {
|
|
log.Warn("Finalise updateInvestingRoot", "err", err, "addr", addr, "stateObject", *stateObject)
|
|
}
|
|
stateObject.updateBorrowingRoot(ls.db)
|
|
stateObject.updateOrderRoot(ls.db)
|
|
stateObject.updateLendingTradeRoot(ls.db)
|
|
stateObject.updateLiquidationTimeRoot(ls.db)
|
|
// Update the object in the main tradeId trie.
|
|
ls.updateLendingExchange(stateObject)
|
|
//delete(s.investingStatesDirty, addr)
|
|
}
|
|
}
|
|
ls.clearJournalAndRefund()
|
|
}
|
|
|
|
// IntermediateRoot computes the current root orderBook of the state trie.
|
|
// It is called in between transactions to get the root orderBook that
|
|
// goes into transaction receipts.
|
|
func (ls *LendingStateDB) IntermediateRoot() common.Hash {
|
|
ls.Finalise()
|
|
return ls.trie.Hash()
|
|
}
|
|
|
|
// Commit writes the state to the underlying in-memory trie database.
|
|
func (ls *LendingStateDB) Commit() (root common.Hash, err error) {
|
|
defer ls.clearJournalAndRefund()
|
|
// Commit objects to the trie.
|
|
for addr, stateObject := range ls.lendingExchangeStates {
|
|
if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty {
|
|
// Write any storage changes in the state object to its storage trie.
|
|
if err := stateObject.CommitInvestingTrie(ls.db); err != nil {
|
|
return EmptyHash, err
|
|
}
|
|
if err := stateObject.CommitBorrowingTrie(ls.db); err != nil {
|
|
return EmptyHash, err
|
|
}
|
|
if err := stateObject.CommitLendingItemTrie(ls.db); err != nil {
|
|
return EmptyHash, err
|
|
}
|
|
if err := stateObject.CommitLendingTradeTrie(ls.db); err != nil {
|
|
return EmptyHash, err
|
|
}
|
|
if err := stateObject.CommitLiquidationTimeTrie(ls.db); err != nil {
|
|
return EmptyHash, err
|
|
}
|
|
// Update the object in the main tradeId trie.
|
|
ls.updateLendingExchange(stateObject)
|
|
delete(ls.lendingExchangeStatesDirty, addr)
|
|
}
|
|
}
|
|
// Write trie changes.
|
|
root, err = ls.trie.Commit(func(leaf []byte, parent common.Hash) error {
|
|
var exchange lendingObject
|
|
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
|
|
return nil
|
|
}
|
|
if exchange.InvestingRoot != EmptyRoot {
|
|
ls.db.TrieDB().Reference(exchange.InvestingRoot, parent)
|
|
}
|
|
if exchange.BorrowingRoot != EmptyRoot {
|
|
ls.db.TrieDB().Reference(exchange.BorrowingRoot, parent)
|
|
}
|
|
if exchange.LendingItemRoot != EmptyRoot {
|
|
ls.db.TrieDB().Reference(exchange.LendingItemRoot, parent)
|
|
}
|
|
if exchange.LendingTradeRoot != EmptyRoot {
|
|
ls.db.TrieDB().Reference(exchange.LendingTradeRoot, parent)
|
|
}
|
|
if exchange.LiquidationTimeRoot != EmptyRoot {
|
|
ls.db.TrieDB().Reference(exchange.LiquidationTimeRoot, parent)
|
|
}
|
|
return nil
|
|
})
|
|
log.Debug("Lending State Trie cache stats after commit", "root", root.Hex())
|
|
return root, err
|
|
}
|
|
|
|
func (ls *LendingStateDB) InsertLiquidationTime(lendingBook common.Hash, time *big.Int, tradeId uint64) {
|
|
timeHash := common.BigToHash(time)
|
|
lendingExchangeState := ls.getLendingExchange(lendingBook)
|
|
if lendingExchangeState == nil {
|
|
lendingExchangeState = ls.createLendingExchangeObject(lendingBook)
|
|
}
|
|
liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash)
|
|
if liquidationTime == nil {
|
|
liquidationTime = lendingExchangeState.createLiquidationTime(ls.db, timeHash)
|
|
}
|
|
liquidationTime.insertTradeId(ls.db, common.Uint64ToHash(tradeId))
|
|
liquidationTime.AddVolume(One)
|
|
}
|
|
|
|
func (ls *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, tradeId uint64, time uint64) error {
|
|
timeHash := common.Uint64ToHash(time)
|
|
tradeIdHash := common.Uint64ToHash(tradeId)
|
|
lendingExchangeState := ls.getLendingExchange(lendingBook)
|
|
if lendingExchangeState == nil {
|
|
return fmt.Errorf("lending book not found: %s", lendingBook.Hex())
|
|
}
|
|
liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash)
|
|
if liquidationTime == nil {
|
|
return fmt.Errorf("not found liquidation time: %s , %d", lendingBook.Hex(), time)
|
|
}
|
|
if !liquidationTime.Exist(ls.db, tradeIdHash) {
|
|
return fmt.Errorf("not exist tradeId: %s, %d, %d", lendingBook.Hex(), time, tradeId)
|
|
}
|
|
liquidationTime.removeTradeId(ls.db, tradeIdHash)
|
|
liquidationTime.subVolume(One)
|
|
if liquidationTime.Volume().Sign() == 0 {
|
|
err := lendingExchangeState.getLiquidationTimeTrie(ls.db).TryDelete(timeHash[:])
|
|
if err != nil {
|
|
log.Warn("RemoveLiquidationTime getLiquidationTimeTrie.TryDelete", "err", err, "timeHash[:]", timeHash[:])
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ls *LendingStateDB) GetLowestLiquidationTime(lendingBook common.Hash, time *big.Int) (*big.Int, []common.Hash) {
|
|
liquidationData := []common.Hash{}
|
|
lendingExchangeState := ls.getLendingExchange(lendingBook)
|
|
if lendingExchangeState == nil {
|
|
return common.Big0, liquidationData
|
|
}
|
|
lowestPriceHash, liquidationState := lendingExchangeState.getLowestLiquidationTime(ls.db)
|
|
lowestTime := new(big.Int).SetBytes(lowestPriceHash[:])
|
|
if liquidationState != nil && lowestTime.Sign() > 0 && lowestTime.Cmp(time) <= 0 {
|
|
liquidationData = liquidationState.getAllTradeIds(ls.db)
|
|
}
|
|
return lowestTime, liquidationData
|
|
}
|
|
|
|
func (ls *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId uint64) error {
|
|
tradeIdHash := common.Uint64ToHash(tradeId)
|
|
stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
|
|
if stateObject == nil {
|
|
return fmt.Errorf("not found order book: %s", orderBook.Hex())
|
|
}
|
|
lendingTrade := stateObject.getLendingTrade(ls.db, tradeIdHash)
|
|
if lendingTrade == nil || lendingTrade.empty() {
|
|
return fmt.Errorf("lending trade empty order book: %s , trade id : %s , trade id hash : %s", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex())
|
|
}
|
|
ls.journal = append(ls.journal, cancelTrading{
|
|
orderBook: orderBook,
|
|
order: ls.GetLendingTrade(orderBook, tradeIdHash),
|
|
})
|
|
lendingTrade.SetAmount(Zero)
|
|
return nil
|
|
}
|