145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package api
|
|
|
|
import (
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/database"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
|
|
)
|
|
|
|
type Api struct {
|
|
db *database.Database
|
|
p2api *P2PoolApi
|
|
}
|
|
|
|
func New(db *database.Database, p2api *P2PoolApi) (*Api, error) {
|
|
api := &Api{
|
|
db: db,
|
|
p2api: p2api,
|
|
}
|
|
|
|
return api, nil
|
|
}
|
|
|
|
func (a *Api) GetDatabase() *database.Database {
|
|
return a.db
|
|
}
|
|
|
|
func (a *Api) GetBlockWindowPayouts(tip *database.Block) (shares map[uint64]types.Difficulty) {
|
|
if tip == nil {
|
|
return nil
|
|
}
|
|
//TODO: adjust for fork
|
|
shares = make(map[uint64]types.Difficulty)
|
|
|
|
var blockDepth uint64
|
|
|
|
block := tip
|
|
|
|
blockCache := make(map[uint64]*database.Block, a.p2api.Consensus().ChainWindowSize)
|
|
for b := range a.db.GetBlocksInWindow(&tip.Height, a.p2api.Consensus().ChainWindowSize) {
|
|
blockCache[b.Height] = b
|
|
}
|
|
|
|
mainchainDiff := a.p2api.MainDifficultyByHeight(randomx.SeedHeight(tip.Main.Height))
|
|
|
|
if mainchainDiff == types.ZeroDifficulty {
|
|
return nil
|
|
}
|
|
|
|
//TODO: remove this hack
|
|
sidechainVersion := sidechain.ShareVersion_V1
|
|
if tip.Timestamp >= sidechain.ShareVersion_V2MainNetTimestamp {
|
|
sidechainVersion = sidechain.ShareVersion_V2
|
|
}
|
|
maxPplnsWeight := types.MaxDifficulty
|
|
|
|
if sidechainVersion > sidechain.ShareVersion_V1 {
|
|
maxPplnsWeight = mainchainDiff.Mul64(2)
|
|
}
|
|
var pplnsWeight types.Difficulty
|
|
|
|
for {
|
|
|
|
curWeight := block.Difficulty
|
|
|
|
for uncle := range a.db.GetUnclesByParentId(block.Id) {
|
|
if (tip.Height - uncle.Block.Height) >= a.p2api.Consensus().ChainWindowSize {
|
|
continue
|
|
}
|
|
|
|
unclePenalty := uncle.Block.Difficulty.Mul64(a.p2api.Consensus().UnclePenalty).Div64(100)
|
|
uncleWeight := uncle.Block.Difficulty.Sub(unclePenalty)
|
|
newPplnsWeight := pplnsWeight.Add(uncleWeight)
|
|
|
|
if newPplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
|
continue
|
|
}
|
|
|
|
curWeight = curWeight.Add(uncleWeight)
|
|
|
|
if _, ok := shares[uncle.Block.MinerId]; !ok {
|
|
shares[uncle.Block.MinerId] = types.DifficultyFrom64(0)
|
|
}
|
|
shares[uncle.Block.MinerId] = shares[uncle.Block.MinerId].Add(uncleWeight)
|
|
}
|
|
|
|
if _, ok := shares[block.MinerId]; !ok {
|
|
shares[block.MinerId] = types.DifficultyFrom64(0)
|
|
}
|
|
shares[block.MinerId] = shares[block.MinerId].Add(curWeight)
|
|
|
|
pplnsWeight = pplnsWeight.Add(curWeight)
|
|
|
|
if pplnsWeight.Cmp(maxPplnsWeight) > 0 {
|
|
break
|
|
}
|
|
|
|
blockDepth++
|
|
|
|
if blockDepth >= a.p2api.Consensus().ChainWindowSize {
|
|
break
|
|
}
|
|
|
|
if block.Height == 0 {
|
|
break
|
|
}
|
|
|
|
if b, ok := blockCache[block.Height-1]; ok && b.Id == block.PreviousId {
|
|
block = b
|
|
} else {
|
|
block = a.db.GetBlockById(block.PreviousId)
|
|
}
|
|
if block == nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
totalReward := tip.Coinbase.Reward
|
|
|
|
if totalReward > 0 {
|
|
totalWeight := types.DifficultyFrom64(0)
|
|
for _, w := range shares {
|
|
totalWeight = totalWeight.Add(w)
|
|
}
|
|
|
|
w := types.DifficultyFrom64(0)
|
|
rewardGiven := types.DifficultyFrom64(0)
|
|
|
|
for miner, weight := range shares {
|
|
w = w.Add(weight)
|
|
nextValue := w.Mul64(totalReward).Div(totalWeight)
|
|
shares[miner] = nextValue.Sub(rewardGiven)
|
|
rewardGiven = nextValue
|
|
}
|
|
}
|
|
|
|
if (block == nil || block.Height != 0) &&
|
|
((sidechainVersion == sidechain.ShareVersion_V1 && blockDepth != a.p2api.Consensus().ChainWindowSize) ||
|
|
(sidechainVersion > sidechain.ShareVersion_V1 && (blockDepth != a.p2api.Consensus().ChainWindowSize) && pplnsWeight.Cmp(maxPplnsWeight) <= 0)) {
|
|
return nil
|
|
}
|
|
|
|
return shares
|
|
}
|