Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions consensus/posv/posv.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type TomoXService interface {
IsSDKNode() bool
SyncDataToSDKNode(txDataMatch tomox_state.TxDataMatch, txHash common.Hash, txMatchTime time.Time, statedb *state.StateDB, dirtyOrderCount *uint64) error
RollbackReorgTxMatch(txhash common.Hash)
GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, coinbase common.Address, tokenAddr common.Address) (*big.Int, error)
}

// Posv proof-of-stake-voting protocol constants.
Expand Down
39 changes: 33 additions & 6 deletions core/order_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package core
import (
"errors"
"fmt"
"github.com/tomochain/tomochain/consensus"
"github.com/tomochain/tomochain/consensus/posv"
"github.com/tomochain/tomochain/tomox/tomox_state"
"math/big"
"sort"
Expand Down Expand Up @@ -82,6 +84,13 @@ type blockChainTomox interface {
OrderStateAt(block *types.Block) (*tomox_state.TomoXStateDB, error)
StateAt(root common.Hash) (*state.StateDB, error)
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
Engine() consensus.Engine
// GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *types.Header
// CurrentHeader retrieves the current header from the local chain.
CurrentHeader() *types.Header
// Config retrieves the blockchain's chain configuration.
Config() *params.ChainConfig
}

// DefaultOrderPoolConfig contains the default configurations for the transaction
Expand Down Expand Up @@ -453,16 +462,34 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error {
return ErrInvalidOrderUserAddress
}

statedb, err := pool.chain.StateAt(pool.chain.CurrentBlock().Root())
if err != nil {
return fmt.Errorf("failed to get statedb Error: %v", err)
}
if !tomox_state.IsValidRelayer(statedb, tx.ExchangeAddress()) {
cloneStateDb := pool.currentRootState.Copy()
cloneTomoXStateDb := pool.currentOrderState.Copy()

if !tomox_state.IsValidRelayer(cloneStateDb, tx.ExchangeAddress()) {
return fmt.Errorf("invalid relayer. ExchangeAddress: %s", tx.ExchangeAddress().Hex())
}
if err := tomox_state.VerifyPair(statedb, tx.ExchangeAddress(), tx.BaseToken(), tx.QuoteToken()); err != nil {
if err := tomox_state.VerifyPair(cloneStateDb, tx.ExchangeAddress(), tx.BaseToken(), tx.QuoteToken()); err != nil {
return err
}
posvEngine, ok := pool.chain.Engine().(*posv.Posv)
if !ok {
return ErrNotPoSV
}
tomoXServ := posvEngine.GetTomoXService()
if tomoXServ == nil {
return fmt.Errorf("tomox not found in order validation")
}
baseDecimal, err := tomoXServ.GetTokenDecimal(pool.chain, cloneStateDb, pool.chain.CurrentBlock().Header().Coinbase, tx.BaseToken())
if err != nil {
return fmt.Errorf("validateOrder: failed to get baseDecimal. err: %v", err)
}
quoteDecimal, err := tomoXServ.GetTokenDecimal(pool.chain, cloneStateDb, pool.chain.CurrentBlock().Header().Coinbase, tx.QuoteToken())
if err != nil {
return fmt.Errorf("validateOrder: failed to get quoteDecimal. err: %v", err)
}
if err := tomox_state.VerifyBalance(cloneStateDb, cloneTomoXStateDb, tx, baseDecimal, quoteDecimal); err != nil {
return fmt.Errorf("not enough balance to make this order. OrderHash: %s. UserAddress: %s. PairName: %s. Err: %v", tx.Hash().Hex(), tx.UserAddress().Hex(), tx.PairName(), err)
}
return nil
}

Expand Down
164 changes: 5 additions & 159 deletions tomox/order_processor.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package tomox

import (
"encoding/json"
"errors"
"github.com/tomochain/tomochain/consensus"
"math/big"
"strconv"
Expand All @@ -15,9 +13,6 @@ import (
"github.com/tomochain/tomochain/tomox/tomox_state"
)

var emptyAddress = common.StringToAddress("")
var errQuantityTradeTooSmall = errors.New("Quantity trade too small")

func (tomox *TomoX) CommitOrder(coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tomoXstatedb *tomox_state.TomoXStateDB, orderBook common.Hash, order *tomox_state.OrderItem) ([]map[string]string, []*tomox_state.OrderItem, error) {
snap := tomoXstatedb.Snapshot()
trades, rejects, err := tomox.ApplyOrder(coinbase, chain, statedb, tomoXstatedb, orderBook, order)
Expand Down Expand Up @@ -221,7 +216,7 @@ func (tomox *TomoX) processOrderList(coinbase common.Address, chain consensus.Ch
quotePrice = tomoXstatedb.GetPrice(tomox_state.GetOrderBookHash(oldestOrder.QuoteToken, common.HexToAddress(common.TomoNativeAddress)))
}
tradedQuantity, rejectMaker, err := tomox.getTradeQuantity(quotePrice, coinbase, chain, statedb, order, &oldestOrder, maxTradedQuantity)
if err != nil && err == errQuantityTradeTooSmall {
if err != nil && err == tomox_state.ErrQuantityTradeTooSmall {
if tradedQuantity.Cmp(maxTradedQuantity) == 0 {
if quantityToTrade.Cmp(amount) == 0 { // reject Taker & maker
rejects = append(rejects, order)
Expand Down Expand Up @@ -346,10 +341,10 @@ func (tomox *TomoX) getTradeQuantity(quotePrice *big.Int, coinbase common.Addres
log.Debug("GetTradeQuantity", "side", takerOrder.Side, "takerBalance", takerBalance, "makerBalance", makerBalance, "BaseToken", makerOrder.BaseToken, "QuoteToken", makerOrder.QuoteToken, "quantity", quantity, "rejectMaker", rejectMaker, "quotePrice", quotePrice)
if quantity.Sign() > 0 {
// Apply Match Order
setteBalance, err := GetSettleBalance(quotePrice, takerOrder.Side, takerFeeRate, makerOrder.BaseToken, makerOrder.QuoteToken, makerOrder.Price, makerFeeRate, baseTokenDecimal, quoteTokenDecimal, quantity)
log.Debug("GetSettleBalance", "setteBalance", setteBalance, "err", err)
settleBalanceResult, err := tomox_state.GetSettleBalance(quotePrice, takerOrder.Side, takerFeeRate, makerOrder.BaseToken, makerOrder.QuoteToken, makerOrder.Price, makerFeeRate, baseTokenDecimal, quoteTokenDecimal, quantity)
log.Debug("GetSettleBalance", "settleBalanceResult", settleBalanceResult, "err", err)
if err == nil {
err = SetteBalance(coinbase, takerOrder, makerOrder, setteBalance, statedb)
err = DoSettleBalance(coinbase, takerOrder, makerOrder, settleBalanceResult, statedb)
}
return quantity, rejectMaker, err
}
Expand Down Expand Up @@ -452,156 +447,7 @@ func GetTradeQuantity(takerSide string, takerFeeRate *big.Int, takerBalance *big
}
}

type TradeResult struct {
Fee *big.Int
InToken common.Address
InQuantity *big.Int
InTotal *big.Int
OutToken common.Address
OutQuantity *big.Int
OutTotal *big.Int
}
type SettleBalance struct {
Taker TradeResult
Maker TradeResult
}

func (settleBalance *SettleBalance) String() string {
json, _ := json.Marshal(settleBalance)
return string(json)
}

func GetSettleBalance(quotePrice *big.Int, takerSide string, takerFeeRate *big.Int, baseToken, quoteToken common.Address, makerPrice *big.Int, makerFeeRate *big.Int, baseTokenDecimal *big.Int, quoteTokenDecimal *big.Int, quantityToTrade *big.Int) (*SettleBalance, error) {
log.Debug("GetSettleBalance", "takerSide", takerSide, "takerFeeRate", takerFeeRate, "baseToken", baseToken, "quoteToken", quoteToken, "makerPrice", makerPrice, "makerFeeRate", makerFeeRate, "baseTokenDecimal", baseTokenDecimal, "quantityToTrade", quantityToTrade)
var result *SettleBalance
//result = map[common.Address]map[string]interface{}{}
if takerSide == tomox_state.Bid {
// maker InQuantity quoteTokenQuantity=(quantityToTrade*maker.Price/baseTokenDecimal)
quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice)
quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal)
// Fee
// charge on the token he/she has before the trade, in this case: quoteToken
// charge on the token he/she has before the trade, in this case: baseToken
// takerFee = quoteTokenQuantity*takerFeeRate/baseFee=(quantityToTrade*maker.Price/baseTokenDecimal) * makerFeeRate/baseFee
takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate)
takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee)
// charge on the token he/she has before the trade, in this case: baseToken
makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate)
makerFee = new(big.Int).Div(makerFee, common.TomoXBaseFee)
if quoteTokenQuantity.Cmp(makerFee) <= 0 {
log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee)
return result, errQuantityTradeTooSmall
}
if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 {
exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice)
exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal)
log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice)
if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 {
log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice)
return result, errQuantityTradeTooSmall
}
exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice)
exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal)
log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice)
if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 {
log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice)
return result, errQuantityTradeTooSmall
}
}
inTotal := new(big.Int).Sub(quoteTokenQuantity, makerFee)
//takerOutTotal= quoteTokenQuantity + takerFee = quantityToTrade*maker.Price/baseTokenDecimal + quantityToTrade*maker.Price/baseTokenDecimal * takerFeeRate/baseFee
// = quantityToTrade * maker.Price/baseTokenDecimal ( 1 + takerFeeRate/baseFee)
// = quantityToTrade * maker.Price * (baseFee + takerFeeRate ) / ( baseTokenDecimal * baseFee)
takerOutTotal := new(big.Int).Add(quoteTokenQuantity, takerFee)

