Refactor pool_info API, deprecate several fields

This commit is contained in:
DataHoarder 2023-07-25 07:59:41 +02:00
parent e7e1aee11b
commit b601e49a8a
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
15 changed files with 266 additions and 242 deletions

View file

@ -93,7 +93,40 @@ func main() {
return result
}
fillSideBlockResult := func(params url.Values, sideBlocks chan *index.SideBlock) chan *index.SideBlock {
fillSideBlockResult := func(fillUncles, fillMined, fillMiner bool, sideBlock *index.SideBlock) *index.SideBlock {
if fillMiner {
miner := indexDb.GetMiner(sideBlock.Miner)
sideBlock.MinerAddress = miner.Address()
sideBlock.MinerAlias = miner.Alias()
}
if fillMined {
mainTipAtHeight := indexDb.GetMainBlockByHeight(sideBlock.MainHeight)
if mainTipAtHeight != nil {
sideBlock.MinedMainAtHeight = mainTipAtHeight.Id == sideBlock.MainId
sideBlock.MainDifficulty = mainTipAtHeight.Difficulty
} else {
poolInfo := lastPoolInfo.Load()
if (poolInfo.MainChain.Height + 1) == sideBlock.MainHeight {
sideBlock.MainDifficulty = lastPoolInfo.Load().MainChain.NextDifficulty.Lo
} else if poolInfo.MainChain.Height == sideBlock.MainHeight {
sideBlock.MainDifficulty = lastPoolInfo.Load().MainChain.Difficulty.Lo
}
}
}
if fillUncles {
for u := range indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId) {
sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{
TemplateId: u.TemplateId,
Miner: u.Miner,
SideHeight: u.SideHeight,
Difficulty: u.Difficulty,
})
}
}
return sideBlock
}
fillSideBlockChannelResult := func(params url.Values, sideBlocks chan *index.SideBlock) chan *index.SideBlock {
result := make(chan *index.SideBlock)
fillUncles := !params.Has("noUncles")
fillMined := !params.Has("noMainStatus")
@ -101,36 +134,7 @@ func main() {
go func() {
defer close(result)
for sideBlock := range sideBlocks {
if fillMiner {
miner := indexDb.GetMiner(sideBlock.Miner)
sideBlock.MinerAddress = miner.Address()
sideBlock.MinerAlias = miner.Alias()
}
if fillMined {
mainTipAtHeight := indexDb.GetMainBlockByHeight(sideBlock.MainHeight)
if mainTipAtHeight != nil {
sideBlock.MinedMainAtHeight = mainTipAtHeight.Id == sideBlock.MainId
sideBlock.MainDifficulty = mainTipAtHeight.Difficulty
} else {
poolInfo := lastPoolInfo.Load()
if (poolInfo.MainChain.Height + 1) == sideBlock.MainHeight {
sideBlock.MainDifficulty = lastPoolInfo.Load().MainChain.NextDifficulty.Lo
} else if poolInfo.MainChain.Height == sideBlock.MainHeight {
sideBlock.MainDifficulty = lastPoolInfo.Load().MainChain.Difficulty.Lo
}
}
}
if fillUncles {
for u := range indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId) {
sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{
TemplateId: u.TemplateId,
Miner: u.Miner,
SideHeight: u.SideHeight,
Difficulty: u.Difficulty,
})
}
}
result <- sideBlock
result <- fillSideBlockResult(fillUncles, fillMined, fillMiner, sideBlock)
}
}()
return result
@ -160,7 +164,7 @@ func main() {
tip := indexDb.GetSideBlockTip()
if oldPoolInfo != nil && oldPoolInfo.SideChain.Id == tip.TemplateId {
if oldPoolInfo != nil && oldPoolInfo.SideChain.LastBlock.TemplateId == tip.TemplateId {
//no changes!
//this race is ok
@ -168,6 +172,8 @@ func main() {
return
}
var bottom *index.SideBlock
var bottomUncles []*index.SideBlock
blockCount := 0
uncleCount := 0
@ -197,7 +203,9 @@ func main() {
}
}, func(slot index.SideBlockWindowSlot) {
blockCount++
bottomUncles = slot.Uncles
uncleCount += len(slot.Uncles)
bottom = slot.Block
}); err != nil {
log.Printf("error scanning PPLNS window: %s", err)
@ -286,13 +294,9 @@ func main() {
result := &cmdutils.PoolInfoResult{
SideChain: cmdutils.PoolInfoResultSideChain{
Consensus: consensus,
Id: tip.TemplateId,
Height: tip.SideHeight,
Version: sidechain.P2PoolShareVersion(consensus, tip.Timestamp),
Difficulty: types.DifficultyFrom64(tip.Difficulty),
CumulativeDifficulty: tip.CumulativeDifficulty,
Timestamp: tip.Timestamp,
LastBlock: fillSideBlockResult(true, false, true, tip),
SecondsSinceLastBlock: max(0, time.Now().Unix()-int64(tip.Timestamp)),
LastFound: nil, //to be filled later
Effort: cmdutils.PoolInfoResultSideChainEffort{
Current: currentEffort,
Average10: averageEffort(10),
@ -306,22 +310,45 @@ func main() {
Uncles: uncleCount,
Weight: pplnsWeight,
Versions: versions,
Top: tip.TemplateId,
Bottom: bottom.TemplateId,
BottomUncles: func() (result []types.Hash) {
for _, u := range bottomUncles {
result = append(result, u.TemplateId)
}
return result
}(),
},
WindowSize: blockCount,
MaxWindowSize: int(consensus.ChainWindowSize),
BlockTime: int(consensus.TargetBlockTime),
UnclePenalty: int(consensus.UnclePenalty),
Found: totalKnown.blocksFound,
Miners: totalKnown.minersKnown,
Found: totalKnown.blocksFound,
Miners: totalKnown.minersKnown,
Id: tip.TemplateId,
Height: tip.SideHeight,
Version: sidechain.P2PoolShareVersion(consensus, tip.Timestamp),
Difficulty: types.DifficultyFrom64(tip.Difficulty),
CumulativeDifficulty: tip.CumulativeDifficulty,
Timestamp: tip.Timestamp,
WindowSize: blockCount,
MaxWindowSize: int(consensus.ChainWindowSize),
BlockTime: int(consensus.TargetBlockTime),
UnclePenalty: int(consensus.UnclePenalty),
},
MainChain: cmdutils.PoolInfoResultMainChain{
Consensus: cmdutils.PoolInfoResultMainChainConsensus{
HardForks: sidechain.NetworkHardFork(consensus),
BlockTime: monero.BlockTime,
},
Id: mainTip.Id,
CoinbaseId: mainTip.CoinbaseId,
Height: mainTip.Height,
Difficulty: networkDifficulty,
Reward: mainTip.Reward,
BaseReward: expectedBaseBlockReward,
NextDifficulty: minerDifficulty,
BlockTime: monero.BlockTime,
BlockTime: monero.BlockTime,
},
Versions: struct {
P2Pool cmdutils.VersionInfo `json:"p2pool"`
@ -329,6 +356,10 @@ func main() {
}{P2Pool: getP2PoolVersion(), Monero: getMoneroVersion()},
}
if len(lastBlocksFound) > 0 {
result.SideChain.LastFound = fillFoundBlockResult(nil, lastBlocksFound[:1])[0]
}
lastPoolInfo.Store(result)
}
@ -724,7 +755,7 @@ func main() {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
result := fillSideBlockResult(params, indexDb.GetSideBlocksInWindow(from, window))
result := fillSideBlockChannelResult(params, indexDb.GetSideBlocksInWindow(from, window))
_ = httputils.StreamJsonSlice(request, writer, result)
})
@ -782,7 +813,7 @@ func main() {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
result := fillSideBlockResult(params, indexDb.GetSideBlocksByMinerIdInWindow(miner.Id(), from, window))
result := fillSideBlockChannelResult(params, indexDb.GetSideBlocksByMinerIdInWindow(miner.Id(), from, window))
_ = httputils.StreamJsonSlice(request, writer, result)
})
@ -1192,7 +1223,7 @@ func main() {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
result := fillSideBlockResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, inclusion))
result := fillSideBlockChannelResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, inclusion))
_ = httputils.StreamJsonSlice(request, writer, result)
})
@ -1249,7 +1280,7 @@ func main() {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
result := fillSideBlockResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, index.InclusionInVerifiedChain))
result := fillSideBlockChannelResult(params, indexDb.GetShares(limit, minerId, onlyBlocks, index.InclusionInVerifiedChain))
_ = httputils.StreamJsonSlice(request, writer, result)
})
@ -1483,7 +1514,7 @@ func main() {
c := make(chan *index.SideBlock, 1)
c <- block
close(c)
_ = httputils.EncodeJson(request, writer, <-fillSideBlockResult(params, c))
_ = httputils.EncodeJson(request, writer, <-fillSideBlockChannelResult(params, c))
}
})
@ -1711,15 +1742,15 @@ func main() {
SidechainDifficulty uint64 `json:"sidechainDifficulty"`
SidechainHeight uint64 `json:"sidechainHeight"`
}{
HashRate: poolInfo.SideChain.Difficulty.Div64(consensus.TargetBlockTime).Lo,
HashRate: poolInfo.SideChain.LastBlock.Difficulty / consensus.TargetBlockTime,
Miners: poolInfo.SideChain.Miners,
TotalHashes: poolInfo.SideChain.CumulativeDifficulty.Lo,
TotalHashes: poolInfo.SideChain.LastBlock.CumulativeDifficulty.Lo,
LastBlockFound: lastBlockFound,
LastBlockFoundTime: lastBlockFoundTime,
TotalBlocksFound: poolInfo.SideChain.Found,
PPLNSWindowSize: uint64(poolInfo.SideChain.WindowSize),
SidechainDifficulty: poolInfo.SideChain.Difficulty.Lo,
SidechainHeight: poolInfo.SideChain.Height,
PPLNSWindowSize: uint64(poolInfo.SideChain.Window.Blocks),
SidechainDifficulty: poolInfo.SideChain.LastBlock.Difficulty,
SidechainHeight: poolInfo.SideChain.LastBlock.SideHeight,
},
})
})
@ -1748,7 +1779,18 @@ func main() {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, _ = writer.Write([]byte(fmt.Sprintf("{\"config\":{\"ports\":[{\"port\":3333,\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":300000000},\"network\":{\"height\":%d},\"pool\":{\"stats\":{\"lastBlockFound\":\"%d\"},\"blocks\":[\"%s...%s:%d\",\"%d\"],\"miners\":%d,\"hashrate\":%d,\"roundHashes\":%d}}", mainTip.Height, lastBlockFoundTime*1000, hex.EncodeToString(lastBlockFoundHash[:2]), hex.EncodeToString(lastBlockFoundHash[types.HashSize-2:]), lastBlockFoundTime, lastBlockFound, poolInfo.SideChain.Miners, poolInfo.SideChain.Difficulty.Div64(consensus.TargetBlockTime).Lo, poolInfo.SideChain.CumulativeDifficulty.Sub(lastBlockCumulativeDifficulty).Lo)))
_, _ = writer.Write([]byte(fmt.Sprintf(
"{\"config\":{\"ports\":[{\"port\":3333,\"tls\":false}],\"fee\":0,\"minPaymentThreshold\":300000000},\"network\":{\"height\":%d},\"pool\":{\"stats\":{\"lastBlockFound\":\"%d\"},\"blocks\":[\"%s...%s:%d\",\"%d\"],\"miners\":%d,\"hashrate\":%d,\"roundHashes\":%d}}",
mainTip.Height,
lastBlockFoundTime*1000,
hex.EncodeToString(lastBlockFoundHash[:2]),
hex.EncodeToString(lastBlockFoundHash[types.HashSize-2:]),
lastBlockFoundTime,
lastBlockFound,
poolInfo.SideChain.Miners,
poolInfo.SideChain.LastBlock.Difficulty/consensus.TargetBlockTime,
poolInfo.SideChain.LastBlock.CumulativeDifficulty.Sub(lastBlockCumulativeDifficulty).Lo,
)))
})
server := &http.Server{

View file

@ -72,11 +72,11 @@ type SideBlock struct {
// Extra information filled just for JSON purposes
MinedMainAtHeight bool `json:"mined_main_at_height"`
MinedMainAtHeight bool `json:"mined_main_at_height,omitempty"`
MinerAddress *address.Address `json:"miner_address,omitempty"`
MinerAlias string `json:"miner_alias,omitempty"`
Uncles []SideBlockUncleEntry `json:"uncles,omitempty"`
MainDifficulty uint64 `json:"main_difficulty"`
MainDifficulty uint64 `json:"main_difficulty,omitempty"`
}
type SideBlockUncleEntry struct {

View file

@ -56,19 +56,54 @@ type PoolInfoResult struct {
}
type PoolInfoResultSideChain struct {
Consensus *sidechain.Consensus `json:"consensus"`
Id types.Hash `json:"id"`
Height uint64 `json:"height"`
Version sidechain.ShareVersion `json:"version"`
Difficulty types.Difficulty `json:"difficulty"`
CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"`
SecondsSinceLastBlock int64 `json:"seconds_since_last_block"`
Timestamp uint64 `json:"timestamp"`
Effort PoolInfoResultSideChainEffort `json:"effort"`
Window PoolInfoResultSideChainWindow `json:"window"`
WindowSize int `json:"window_size"`
Found uint64 `json:"found"`
Miners uint64 `json:"miners"`
// Consensus Specifies the consensus parameters for the backing p2pool instance
Consensus *sidechain.Consensus `json:"consensus"`
// LastBlock Last sidechain block on database
LastBlock *index.SideBlock `json:"last_block"`
// SecondsSinceLastBlock
// Prefer using max(0, time.Now().Unix()-int64(LastBlock .Timestamp)) instead
SecondsSinceLastBlock int64 `json:"seconds_since_last_block"`
// LastFound Last sidechain block on database found and accepted on Monero network
LastFound *index.FoundBlock `json:"last_found"`
Effort PoolInfoResultSideChainEffort `json:"effort"`
Window PoolInfoResultSideChainWindow `json:"window"`
// Found Total count of found blocks in database
Found uint64 `json:"found"`
// Miners Total count of miners in database
Miners uint64 `json:"miners"`
// Id Available on LastBlock .TemplateId
// Deprecated
Id types.Hash `json:"id"`
// Height Available on LastBlock .SideHeight
// Deprecated
Height uint64 `json:"height"`
// Version Available via sidechain.P2PoolShareVersion
// Deprecated
Version sidechain.ShareVersion `json:"version"`
// Difficulty Available on LastBlock .Difficulty
// Deprecated
Difficulty types.Difficulty `json:"difficulty"`
// CumulativeDifficulty Available on LastBlock .CumulativeDifficulty
// Deprecated
CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"`
// Timestamp Available on LastBlock .Timestamp
// Deprecated
Timestamp uint64 `json:"timestamp"`
// WindowSize Available on Window .Blocks
// Deprecated
WindowSize int `json:"window_size"`
// MaxWindowSize Available on Consensus
// Deprecated
@ -96,21 +131,44 @@ type PoolInfoResultSideChainEffort struct {
Last []PoolInfoResultSideChainEffortLastEntry `json:"last"`
}
type PoolInfoResultSideChainWindow struct {
Miners int `json:"miners"`
Blocks int `json:"blocks"`
Uncles int `json:"uncles"`
// Miners Unique miners found in window
Miners int `json:"miners"`
// Blocks total count of blocks in the window, including uncles
Blocks int `json:"blocks"`
Uncles int `json:"uncles"`
// Top TemplateId of the window tip
Top types.Hash `json:"top"`
// Bottom TemplateId of the last non-uncle block in the window
Bottom types.Hash `json:"bottom"`
// BottomUncles TemplateId of the uncles included under the last block, if any
BottomUncles []types.Hash `json:"bottom_uncles,omitempty"`
Weight types.Difficulty `json:"weight"`
Versions []SideChainVersionEntry `json:"versions"`
}
type PoolInfoResultMainChainConsensus struct {
BlockTime uint64 `json:"block_time"`
// HardFork information for Monero known hardfork by backing p2pool
HardForks []sidechain.HardFork `json:"hard_forks,omitempty"`
}
type PoolInfoResultMainChain struct {
Id types.Hash `json:"id"`
Height uint64 `json:"height"`
Difficulty types.Difficulty `json:"difficulty"`
Reward uint64 `json:"reward"`
BaseReward uint64 `json:"base_reward"`
Consensus PoolInfoResultMainChainConsensus `json:"consensus"`
Id types.Hash `json:"id"`
CoinbaseId types.Hash `json:"coinbase_id"`
Height uint64 `json:"height"`
Difficulty types.Difficulty `json:"difficulty"`
Reward uint64 `json:"reward"`
BaseReward uint64 `json:"base_reward"`
NextDifficulty types.Difficulty `json:"next_difficulty"`
BlockTime int `json:"block_time"`
// BlockTime included in Consensus
// Deprecated
BlockTime int `json:"block_time"`
}
type MinerInfoBlockData struct {

View file

@ -1,7 +1,11 @@
{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %}
{% code
type ApiPage struct {
// inherit from base page, so its' title is used in error page.
BasePage
PoolInfoExample cmdutils.PoolInfoResult
}
%}
@ -37,122 +41,9 @@ pre {
<h3 class="mono">/api/pool_info</h3>
<p>Response contains general information on the current status of the P2Pool sidechain and Monero network, release versions, and other parameters.</p>
<pre class="smaller">
[result has been cut due to size, relevant portions shown]
[Live result. Has been cut due to size, relevant portions shown]
curl --silent https://{%s p.Context().NetServiceAddress %}/api/pool_info
{
"sidechain": {
"consensus": {
"network_type": "mainnet",
"name": "mainnet test 2",
"password": "",
"block_time": 10,
"min_diff": 100000,
"pplns_window": 2160,
"uncle_penalty": 20
},
"id": "d526cf516019ece4dbb5782f429a8333df9979932510ef26a91a5c2d02e6ac4a",
"height": 4868262,
"difficulty": "00000000000000000000000077cdd958",
"cumulative_difficulty": "0000000000000000001aed8a900b6e8e",
"timestamp": 1681256801,
"effort": {
"current": 109.31824000162864,
"average10": 32.98600297507685,
"average": 77.65727684146015,
"average200": 89.19728805502012,
"last": [
{
"id": "e3ec5e7e7cb76cfd2cff284a8812e4b0aeddf04115eb04440e6d26bd9825b7bb",
"effort": 9.403366358670995
},
{
"id": "e6ceaa02039c09022baaec73146340e20a64b6da19ecd234169c7bf73a569bdd",
"effort": 1.8807123325645854
},
{
"id": "393abe826d25c72fb320fa4bad980728c8e54756fa3ae8945bbb13af964c251c",
"effort": 91.37117352907373
},
{
"id": "d6ff0c1956d6743e137475a3b23823f620bf964ec89eb4891870af4c5db77a2a",
"effort": 22.115510860859477
},
{
"id": "544ad16d7c83f6e3636e965eed17a92449e64538d571064d1f3fa0c3d35d2768",
"effort": 47.80476995257288
},
{
"id": "6220069168433fa220f94600dbca7468d0e94a29a669eeb5e1449e181bdac624",
"effort": 6.130222064837835
},
{
"id": "108ae4af9aad47a1968c205a76ea57fd2fa0cc480d6109ee36f4a0eaa4422810",
"effort": 32.87182581089726
},
{
"id": "7a3d57b90e537f6414f9f898e76f16c22af20f77512dc2cbcf7571b561ecb88c",
"effort": 39.24260486339777
}
]
},
"window": {
"miners": 28,
"blocks": 323,
"uncles": 13,
"weight": "00000000000000000000009eb206d209",
"versions": [
{
"weight": "00000000000000000000005487eca8a5",
"share": 53.266160234670814,
"count": 179,
"software_id": 0,
"software_version": 196610,
"software_string": "P2Pool v3.2"
},
{
"weight": "00000000000000000000003245f084b0",
"share": 31.679050570070547,
"count": 106,
"software_id": 0,
"software_version": 196609,
"software_string": "P2Pool v3.1"
},
{
"weight": "000000000000000000000017e429a4b4",
"share": 15.054789195258637,
"count": 51,
"software_id": 0,
"software_version": 196608,
"software_string": "P2Pool v3.0"
}
]
},
"window_size": 323,
"max_window_size": 2160,
"block_time": 10,
"uncle_penalty": 20,
"found": 22805,
"miners": 5142
},
"mainchain": {
"id": "8bc31d75dadb3d2eae8f30232f327d5726e8f9cea9185f3ef56255594605a1c8",
"height": 2862299,
"difficulty": "00000000000000000000004c2f36c75d",
"block_time": 120
},
"versions": {
"p2pool": {
"version": "v3.2",
"timestamp": 1680284891,
"link": "https://github.com/SChernykh/p2pool/releases/tag/v3.2"
},
"monero": {
"version": "v0.18.2.2",
"timestamp": 1681139283,
"link": "https://github.com/monero-project/monero/releases/tag/v0.18.2.2"
}
}
}
{%= encodeJson(p.PoolInfoExample, true) %}
</pre>
</div>

View file

@ -58,19 +58,19 @@ type CalculateShareTimePageEffortEntry struct {
<div style="text-align: center">
<table class="center" style="max-width: calc(15em + 15em + 15em + 15em);">
{% code between := (float64(p.Context().Pool.SideChain.Difficulty.Lo) / (p.Hashrate * p.Magnitude)) %}
{% code between := (float64(p.Context().Pool.SideChain.LastBlock.Difficulty) / (p.Hashrate * p.Magnitude)) %}
{% code between_solo := (float64(p.Context().Pool.MainChain.Difficulty.Lo) / (p.Hashrate * p.Magnitude)) %}
<tr style="line-height: 1.5;">
<td style="width: 15em"><strong>P2Pool Difficulty</strong><br/>{%s si_units(p.Context().Pool.SideChain.Difficulty.Lo, 2) %}</td>
<td style="width: 15em"><strong>P2Pool Hashrate</strong><br/>{%s si_units(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s</td>
<td style="width: 15em"><strong>P2Pool Difficulty</strong><br/>{%s si_units(p.Context().Pool.SideChain.LastBlock.Difficulty, 2) %}</td>
<td style="width: 15em"><strong>P2Pool Hashrate</strong><br/>{%s si_units(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s</td>
<td style="width: 15em"><strong>Your Hashrate</strong><br/>{%s si_units(p.Hashrate * p.Magnitude, 2) %}H/s</td>
<td title="Mean frequency between P2Pool shares" style="width: 15em; border: #ff6600 dashed 1px;"><strong>Your Share Mean<br/>{%s time_duration_long(between) %}</strong></td>
</tr>
<tr><th colspan="4">&nbsp;</th></tr>
<tr style="line-height: 1.5;">
<td><strong>Monero Difficulty</strong><br/>{%s si_units(p.Context().Pool.MainChain.Difficulty.Lo, 2) %}</td>
<td><strong>Monero Hashrate</strong><br/>{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.BlockTime)), 2) %}H/s</td>
<td title="Mean frequency between P2Pool finds Monero Blocks"><strong>P2Pool Block Mean</strong><br/><em>{%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime))) %}</em></td>
<td><strong>Monero Hashrate</strong><br/>{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.Consensus.BlockTime)), 2) %}H/s</td>
<td title="Mean frequency between P2Pool finds Monero Blocks"><strong>P2Pool Block Mean</strong><br/><em>{%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime))) %}</em></td>
<td title="Mean frequency between Solo Monero Blocks (without P2Pool)"><strong>Your Solo Block Mean</strong><br/><em>{%s time_duration_long(between_solo) %}</em></td>
</tr>
<tr><th colspan="4">&nbsp;</th></tr>

