Skip to content

Commit

Permalink
Match orders in same block as they are placed
Browse files Browse the repository at this point in the history
  • Loading branch information
lumos42 committed Apr 1, 2024
1 parent 3f2ca6c commit 1941b56
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 13 deletions.
5 changes: 3 additions & 2 deletions .avalanche-cli.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"node-config": {
"log-level": "info"
}
}
},
"singlenodeenabled": false
}
16 changes: 16 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,22 @@ func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}

func (t *TransactionsByPriceAndNonce) Copy() *TransactionsByPriceAndNonce {
txs := make(map[common.Address]Transactions, len(t.txs))
for acc, accTxs := range t.txs {
txs[acc] = make(Transactions, len(accTxs))
copy(txs[acc], accTxs)
}
heads := make(TxByPriceAndTime, len(t.heads))
copy(heads, t.heads)
return &TransactionsByPriceAndNonce{
txs: txs,
heads: heads,
signer: t.signer,
baseFee: big.NewInt(0).Set(t.baseFee),
}
}

// copyAddressPtr copies an address.
func copyAddressPtr(a *common.Address) *common.Address {
if a == nil {
Expand Down
5 changes: 5 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) {
s.miner.SetEtherbase(etherbase)
}

func (s *Ethereum) SetOrderbookChecker(orderBookChecker miner.OrderBookChecker) {
s.miner.SetOrderbookChecker(orderBookChecker)

}

func (s *Ethereum) Miner() *miner.Miner { return s.miner }

func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
Expand Down
4 changes: 4 additions & 0 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func (miner *Miner) SetEtherbase(addr common.Address) {
miner.worker.setEtherbase(addr)
}

func (miner *Miner) SetOrderbookChecker(orderBookChecker OrderBookChecker) {

Check failure on line 67 in miner/miner.go

View workflow job for this annotation

GitHub Actions / Golang Unit Tests v1.22.0 (ubuntu-22.04)

undefined: OrderBookChecker
miner.worker.setOrderbookChecker(orderBookChecker)
}

func (miner *Miner) GenerateBlock(predicateContext *precompileconfig.PredicateContext) (*types.Block, error) {
return miner.worker.commitNewWork(predicateContext)
}
Expand Down
28 changes: 28 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ type worker struct {
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
coinbase common.Address
clock *mockable.Clock // Allows us mock the clock for testing

orderbookChecker OrderBookChecker

Check failure on line 103 in miner/worker.go

View workflow job for this annotation

GitHub Actions / Golang Unit Tests v1.22.0 (ubuntu-22.04)

undefined: OrderBookChecker
}

func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker {
Expand All @@ -123,6 +125,10 @@ func (w *worker) setEtherbase(addr common.Address) {
w.coinbase = addr
}

func (w *worker) setOrderbookChecker(orderBookChecker OrderBookChecker) {

Check failure on line 128 in miner/worker.go

View workflow job for this annotation

GitHub Actions / Golang Unit Tests v1.22.0 (ubuntu-22.04)

undefined: OrderBookChecker
w.orderbookChecker = orderBookChecker
}

// commitNewWork generates several new sealing tasks based on the parent block.
func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateContext) (*types.Block, error) {
w.mu.RLock()
Expand Down Expand Up @@ -229,16 +235,38 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte
}
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee)
txsCopy := txs.Copy()
w.commitTransactions(env, txs, header.Coinbase)
w.commitOrderbookTxs(env, txsCopy, header)
}
if len(remoteTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee)
txsCopy := txs.Copy()
w.commitTransactions(env, txs, header.Coinbase)
w.commitOrderbookTxs(env, txsCopy, header)
}

w.orderbookChecker.ResetMemoryDB()

return w.commit(env)
}