result = &SettleBalance{
Taker: TradeResult{
Fee: takerFee,
InToken: baseToken,
InQuantity: quantityToTrade,
InTotal: quantityToTrade,
OutToken: quoteToken,
OutQuantity: quoteTokenQuantity,
OutTotal: takerOutTotal,
},
Maker: TradeResult{
Fee: makerFee,
InToken: quoteToken,
InQuantity: quoteTokenQuantity,
InTotal: inTotal,
OutToken: baseToken,
OutQuantity: quantityToTrade,
OutTotal: quantityToTrade,
},
}
} else {
// Taker InQuantity
// quoteTokenQuantity = quantityToTrade * makerPrice / baseTokenDecimal
quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice)
quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal)
// maker InQuantity

// Fee
// charge on the token he/she has before the trade, in this case: baseToken
// makerFee = quoteTokenQuantity * makerFeeRate / baseFee = quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee
// charge on the token he/she has before the trade, in this case: quoteToken
makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate)
makerFee = makerFee.Div(makerFee, common.TomoXBaseFee)

// charge on the token he/she has before the trade, in this case: baseToken
takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate)
takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee)
if quoteTokenQuantity.Cmp(takerFee) <= 0 {
log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee)
return result, errQuantityTradeTooSmall
}
if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 {
exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice)
exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal)
log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice)
if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 {
log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice)
return result, errQuantityTradeTooSmall
}
exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice)
exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal)
log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice)
if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 {
log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice)
return result, errQuantityTradeTooSmall
}
}
inTotal := new(big.Int).Sub(quoteTokenQuantity, takerFee)
// makerOutTotal = quoteTokenQuantity + makerFee = quantityToTrade * makerPrice / baseTokenDecimal + quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee
// = quantityToTrade * makerPrice / baseTokenDecimal * (1+makerFeeRate / baseFee)
// = quantityToTrade * makerPrice * (baseFee + makerFeeRate) / ( baseTokenDecimal * baseFee )
makerOutTotal := new(big.Int).Add(quoteTokenQuantity, makerFee)
// Fee
result = &SettleBalance{
Taker: TradeResult{
Fee: takerFee,
InToken: quoteToken,
InQuantity: quoteTokenQuantity,
InTotal: inTotal,
OutToken: baseToken,
OutQuantity: quantityToTrade,
OutTotal: quantityToTrade,
},
Maker: TradeResult{
Fee: makerFee,
InToken: baseToken,
InQuantity: quantityToTrade,
InTotal: quantityToTrade,
OutToken: quoteToken,
OutQuantity: quoteTokenQuantity,
OutTotal: makerOutTotal,
},
}
}
return result, nil
}