View file

@ -161,6 +161,8 @@ func diff_hashrate(v any, blockTime uint64) uint64 {
}
} else if d, ok := v.(types.Difficulty); ok {
return d.Div64(blockTime).Lo
} else if d, ok := v.(uint64); ok {
return d / blockTime
}
return 0
}

View file

@ -11,9 +11,21 @@
{% import hex2 "encoding/hex" %}
{% import fasthex "github.com/tmthrgd/go-hex" %}
{% import qt "github.com/valyala/quicktemplate" %}
{% stripspace %}
{% code
func streamencodeJson(w *qt.Writer, val any, indent bool) {
encoder := utils.NewJSONEncoder(w.E())
if indent {
encoder.SetIndent("", " ")
}
if err := encoder.EncodeWithOption(val, utils.JsonEncodeOptions...); err != nil {
panic(err)
}
}
%}
{% func hex(ctx *GlobalRequestContext, val any) %}
{% switch s := val.(type) %}

View file

@ -27,14 +27,14 @@
</h4>
{% if pool := ctx.Pool; pool != nil %}
{% if uint64(pool.Versions.P2Pool.Timestamp+3600*24*30) > pool.SideChain.Timestamp || uint64(pool.Versions.Monero.Timestamp+3600*24*7) > pool.SideChain.Timestamp %}
{% if uint64(pool.Versions.P2Pool.Timestamp+3600*24*30) > pool.SideChain.LastBlock.Timestamp || uint64(pool.Versions.Monero.Timestamp+3600*24*7) > pool.SideChain.LastBlock.Timestamp %}
<h4 style="font-size: 15px; margin: 4px 0px 0px; color:yellow;">
{% else %}
<h4 style="font-size: 13px; margin-top: 5px; ">
{% endif %}
Latest releases: <a href="{%s pool.Versions.P2Pool.Link %}" target="_blank">P2Pool {%s pool.Versions.P2Pool.Version %}</a> <em title="{%s utc_date(uint64(pool.Versions.P2Pool.Timestamp)) %}"><small>{%f.1 float64((pool.SideChain.Timestamp - uint64(pool.Versions.P2Pool.Timestamp))) / (3600*24) %} day(s) ago</small></em>
Latest releases: <a href="{%s pool.Versions.P2Pool.Link %}" target="_blank">P2Pool {%s pool.Versions.P2Pool.Version %}</a> <em title="{%s utc_date(uint64(pool.Versions.P2Pool.Timestamp)) %}"><small>{%f.1 float64((pool.SideChain.LastBlock.Timestamp - uint64(pool.Versions.P2Pool.Timestamp))) / (3600*24) %} day(s) ago</small></em>
::
<a href="{%s pool.Versions.Monero.Link %}" target="_blank">Monero {%s pool.Versions.Monero.Version %}</a> <em title="{%s utc_date(uint64(pool.Versions.Monero.Timestamp)) %}"><small>{%f.1 float64((pool.SideChain.Timestamp - uint64(pool.Versions.Monero.Timestamp))) / (3600*24) %} day(s) ago</small></em>
<a href="{%s pool.Versions.Monero.Link %}" target="_blank">Monero {%s pool.Versions.Monero.Version %}</a> <em title="{%s utc_date(uint64(pool.Versions.Monero.Timestamp)) %}"><small>{%f.1 float64((pool.SideChain.LastBlock.Timestamp - uint64(pool.Versions.Monero.Timestamp))) / (3600*24) %} day(s) ago</small></em>
</h4>
{% endif %}