func (w *worker) commitOrderbookTxs(env *environment, transactions *types.TransactionsByPriceAndNonce, header *types.Header) {
for {
tx := transactions.Peek()
if tx == nil {
break
}
transactions.Pop()

orderbookTxs := w.orderbookChecker.GetMatchingTxs(tx, env.state, header.Number)
if orderbookTxs != nil {
txsByPrice := types.NewTransactionsByPriceAndNonce(env.signer, orderbookTxs, header.BaseFee)
w.commitTransactions(env, txsByPrice, header.Coinbase)
}
}
}

func (w *worker) createCurrentEnvironment(predicateContext *precompileconfig.PredicateContext, parent *types.Header, header *types.Header, tstart time.Time) (*environment, error) {
state, err := w.chain.StateAt(parent.Root)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions plugin/evm/limit_order.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type LimitOrderProcesser interface {
GetTestingAPI() *orderbook.TestingAPI
GetTradingAPI() *orderbook.TradingAPI
RunMatchingPipeline()
GetMemoryDB() orderbook.LimitOrderDatabase
GetLimitOrderTxProcessor() orderbook.LimitOrderTxProcessor
}

type limitOrderProcesser struct {
Expand Down Expand Up @@ -206,6 +208,14 @@ func (lop *limitOrderProcesser) GetTestingAPI() *orderbook.TestingAPI {
return orderbook.NewTestingAPI(lop.memoryDb, lop.backend, lop.configService, lop.hubbleDB)
}

func (lop *limitOrderProcesser) GetMemoryDB() orderbook.LimitOrderDatabase {
return lop.memoryDb
}

func (lop *limitOrderProcesser) GetLimitOrderTxProcessor() orderbook.LimitOrderTxProcessor {
return lop.limitOrderTxProcessor
}

func (lop *limitOrderProcesser) listenAndStoreLimitOrderTransactions() {
logsCh := make(chan []*types.Log)
logsSubscription := lop.backend.SubscribeHubbleLogsEvent(logsCh)
Expand Down
20 changes: 15 additions & 5 deletions plugin/evm/orderbook/config_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type IConfigService interface {

type ConfigService struct {
blockChain *core.BlockChain
stateDB *state.StateDB
}

func NewConfigService(blockChain *core.BlockChain) IConfigService {
Expand All @@ -47,6 +48,20 @@ func NewConfigService(blockChain *core.BlockChain) IConfigService {
}
}

func NewConfigServiceFromStateDB(stateDB *state.StateDB) IConfigService {
return &ConfigService{
stateDB: stateDB,
}
}

func (cs *ConfigService) getStateAtCurrentBlock() *state.StateDB {
if cs.stateDB != nil {
return cs.stateDB
}
stateDB, _ := cs.blockChain.StateAt(cs.blockChain.CurrentBlock().Root)
return stateDB
}

func (cs *ConfigService) GetAcceptableBounds(market Market) (*big.Int, *big.Int) {
return bibliophile.GetAcceptableBounds(cs.getStateAtCurrentBlock(), int64(market))
}
Expand Down Expand Up @@ -79,11 +94,6 @@ func (cs *ConfigService) GetPriceMultiplier(market Market) *big.Int {
return bibliophile.GetMultiplier(cs.getStateAtCurrentBlock(), int64(market))
}

func (cs *ConfigService) getStateAtCurrentBlock() *state.StateDB {
stateDB, _ := cs.blockChain.StateAt(cs.blockChain.CurrentBlock().Root)
return stateDB
}

func (cs *ConfigService) GetActiveMarketsCount() int64 {
return bibliophile.GetActiveMarketsCount(cs.getStateAtCurrentBlock())
}
Expand Down
54 changes: 54 additions & 0 deletions plugin/evm/orderbook/matching_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"sync"
"time"

"github.com/ava-labs/subnet-evm/core/types"
hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils"
"github.com/ava-labs/subnet-evm/utils"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -42,6 +43,18 @@ func NewMatchingPipeline(
}
}

func NewTemporaryMatchingPipeline(
db LimitOrderDatabase,
lotp LimitOrderTxProcessor,
configService IConfigService) *MatchingPipeline {

return &MatchingPipeline{
db: db,
lotp: lotp,
configService: configService,
}
}

func (pipeline *MatchingPipeline) RunSanitization() {
pipeline.db.RemoveExpiredSignedOrders()
}
Expand Down Expand Up @@ -106,6 +119,47 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool {
return false
}

func (pipeline *MatchingPipeline) GetOrderMatchingTransactions(blockNumber *big.Int, markets []Market) map[common.Address]types.Transactions {
pipeline.mu.Lock()
defer pipeline.mu.Unlock()

activeMarkets := pipeline.GetActiveMarkets()
log.Info("MatchingPipeline:GetOrderMatchingTransactions")

if len(activeMarkets) == 0 {
return nil
}

// start fresh and purge all local transactions
pipeline.lotp.PurgeOrderBookTxs()

// fetch various hubble market params and run the matching engine
hState := hu.GetHubbleState()
hState.OraclePrices = hu.ArrayToMap(pipeline.configService.GetUnderlyingPrices())

marginMap := make(map[common.Address]*big.Int)
for addr, trader := range pipeline.db.GetAllTraders() {
userState := &hu.UserState{
Positions: translatePositions(trader.Positions),
Margins: getMargins(&trader, len(hState.Assets)),
PendingFunding: getTotalFunding(&trader, hState.ActiveMarkets),
ReservedMargin: new(big.Int).Set(trader.Margin.Reserved),
// this is the only leveldb read, others above are in-memory reads
ReduceOnlyAmounts: pipeline.configService.GetReduceOnlyAmounts(addr),
}
marginMap[addr] = hu.GetAvailableMargin(hState, userState)
}
for _, market := range markets {
orders := pipeline.fetchOrders(market, hState.OraclePrices[market], map[common.Hash]struct{}{}, blockNumber)
upperBound, _ := pipeline.configService.GetAcceptableBounds(market)
pipeline.runMatchingEngine(pipeline.lotp, orders.longOrders, orders.shortOrders, marginMap, hState.MinAllowableMargin, hState.TakerFee, upperBound)
}

orderbookTxs := pipeline.lotp.GetOrderBookTxs()
pipeline.lotp.PurgeOrderBookTxs()
return orderbookTxs
}

type Orders struct {
longOrders []Order
shortOrders []Order
Expand Down
4 changes: 4 additions & 0 deletions plugin/evm/orderbook/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ func (lotp *MockLimitOrderTxProcessor) GetOrderBookTxsCount() uint64 {
return uint64(args.Int(0))
}

func (lotp *MockLimitOrderTxProcessor) GetOrderBookTxs() map[common.Address]types.Transactions {
return nil
}

func (lotp *MockLimitOrderTxProcessor) ExecuteFundingPaymentTx() error {
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions plugin/evm/orderbook/tx_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var IOCOrderBookContractAddress = common.HexToAddress("0x03000000000000000000000

type LimitOrderTxProcessor interface {
GetOrderBookTxsCount() uint64
GetOrderBookTxs() map[common.Address]types.Transactions
SetOrderBookTxsBlockNumber(blockNumber uint64)
PurgeOrderBookTxs()
ExecuteMatchedOrdersTx(incomingOrder Order, matchedOrder Order, fillAmount *big.Int) error
Expand Down Expand Up @@ -244,6 +245,10 @@ func (lotp *limitOrderTxProcessor) GetOrderBookTxsCount() uint64 {
return lotp.txPool.GetOrderBookTxsCount()
}

func (lotp *limitOrderTxProcessor) GetOrderBookTxs() map[common.Address]types.Transactions {
return lotp.txPool.GetOrderBookTxs()
}

func (lotp *limitOrderTxProcessor) SetOrderBookTxsBlockNumber(blockNumber uint64) {
lotp.txPool.SetOrderBookTxsBlockNumber(blockNumber)
}
Expand Down
2 changes: 2 additions & 0 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig.
vm.miner = vm.eth.Miner()

vm.limitOrderProcesser = vm.NewLimitOrderProcesser()
tempMatcher := orderbook.NewTempMatcher(vm.limitOrderProcesser.GetMemoryDB(), vm.limitOrderProcesser.GetLimitOrderTxProcessor())
vm.eth.SetOrderbookChecker(tempMatcher)
vm.eth.Start()
return vm.initChainState(vm.blockChain.LastAcceptedBlock())
}
Expand Down
4 changes: 2 additions & 2 deletions precompile/contracts/bibliophile/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ type OrderDetails struct {
func GetIOCOrdersVariables(stateDB contract.StateDB, orderHash common.Hash) VariablesReadFromIOCOrdersSlots {
blockPlaced := iocGetBlockPlaced(stateDB, orderHash)
filledAmount := iocGetOrderFilledAmount(stateDB, orderHash)
orderStatus := iocGetOrderStatus(stateDB, orderHash)
orderStatus := IOCGetOrderStatus(stateDB, orderHash)

iocExpirationCap := iocGetExpirationCap(stateDB)
return VariablesReadFromIOCOrdersSlots{
Expand All @@ -203,7 +203,7 @@ type VariablesReadFromOrderbookSlots struct {
func GetOrderBookVariables(stateDB contract.StateDB, traderAddress string, senderAddress string, orderHash common.Hash) VariablesReadFromOrderbookSlots {
blockPlaced := getBlockPlaced(stateDB, orderHash)
filledAmount := getOrderFilledAmount(stateDB, orderHash)
orderStatus := getOrderStatus(stateDB, orderHash)
orderStatus := GetOrderStatus(stateDB, orderHash)
isTradingAuthoriy := IsTradingAuthority(stateDB, common.HexToAddress(traderAddress), common.HexToAddress(senderAddress))
return VariablesReadFromOrderbookSlots{
OrderDetails: OrderDetails{
Expand Down
4 changes: 2 additions & 2 deletions precompile/contracts/bibliophile/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (b *bibliophileClient) GetOrderFilledAmount(orderHash [32]byte) *big.Int {
}

func (b *bibliophileClient) GetOrderStatus(orderHash [32]byte) int64 {
return getOrderStatus(b.accessibleState.GetStateDB(), orderHash)
return GetOrderStatus(b.accessibleState.GetStateDB(), orderHash)
}

func (b *bibliophileClient) IOC_GetBlockPlaced(orderHash [32]byte) *big.Int {
Expand All @@ -132,7 +132,7 @@ func (b *bibliophileClient) IOC_GetOrderFilledAmount(orderHash [32]byte) *big.In
}

func (b *bibliophileClient) IOC_GetOrderStatus(orderHash [32]byte) int64 {
return iocGetOrderStatus(b.accessibleState.GetStateDB(), orderHash)
return IOCGetOrderStatus(b.accessibleState.GetStateDB(), orderHash)
}

func (b *bibliophileClient) IsTradingAuthority(trader, senderOrSigner common.Address) bool {
Expand Down
2 changes: 1 addition & 1 deletion precompile/contracts/bibliophile/ioc_order_book.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func iocGetOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.
return fromTwosComplement(num)
}

func iocGetOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 {
func IOCGetOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 {
orderInfo := iocOrderInfoMappingStorageSlot(orderHash)
return new(big.Int).SetBytes(stateDB.GetState(common.HexToAddress(IOC_ORDERBOOK_ADDRESS), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(2)))).Bytes()).Int64()
}
Expand Down
2 changes: 1 addition & 1 deletion precompile/contracts/bibliophile/limit_order_book.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func getOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.Int
return fromTwosComplement(num)
}

func getOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 {
func GetOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 {
orderInfo := orderInfoMappingStorageSlot(orderHash)
return new(big.Int).SetBytes(stateDB.GetState(common.HexToAddress(LIMIT_ORDERBOOK_GENESIS_ADDRESS), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(3)))).Bytes()).Int64()
}
Expand Down

0 comments on commit 1941b56

Please sign in to comment.