func SetteBalance(coinbase common.Address, takerOrder, makerOrder *tomox_state.OrderItem, settleBalance *SettleBalance, statedb *state.StateDB) error {
func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tomox_state.OrderItem, settleBalance *tomox_state.SettleBalance, statedb *state.StateDB) error {
takerExOwner := tomox_state.GetRelayerOwner(takerOrder.ExchangeAddress, statedb)
makerExOwner := tomox_state.GetRelayerOwner(makerOrder.ExchangeAddress, statedb)
matchingFee := big.NewInt(0)
Expand Down
19 changes: 19 additions & 0 deletions tomox/tomox_state/orderitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/globalsign/mgo/bson"
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/core/state"
"github.com/tomochain/tomochain/core/types"
"github.com/tomochain/tomochain/crypto"
"github.com/tomochain/tomochain/crypto/sha3"
"github.com/tomochain/tomochain/log"
Expand Down Expand Up @@ -376,6 +377,24 @@ func VerifyPair(statedb *state.StateDB, exchangeAddress, baseToken, quoteToken c
return fmt.Errorf("invalid exchange pair. Base: %s. Quote: %s. Exchange: %s", baseToken.Hex(), quoteToken.Hex(), exchangeAddress.Hex())
}

func VerifyBalance(statedb *state.StateDB, tomoxStateDb *TomoXStateDB, order *types.OrderTransaction, baseDecimal, quoteDecimal *big.Int) error {
var quotePrice *big.Int
if order.QuoteToken().String() != common.TomoNativeAddress {
quotePrice = tomoxStateDb.GetPrice(GetOrderBookHash(order.QuoteToken(), common.HexToAddress(common.TomoNativeAddress)))
}
feeRate := GetExRelayerFee(order.ExchangeAddress(), statedb)
balanceResult, err := GetSettleBalance(quotePrice, order.Side(), feeRate, order.BaseToken(), order.QuoteToken(), order.Price(), common.Big0, baseDecimal, quoteDecimal, order.Quantity())
if err != nil {
return err
}
expectedBalance := balanceResult.Taker.OutTotal
actualBalance := GetTokenBalance(order.UserAddress(), balanceResult.Taker.OutToken, statedb)
if actualBalance.Cmp(expectedBalance) < 0 {
return fmt.Errorf("token: %s . ExpectedBalance: %s . ActualBalance: %s", balanceResult.Taker.OutToken.Hex(), expectedBalance.String(), actualBalance.String())
}
return nil
}

// MarshalSignature marshals the signature struct to []byte
func (s *Signature) MarshalSignature() ([]byte, error) {
sigBytes1 := s.R.Bytes()
Expand Down
Loading