View file

@ -35,10 +35,10 @@ type IndexPage struct {
<th style="width: 15em">Monero Hashrate</th>
</tr>
<tr>
<td title="{%= hex(p.Context(), p.Context().Pool.SideChain.Id) %}"><a href="/share/{%= hex(p.Context(), p.Context().Pool.SideChain.Id) %}">{%dul p.Context().Pool.SideChain.Height %}</a></td>
<td>{%s si_units(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s</td>
<td title="{%= hex(p.Context(), p.Context().Pool.SideChain.LastBlock.TemplateId) %}"><a href="/share/{%= hex(p.Context(), p.Context().Pool.SideChain.LastBlock.TemplateId) %}">{%dul p.Context().Pool.SideChain.LastBlock.SideHeight %}</a></td>
<td>{%s si_units(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s</td>
<td title="{%= hex(p.Context(), p.Context().Pool.MainChain.Id) %}"><a href="/b/{%s benc(p.Context().Pool.MainChain.Height) %}">{%dul p.Context().Pool.MainChain.Height %}</a></td>
<td>{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.BlockTime)), 2) %}H/s</td>
<td>{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.Consensus.BlockTime)), 2) %}H/s</td>
</tr>
<tr><th colspan="4">&nbsp;</th></tr>
<tr>
@ -48,8 +48,8 @@ type IndexPage struct {
<th title="This includes blocks the site knows about since it started observing. There might be more orphaned or produced by other sidechain not included here.">Blocks Found</th>
</tr>
<tr>
<td>{%s si_units(p.Context().Pool.SideChain.Difficulty.Lo, 2) %}</td>
<td>{%f.2 (float64(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime)) / float64(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.BlockTime))))*100 %}%</td>
<td>{%s si_units(p.Context().Pool.SideChain.LastBlock.Timestamp, 2) %}</td>
<td>{%f.2 (float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)) / float64(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.Consensus.BlockTime))))*100 %}%</td>
<td>{%s si_units(p.Context().Pool.MainChain.Difficulty.Lo, 2) %}</td>
<td>{%dul p.Context().Pool.SideChain.Found %}</td>
</tr>
@ -69,9 +69,9 @@ type IndexPage struct {
/
<span class="small" style="color: {%s effort_color(p.Context().Pool.SideChain.Effort.Average200) %};" title="Last 200 found blocks">{%f.2 p.Context().Pool.SideChain.Effort.Average200 %}%</span>
</td>
<td>{%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime))) %}</td>
{% if len(p.FoundBlocks) > 0 %}
<td title="{%s utc_date(p.FoundBlocks[0].MainBlock.Timestamp) %}">{%s time_elapsed_short(p.FoundBlocks[0].MainBlock.Timestamp) %}</td>
<td>{%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime))) %}</td>
{% if p.Context().Pool.SideChain.LastFound != nil %}
<td title="{%s utc_date(p.Context().Pool.SideChain.LastFound.MainBlock.Timestamp) %}">{%s time_elapsed_short(p.Context().Pool.SideChain.LastFound.MainBlock.Timestamp) %}</td>
{% else %}
<td>-</td>
{% endif %}
@ -89,7 +89,7 @@ type IndexPage struct {
{%f.2 p.Context().Pool.SideChain.Effort.Current %}%
</td>
<td>{%d p.Context().Pool.SideChain.Window.Blocks %} blocks (+{%d p.Context().Pool.SideChain.Window.Uncles %} uncles)</td>
<td title="{%s utc_date(p.Context().Pool.SideChain.Timestamp) %}">{%s time_elapsed_short(p.Context().Pool.SideChain.Timestamp) %}</td>
<td title="{%s utc_date(p.Context().Pool.SideChain.LastBlock.Timestamp) %}">{%s time_elapsed_short(p.Context().Pool.SideChain.LastBlock.Timestamp) %}</td>
</tr>
</table>
</div>

View file

@ -140,7 +140,7 @@ type MinerPage struct {
<td title="{%s utc_date(p.Miner.LastShareTimestamp) %}">{%s time_elapsed_short(p.Miner.LastShareTimestamp) %}</td>
<td>{%dul p.Positions.BlocksInWindow.Total() %} blocks (+{%dul p.Positions.UnclesInWindow.Total() %} uncles)</td>
{% code windowWeightRatio := float64(p.WindowWeight) / float64(p.Context().Pool.SideChain.Window.Weight.Lo) %}
<td>{%s si_units(windowWeightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
<td>{%s si_units(windowWeightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
<td>{%f.3 windowWeightRatio*100 %}%</td>
<td>{%s monero_to_xmr(p.ExpectedRewardPerWindow) %} XMR</td>
</tr>
@ -155,8 +155,8 @@ type MinerPage struct {
<tr>
<td>{% if p.LastPoolBlock != nil %}{%dul p.Miner.Shares[1].ShareCount %} blocks (+{%dul p.Miner.Shares[1].UncleCount %} uncles{% if p.Miner.Shares[0].ShareCount > 0 %}, +{%dul p.Miner.Shares[0].ShareCount %} orphans{% endif %}){% else %}No shares reported{% endif %}</td>
<td>{%dul p.Positions.Blocks.Total() %} blocks (+{%dul p.Positions.Uncles.Total() %} uncles)</td>
{% code weightRatio := (float64(p.Weight) / (float64(p.Context().Pool.SideChain.Window.Weight.Mul64(4).Lo) * (float64(p.Context().Pool.SideChain.MaxWindowSize) / float64(p.Context().Pool.SideChain.WindowSize)))) %}
<td>{%s si_units(weightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
{% code weightRatio := (float64(p.Weight) / (float64(p.Context().Pool.SideChain.Window.Weight.Mul64(4).Lo) * (float64(p.Context().Pool.SideChain.Consensus.ChainWindowSize) / float64(p.Context().Pool.SideChain.Window.Blocks)))) %}
<td>{%s si_units(weightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
<td>{%f.3 weightRatio*100 %}%</td>
<td>{%s monero_to_xmr(p.ExpectedRewardPerDay) %} XMR</td>
</tr>

View file

@ -85,7 +85,7 @@ type MinersPageMinerEntry struct {
<td style="vertical-align: middle">{%= software_info(m.SoftwareId, m.SoftwareVersion) %}</td>
{% code minerRatio := float64(m.Weight.Lo) / float64(p.WindowWeight.Lo) %}
<td style="vertical-align: middle">{%f.3 minerRatio*100 %}%</td>
<td style="vertical-align: middle">{%s si_units(minerRatio * (float64(p.Context().Pool.SideChain.Difficulty.Lo) / float64(p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
<td style="vertical-align: middle">{%s si_units(minerRatio * (float64(p.Context().Pool.SideChain.LastBlock.Difficulty) / float64(p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>
<td style="vertical-align: middle">{%dul m.Shares.Total() %} block(s) +{%dul m.Uncles.Total() %} uncle(s)</td>
<td>
<code class="mono small">{%s m.Shares.String() %}</code>

View file

@ -154,7 +154,7 @@
{% code var total uint64 %}
{%= TemplatePayouts(ctx, payouts, &total) %}
{% if sideBlock.EffectiveHeight > (ctx.Pool.SideChain.Height - uint64(ctx.Pool.SideChain.WindowSize)) %}
{% if sideBlock.EffectiveHeight > (ctx.Pool.SideChain.LastBlock.SideHeight - uint64(ctx.Pool.SideChain.Window.Blocks)) %}
<div class="center"><h3>Share is inside the PPLNS window. Any Monero blocks found during this period by any P2Pool miner will provide a direct payout.</h3></div>
{% else %}
<div class="center">Share is outside the PPLNS window. No more payouts for this share will be provided by other P2Pool miners.</div>

View file

@ -182,7 +182,7 @@ func main() {
time.Sleep(1)
continue
}
if t.SideChain.Id != types.ZeroHash {
if t.SideChain.LastBlock != nil {
basePoolInfo = t
break
}
@ -199,6 +199,15 @@ func main() {
var lastPoolInfo atomic.Pointer[cmdutils.PoolInfoResult]
ensureGetLastPoolInfo := func() *cmdutils.PoolInfoResult {
poolInfo := lastPoolInfo.Load()
if poolInfo == nil {
poolInfo = getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5)
lastPoolInfo.Store(poolInfo)
}
return poolInfo
}
baseContext := views.GlobalRequestContext{
DonationAddress: types.DonationAddress,
SiteTitle: os.Getenv("SITE_TITLE"),
@ -269,7 +278,7 @@ func main() {
poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5)
lastPoolInfo.Store(poolInfo)
secondsPerBlock := float64(poolInfo.MainChain.Difficulty.Lo) / float64(poolInfo.SideChain.Difficulty.Div64(consensus.TargetBlockTime).Lo)
secondsPerBlock := float64(poolInfo.MainChain.Difficulty.Lo) / float64(poolInfo.SideChain.LastBlock.Difficulty/consensus.TargetBlockTime)
blocksToFetch := uint64(math.Ceil((((time.Hour*24).Seconds()/secondsPerBlock)*2)/100) * 100)
@ -278,7 +287,7 @@ func main() {
blocksFound := cmdutils.NewPositionChart(30*4, consensus.ChainWindowSize*4)
tip := int64(poolInfo.SideChain.Height)
tip := int64(poolInfo.SideChain.LastBlock.SideHeight)
for _, b := range blocks {
blocksFound.Add(int(tip-int64(b.SideHeight)), 1)
}
@ -300,7 +309,15 @@ func main() {
})
serveMux.HandleFunc("/api", func(writer http.ResponseWriter, request *http.Request) {
renderPage(request, writer, &views.ApiPage{})
poolInfo := *ensureGetLastPoolInfo()
if len(poolInfo.SideChain.Effort.Last) > 5 {
poolInfo.SideChain.Effort.Last = poolInfo.SideChain.Effort.Last[:5]
}
p := &views.ApiPage{
PoolInfoExample: poolInfo,
}
renderPage(request, writer, p)
})
serveMux.HandleFunc("/calculate-share-time", func(writer http.ResponseWriter, request *http.Request) {
@ -333,7 +350,7 @@ func main() {
efforts = append(efforts, views.CalculateShareTimePageEffortEntry{
Effort: v,
Probability: (1 - math.Exp(-(v / 100))) * 100,
Between: (float64(poolInfo.SideChain.Difficulty.Lo) * (v / 100)) / currentHashRate,
Between: (float64(poolInfo.SideChain.LastBlock.Difficulty) * (v / 100)) / currentHashRate,
BetweenSolo: (float64(poolInfo.MainChain.Difficulty.Lo) * (v / 100)) / currentHashRate,
})
}
@ -641,9 +658,9 @@ func main() {
poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5)
lastPoolInfo.Store(poolInfo)
currentWindowSize := uint64(poolInfo.SideChain.WindowSize)
currentWindowSize := uint64(poolInfo.SideChain.Window.Blocks)
windowSize := currentWindowSize
if poolInfo.SideChain.Height <= windowSize {
if poolInfo.SideChain.LastBlock.SideHeight <= windowSize {
windowSize = consensus.ChainWindowSize
}
size := uint64(30)
@ -661,7 +678,7 @@ func main() {
miners := make(map[uint64]*views.MinersPageMinerEntry)
tipHeight := poolInfo.SideChain.Height
tipHeight := poolInfo.SideChain.LastBlock.SideHeight
wend := tipHeight - windowSize
tip := shares[0]
@ -850,9 +867,9 @@ func main() {
const totalWindows = 4
wsize := consensus.ChainWindowSize * totalWindows
currentWindowSize := uint64(poolInfo.SideChain.WindowSize)
currentWindowSize := uint64(poolInfo.SideChain.Window.Blocks)
tipHeight := poolInfo.SideChain.Height
tipHeight := poolInfo.SideChain.LastBlock.SideHeight
var shares, lastShares, lastOrphanedShares []*index.SideBlock
@ -910,8 +927,8 @@ func main() {
close(sweepC)
}
sharesInWindow := cmdutils.NewPositionChart(30, uint64(poolInfo.SideChain.WindowSize))
unclesInWindow := cmdutils.NewPositionChart(30, uint64(poolInfo.SideChain.WindowSize))
sharesInWindow := cmdutils.NewPositionChart(30, uint64(poolInfo.SideChain.Window.Blocks))
unclesInWindow := cmdutils.NewPositionChart(30, uint64(poolInfo.SideChain.Window.Blocks))
sharesFound := cmdutils.NewPositionChart(30*totalWindows, consensus.ChainWindowSize*totalWindows)
unclesFound := cmdutils.NewPositionChart(30*totalWindows, consensus.ChainWindowSize*totalWindows)
@ -992,7 +1009,7 @@ func main() {
}
if windowDiff.Cmp64(0) > 0 {
longWindowWeight := poolInfo.SideChain.Window.Weight.Mul64(4).Mul64(poolInfo.SideChain.Consensus.ChainWindowSize).Div64(uint64(poolInfo.SideChain.WindowSize))
longWindowWeight := poolInfo.SideChain.Window.Weight.Mul64(4).Mul64(poolInfo.SideChain.Consensus.ChainWindowSize).Div64(uint64(poolInfo.SideChain.Window.Blocks))
averageRewardPerBlock := longDiff.Mul64(poolInfo.MainChain.BaseReward).Div(longWindowWeight).Lo
minerPage.ExpectedRewardPerDay = longWindowWeight.Mul64(averageRewardPerBlock).Div(poolInfo.MainChain.NextDifficulty).Lo
@ -1000,8 +1017,8 @@ func main() {
minerPage.ExpectedRewardPerWindow = poolInfo.SideChain.Window.Weight.Mul64(expectedRewardNextBlock).Div(poolInfo.MainChain.NextDifficulty).Lo
}
totalWeight := poolInfo.SideChain.Window.Weight.Mul64(4).Mul64(poolInfo.SideChain.Consensus.ChainWindowSize).Div64(uint64(poolInfo.SideChain.WindowSize))
dailyHashRate := poolInfo.SideChain.Difficulty.Mul(longDiff).Div(totalWeight).Div64(consensus.TargetBlockTime).Lo
totalWeight := poolInfo.SideChain.Window.Weight.Mul64(4).Mul64(poolInfo.SideChain.Consensus.ChainWindowSize).Div64(uint64(poolInfo.SideChain.Window.Blocks))
dailyHashRate := types.DifficultyFrom64(poolInfo.SideChain.LastBlock.Difficulty).Mul(longDiff).Div(totalWeight).Div64(consensus.TargetBlockTime).Lo
hashRate := float64(0)
magnitude := float64(1000)

View file

@ -93,7 +93,9 @@ type Consensus struct {
MinimumDifficulty uint64 `json:"min_diff"`
ChainWindowSize uint64 `json:"pplns_window"`
UnclePenalty uint64 `json:"uncle_penalty"`
// HardFork optional hardfork information for p2pool
// If empty it will be filled with the default hardfork list to the corresponding NetworkType
HardForks []HardFork `json:"hard_forks,omitempty"`
hasher randomx.Hasher

View file

@ -1,7 +1,5 @@
package sidechain
import "log"
type HardFork struct {
Version uint8 `json:"version"`
Height uint64 `json:"height"`
@ -119,19 +117,21 @@ var p2poolStageNetHardForks = []HardFork{
{p2poolMainNetHardForks[len(p2poolMainNetHardForks)-1].Version, 0, 0, 0},
}
func NetworkMajorVersion(consensus *Consensus, height uint64) uint8 {
var hardForks []HardFork
func NetworkHardFork(consensus *Consensus) []HardFork {
switch consensus.NetworkType {
case NetworkMainnet:
hardForks = mainNetHardForks
return mainNetHardForks
case NetworkTestnet:
hardForks = testNetHardForks
return testNetHardForks
case NetworkStagenet:
hardForks = stageNetHardForks
return stageNetHardForks
default:
log.Panicf("invalid network type for determining share version")
return 0
panic("invalid network type for determining share version")
}
}
func NetworkMajorVersion(consensus *Consensus, height uint64) uint8 {
hardForks := NetworkHardFork(consensus)
if len(hardForks) == 0 {
return 0