Skip to content
This repository was archived by the owner on Mar 18, 2019. It is now read-only.
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions config/relay.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,9 @@ title = "miner"
[cloud_watch]
enabled = false
region = ""

[order_difficulty]
base_difficulty = "0x0000000000000000000000000000000000000000000000000000000000000010"
threshold = 20
cal_count = 100
duration = 2
14 changes: 8 additions & 6 deletions gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"errors"
"fmt"
"github.com/Loopring/relay-cluster/accountmanager"
"github.com/Loopring/relay-cluster/gateway/order_difficulty"
"github.com/Loopring/relay-cluster/ordermanager/manager"
"github.com/Loopring/relay-cluster/ordermanager/viewer"
"github.com/Loopring/relay-lib/broadcast"
Expand All @@ -36,8 +37,8 @@ import (
"github.com/Loopring/relay-lib/types"
"github.com/ethereum/go-ethereum/common"
"math/big"
"time"
"strings"
"time"
)

type Gateway struct {
Expand Down Expand Up @@ -85,7 +86,7 @@ func Initialize(filterOptions *GatewayFiltersOptions, options *GateWayOptions, o
gateway.marketCap = marketCap

// new pow filter
powFilter := &PowFilter{Difficulty: types.HexToBigint(filterOptions.PowFilter.Difficulty)}
powFilter := &PowFilter{}

// new base filter
baseFilter := &BaseFilter{
Expand Down Expand Up @@ -400,7 +401,6 @@ func (f *CutoffFilter) filter(o *types.Order) (bool, error) {
}

type PowFilter struct {
Difficulty *big.Int
}

func (f *PowFilter) filter(o *types.Order) (bool, error) {
Expand All @@ -410,9 +410,11 @@ func (f *PowFilter) filter(o *types.Order) (bool, error) {
}

pow := GetPow(o.V, o.R, o.S, o.PowNonce)

if pow.Cmp(f.Difficulty) < 0 {
return false, fmt.Errorf("invalid pow")
if diffHex, err := order_difficulty.GetDifficulty(); nil == err {
diff := types.HexToBigint(diffHex)
if pow.Cmp(diff) < 0 {
return false, fmt.Errorf("invalid pow")
}
}
return true, nil
}
Expand Down
183 changes: 142 additions & 41 deletions gateway/order_difficulty/difficulty.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,78 @@
package order_difficulty

import (
"encoding/json"
"github.com/Loopring/relay-lib/cache"
"github.com/Loopring/relay-lib/eventemitter"
"github.com/Loopring/relay-lib/log"
"github.com/Loopring/relay-lib/types"
"github.com/Loopring/relay-lib/zklock"
"github.com/ethereum/go-ethereum/common"
"gonum.org/v1/gonum/stat"
"math/big"
"qiniupkg.com/x/log.v7"
"strconv"
"time"
)

const (
OrderCountPerSecond = "o_cnt_per_s_"
OrderDifficulty = "order_diff"
OrderDifficultyKey = "order_diff"
ZklockDifficulty = "zklock_diff"
)

type OrderDifficultyEvaluator struct {
//currentDifficult *OrderDifficulty
//parentDifficult *OrderDifficulty
evaluator Evaluator
baseDifficulty *big.Int
orderTraffic int64
triggerThreshold float64
stopFuns []func()
calCount int64 //must be odd
evaluator Evaluator
stopFuns []func()
calCount int64 //must be odd
duration int64
}

type OrderDifficulty struct {
difficulty *big.Int
ordersNum int64
timeStamp int64
Difficulty string
OrdersNum int64
TimeStamp int64
}

func (evaluator *OrderDifficultyEvaluator) getCacheKey(createTime int64) (key string, expireAt int64) {
func (evaluator *OrderDifficultyEvaluator) getOrderCacheKey(createTime int64) (key string, expireAt int64) {
mod := createTime % evaluator.calCount
orderSection := createTime - mod
orderSectionStr := strconv.FormatInt(orderSection, 10)
return OrderCountPerSecond + orderSectionStr, createTime + evaluator.calCount
return OrderCountPerSecond + orderSectionStr, createTime + evaluator.calCount*evaluator.duration
}

func (evaluator *OrderDifficultyEvaluator) getDiffCacheKey(createTime int64) (key string, expireAt int64) {
mod := createTime % evaluator.calCount
orderSection := createTime - mod
orderSectionStr := strconv.FormatInt(orderSection, 10)
return OrderDifficultyKey + orderSectionStr, createTime + evaluator.calCount*evaluator.duration
}

func NewOrderDifficultyEvaluator(config OrderDifficultyConfig) *OrderDifficultyEvaluator {
if "" == config.BaseDifficulty {
config.BaseDifficulty = "0x10"
}
if 0 == config.CalCount {
config.CalCount = 1000
}
if 0 == config.Duration {
config.Duration = 2
}
if 0 == config.Threshold {
config.Threshold = 10
}
evaluator := &OrderDifficultyEvaluator{calCount: config.CalCount, duration: config.Duration}
baseDifficulty := types.HexToBigint(config.BaseDifficulty)
evaluator.evaluator = &LinearEvaluator{baseDifficulty: baseDifficulty, threshold: config.Threshold}
return evaluator
}

type OrderDifficultyConfig struct {
BaseDifficulty string
Threshold int64
CalCount int64
Duration int64
}

func (evaluator *OrderDifficultyEvaluator) Start() {
Expand All @@ -67,28 +99,55 @@ func (evaluator *OrderDifficultyEvaluator) Start() {
if err := zklock.TryLock(ZklockDifficulty); nil != err {
log.Errorf("erro:%s", err.Error())
} else {
now := time.Now().Unix()
orderCntList := []int64{}
for i := evaluator.calCount; i > 0; i-- {
t := now - i
cacheKey, _ := evaluator.getCacheKey(t)
if data, err := cache.Get(cacheKey); nil == err {
cnt, _ := strconv.ParseInt(string(data), 10, 0)
orderCntList = append(orderCntList, cnt)
}
if _, err1 := GetDifficulty(); nil != err1 {
diffHex := types.BigintToHex(evaluator.evaluator.getBaseDifficulty())
cache.Set(OrderDifficultyKey, []byte(diffHex), int64(10000))
}
//now := time.Now().Unix()
//orderCntList := []*OrderDifficulty{}
//for i := evaluator.calCount; i > 0; i-- {
// t := now - i*evaluator.duration
// cacheKey, _ := evaluator.getDiffCacheKey(t)
// orderDifficulty := &OrderDifficulty{}
// if data, err := cache.Get(cacheKey); nil == err {
// json.Unmarshal(data, orderDifficulty)
// }
// orderCntList = append(orderCntList, orderDifficulty)
//}
for {
select {
case <-time.After(2 * time.Second):
cacheKey, _ := evaluator.getCacheKey(time.Now().Unix() - 1)
if data, err := cache.Get(cacheKey); nil == err {
cnt, _ := strconv.ParseInt(string(data), 10, 0)
orderCntList = append(orderCntList, cnt)
case <-time.After(time.Duration(evaluator.duration) * time.Second):
now := time.Now().Unix()
if diffHex, err := GetDifficulty(); nil == err {
currentDiff := diffHex
orderDiff := &OrderDifficulty{}
cacheKey, _ := evaluator.getOrderCacheKey(now)
orderDiff.OrdersNum = 0
if data, err1 := cache.Get(cacheKey); nil == err1 {
if orderNum, err3 := strconv.Atoi(string(data)); nil == err3 {
orderDiff.OrdersNum = int64(orderNum)
}
}
diffCacheKey, _ := evaluator.getDiffCacheKey(now)
orderDiff.Difficulty = currentDiff
if data, err4 := json.Marshal(orderDiff); nil == err4 {
cache.Set(diffCacheKey, data, (evaluator.calCount+2)*evaluator.duration)
}
}
diff := evaluator.evaluator.CalcAndSaveDifficulty(orderCntList)
orderCntList := []*OrderDifficulty{}
for i := evaluator.calCount; i > 0; i-- {
t := now - i*evaluator.duration
diffCacheKey, _ := evaluator.getDiffCacheKey(t)
orderDifficulty := &OrderDifficulty{}
if data, err := cache.Get(diffCacheKey); nil == err {
json.Unmarshal(data, orderDifficulty)
}
orderCntList = append(orderCntList, orderDifficulty)
}
diff := evaluator.evaluator.CalcAndSaveDifficulty(orderCntList, evaluator.duration)
diffHash := common.BytesToHash(diff.Bytes())
cache.Set(OrderDifficulty, []byte(diffHash.Hex()), int64(0))
orderCntList = orderCntList[1:]
log.Infof("current order difficulty:%s", diff.String())
cache.Set(OrderDifficultyKey, []byte(diffHash.Hex()), int64(0))
}
}
}
Expand All @@ -101,12 +160,12 @@ func (evaluator *OrderDifficultyEvaluator) Stop() {
}
}

//add ordersNum
//add OrdersNum
func (evaluator *OrderDifficultyEvaluator) HandleNewOrder() {
watcher := &eventemitter.Watcher{
Concurrent: false, Handle: func(input eventemitter.EventData) error {
state := input.(*types.OrderState)
cacheKey, expireAt := evaluator.getCacheKey(state.RawOrder.CreateTime)
cacheKey, expireAt := evaluator.getOrderCacheKey(state.RawOrder.CreateTime)
_, err := cache.Incr(cacheKey)
if nil == err {
err = cache.ExpireAt(cacheKey, expireAt)
Expand All @@ -122,29 +181,71 @@ func (evaluator *OrderDifficultyEvaluator) HandleNewOrder() {
}

type Evaluator interface {
CalcAndSaveDifficulty(orderCntList []int64) *big.Int
CalcAndSaveDifficulty(orderCntList []*OrderDifficulty, duration int64) *big.Int
getBaseDifficulty() *big.Int
}

type LinearEvaluator struct {
baseDifficulty *big.Int
threshold int64
}

//控制订单的提交速度,随着订单的流量增大而增大
func (evaluator *LinearEvaluator) CalcAndSaveDifficulty(orderCntList []int64) *big.Int {
func (evaluator *LinearEvaluator) getBaseDifficulty() *big.Int {
return new(big.Int).Set(evaluator.baseDifficulty)
}

func (evaluator *LinearEvaluator) CalNextOrderCnt(orderCntList []*OrderDifficulty, duration int64) int64 {
xes := []float64{}
yes := []float64{}
now := time.Now().Unix()
for idx, cnt := range orderCntList {
xes = append(xes, float64(idx))
yes = append(yes, float64(cnt))
//println("sum",cnt.OrdersNum)
xes = append(xes, float64(now-duration*int64(len(orderCntList)-idx)))
yes = append(yes, float64(cnt.OrdersNum))
}
alpha, beta := stat.LinearRegression(xes, yes, nil, false)
return int64(beta*float64(now) + alpha)
}

func GetDifficulty() (common.Hash, error) {
if data, err := cache.Get(OrderDifficulty); nil == err {
return common.HexToHash(string(data)), nil
//控制订单的提交速度,随着订单的流量增大而增大
func (evaluator *LinearEvaluator) CalcAndSaveDifficulty(orderCntList []*OrderDifficulty, duration int64) *big.Int {
nextCnt := evaluator.CalNextOrderCnt(orderCntList, duration)
log.Infof("next order count:%d", nextCnt)
orderList := append(orderCntList, &OrderDifficulty{OrdersNum: nextCnt})
return evaluator.nextDifficulty(orderList)
}

func NewOrderDiff(ordersNum int64, diff string) *OrderDifficulty {
return &OrderDifficulty{OrdersNum: ordersNum, Difficulty: diff}
}

func NewLinearEvaluator(baseDifficulty *big.Int, threshold int64) *LinearEvaluator {
return &LinearEvaluator{baseDifficulty: baseDifficulty, threshold: threshold}
}

func (evaluator *LinearEvaluator) nextDifficulty(orderCntList []*OrderDifficulty) *big.Int {
nextOrderDiff := orderCntList[len(orderCntList)-1]
if nextOrderDiff.OrdersNum < evaluator.threshold {
return evaluator.baseDifficulty
} else {
currentDiff := new(big.Int).Set(evaluator.baseDifficulty)
if currentDiffHex, err := GetDifficulty(); nil == err {
currentDiff = types.HexToBigint(currentDiffHex)
}
addRatio := big.NewInt(3)
if nextOrderDiff.OrdersNum > int64(float64(evaluator.threshold)*1.5) {
addRatio = big.NewInt(1)
} else if nextOrderDiff.OrdersNum < int64(float64(evaluator.threshold)*1.2) {
addRatio = big.NewInt(5)
}
return new(big.Int).Add(currentDiff, new(big.Int).Quo(currentDiff, addRatio))
}
}

func GetDifficulty() (string, error) {
if data, err := cache.Get(OrderDifficultyKey); nil == err {
return string(data), nil
} else {
return common.Hash{}, err
return "0x0", err
}
}
Loading