Initial commit
This commit is contained in:
commit
98b25e0428
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
.idea
|
||||
p2pool.cache
|
||||
p2pool_peers.txt
|
||||
default.pgo.tmp
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2024 WeebDataHoarder, go-p2pool Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
892
api.go
Normal file
892
api.go
Normal file
|
@ -0,0 +1,892 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/p2p"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
cmdutils "git.gammaspectra.live/P2Pool/observer-cmd-utils/httputils"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getServerMux(instance *P2Pool) *mux.Router {
|
||||
|
||||
serveMux := mux.NewRouter()
|
||||
|
||||
archiveCache := instance.AddressableCache()
|
||||
|
||||
// ================================= Peering section =================================
|
||||
serveMux.HandleFunc("/server/peers", func(writer http.ResponseWriter, request *http.Request) {
|
||||
clients := instance.Server().Clients()
|
||||
result := make([]p2pooltypes.P2PoolServerPeerResult, 0, len(clients))
|
||||
for _, c := range clients {
|
||||
if !c.IsGood() {
|
||||
continue
|
||||
}
|
||||
result = append(result, p2pooltypes.P2PoolServerPeerResult{
|
||||
Incoming: c.IsIncomingConnection,
|
||||
Address: c.AddressPort.Addr().String(),
|
||||
SoftwareId: c.VersionInformation.SoftwareId.String(),
|
||||
SoftwareVersion: c.VersionInformation.SoftwareVersion.String(),
|
||||
ProtocolVersion: c.VersionInformation.Protocol.String(),
|
||||
ConnectionTime: uint64(c.ConnectionTime.Unix()),
|
||||
ListenPort: c.ListenPort.Load(),
|
||||
Latency: uint64(time.Duration(c.PingDuration.Load()).Milliseconds()),
|
||||
PeerId: c.PeerId.Load(),
|
||||
})
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/server/connection_check/{addrPort:.+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
addrPort, err := netip.ParseAddrPort(mux.Vars(request)["addrPort"])
|
||||
if err != nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: err.Error(),
|
||||
})
|
||||
_, _ = writer.Write(buf)
|
||||
return
|
||||
}
|
||||
|
||||
var client *p2p.Client
|
||||
var alreadyConnected bool
|
||||
isBanned, banEntry := instance.Server().IsBanned(addrPort.Addr())
|
||||
for _, c := range instance.Server().Clients() {
|
||||
if c.AddressPort.Addr().Compare(addrPort.Addr()) == 0 && uint16(c.ListenPort.Load()) == addrPort.Port() {
|
||||
client = c
|
||||
alreadyConnected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if client == nil {
|
||||
if client, err = instance.Server().DirectConnect(addrPort); err != nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: err.Error(),
|
||||
})
|
||||
_, _ = writer.Write(buf)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
buf, _ := utils.MarshalJSON(struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: "could not find client",
|
||||
})
|
||||
_, _ = writer.Write(buf)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
if client.Closed.Load() || (client.IsGood() && client.PingDuration.Load() > 0 && client.LastKnownTip.Load() != nil) {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
|
||||
banError := ""
|
||||
errorStr := ""
|
||||
if err := client.BanError(); err != nil {
|
||||
errorStr = err.Error()
|
||||
}
|
||||
|
||||
if banEntry != nil && banEntry.Error != nil {
|
||||
banError = banEntry.Error.Error()
|
||||
}
|
||||
|
||||
info := p2pooltypes.P2PoolConnectionCheckInformation[*sidechain.PoolBlock]{
|
||||
Address: client.AddressPort.Addr().Unmap().String(),
|
||||
Port: client.AddressPort.Port(),
|
||||
ListenPort: uint16(client.ListenPort.Load()),
|
||||
PeerId: client.PeerId.Load(),
|
||||
SoftwareId: client.VersionInformation.SoftwareId.String(),
|
||||
SoftwareVersion: client.VersionInformation.SoftwareVersion.String(),
|
||||
ProtocolVersion: client.VersionInformation.Protocol.String(),
|
||||
ConnectionTime: uint64(client.ConnectionTime.Unix()),
|
||||
Latency: uint64(time.Duration(client.PingDuration.Load()).Milliseconds()),
|
||||
Incoming: client.IsIncomingConnection,
|
||||
BroadcastTime: client.LastBroadcastTimestamp.Load(),
|
||||
BroadcastHeight: client.BroadcastMaxHeight.Load(),
|
||||
Tip: client.LastKnownTip.Load(),
|
||||
Closed: client.Closed.Load(),
|
||||
AlreadyConnected: alreadyConnected,
|
||||
HandshakeComplete: client.HandshakeComplete.Load(),
|
||||
LastActive: client.LastActiveTimestamp.Load(),
|
||||
Banned: isBanned,
|
||||
Error: errorStr,
|
||||
BanError: banError,
|
||||
}
|
||||
|
||||
if isBanned {
|
||||
client.Close()
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, info)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/server/peerlist", func(writer http.ResponseWriter, request *http.Request) {
|
||||
peers := instance.Server().PeerList()
|
||||
var result []byte
|
||||
for _, c := range peers {
|
||||
result = append(result, []byte(c.AddressPort.String()+"\n")...)
|
||||
}
|
||||
writer.Header().Set("Content-Type", "text/plain")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write(result)
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/server/status", func(writer http.ResponseWriter, request *http.Request) {
|
||||
result := p2pooltypes.P2PoolServerStatusResult{
|
||||
PeerId: instance.Server().PeerId(),
|
||||
SoftwareId: instance.Server().VersionInformation().SoftwareId.String(),
|
||||
SoftwareVersion: instance.Server().VersionInformation().SoftwareVersion.String(),
|
||||
ProtocolVersion: instance.Server().VersionInformation().Protocol.String(),
|
||||
ListenPort: instance.Server().ListenPort(),
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
})
|
||||
|
||||
// ================================= MainChain section =================================
|
||||
serveMux.HandleFunc("/mainchain/header_by_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
result := instance.GetMinimalBlockHeaderByHeight(height)
|
||||
if result == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/mainchain/difficulty_by_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
result := instance.GetDifficultyByHeight(height)
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/mainchain/header_by_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if id, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
result := instance.GetMinimalBlockHeaderByHash(id)
|
||||
if result == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/mainchain/miner_data", func(writer http.ResponseWriter, request *http.Request) {
|
||||
result := instance.GetMinerDataTip()
|
||||
if result == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/mainchain/tip", func(writer http.ResponseWriter, request *http.Request) {
|
||||
minerData := instance.GetMinerDataTip()
|
||||
if minerData == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
result := instance.GetMinimalBlockHeaderByHeight(minerData.Height - 1)
|
||||
if result == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
}
|
||||
})
|
||||
|
||||
// ================================= SideChain section =================================
|
||||
serveMux.HandleFunc("/sidechain/consensus", func(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, instance.Consensus())
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/sidechain/blocks_by_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
result := make([]p2pooltypes.P2PoolBinaryBlockResult, 0, 1)
|
||||
for _, b := range instance.SideChain().GetPoolBlocksByHeight(height) {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("[]"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/block_by_template_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if templateId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
var result p2pooltypes.P2PoolBinaryBlockResult
|
||||
if b := instance.SideChain().GetPoolBlockByTemplateId(templateId); b != nil {
|
||||
result.Version = int(b.ShareVersion())
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Error = err.Error()
|
||||
} else {
|
||||
result.Blob = blob
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("[]"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/state/short", func(writer http.ResponseWriter, request *http.Request) {
|
||||
tip := instance.SideChain().GetChainTip()
|
||||
if tip != nil {
|
||||
tipId := instance.SideChain().GetChainTip().SideTemplateId(instance.Consensus())
|
||||
chain, uncles := instance.SideChain().GetPoolBlocksFromTip(tipId)
|
||||
result := p2pooltypes.P2PoolSideChainStateResult{
|
||||
TipHeight: tip.Side.Height,
|
||||
TipId: tipId,
|
||||
Chain: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(chain)),
|
||||
Uncles: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(uncles)),
|
||||
}
|
||||
for _, b := range chain {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, b := range uncles {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/state/tip", func(writer http.ResponseWriter, request *http.Request) {
|
||||
tip := instance.SideChain().GetChainTip()
|
||||
if tip != nil {
|
||||
tipId := instance.SideChain().GetChainTip().SideTemplateId(instance.Consensus())
|
||||
chain, uncles := instance.SideChain().GetPoolBlocksFromTip(tipId)
|
||||
result := p2pooltypes.P2PoolSideChainStateResult{
|
||||
TipHeight: tip.Side.Height,
|
||||
TipId: tipId,
|
||||
Chain: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(chain)),
|
||||
Uncles: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(uncles)),
|
||||
}
|
||||
for _, b := range chain {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, b := range uncles {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/state/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if templateId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
tip := instance.SideChain().GetPoolBlockByTemplateId(templateId)
|
||||
if tip != nil {
|
||||
tipId := tip.SideTemplateId(instance.Consensus())
|
||||
chain, uncles := instance.SideChain().GetPoolBlocksFromTip(tipId)
|
||||
result := p2pooltypes.P2PoolSideChainStateResult{
|
||||
TipHeight: tip.Side.Height,
|
||||
TipId: tipId,
|
||||
Chain: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(chain)),
|
||||
Uncles: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, len(uncles)),
|
||||
}
|
||||
for _, b := range chain {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, b := range uncles {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/tip", func(writer http.ResponseWriter, request *http.Request) {
|
||||
var result p2pooltypes.P2PoolBinaryBlockResult
|
||||
if b := instance.SideChain().GetChainTip(); b != nil {
|
||||
result.Version = int(b.ShareVersion())
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Error = err.Error()
|
||||
} else {
|
||||
result.Blob = blob
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
})
|
||||
serveMux.HandleFunc("/sidechain/status", func(writer http.ResponseWriter, request *http.Request) {
|
||||
result := p2pooltypes.P2PoolSideChainStatusResult{
|
||||
Synchronized: instance.SideChain().PreCalcFinished(),
|
||||
Blocks: instance.SideChain().GetPoolBlockCount(),
|
||||
}
|
||||
tip := instance.SideChain().GetHighestKnownTip()
|
||||
|
||||
if tip != nil {
|
||||
result.Height = tip.Side.Height
|
||||
result.Id = tip.SideTemplateId(instance.Consensus())
|
||||
result.Difficulty = tip.Side.Difficulty
|
||||
result.CumulativeDifficulty = tip.Side.CumulativeDifficulty
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
})
|
||||
|
||||
preAllocatedSharesPool := sidechain.NewPreAllocatedSharesPool(instance.Consensus().ChainWindowSize * 2)
|
||||
|
||||
// ================================= Archive section =================================
|
||||
if archiveCache != nil {
|
||||
serveMux.HandleFunc("/archive/window_from_template_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if templateId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
tpls := archiveCache.LoadByTemplateId(templateId)
|
||||
if len(tpls) > 0 {
|
||||
tip := tpls[0]
|
||||
|
||||
mainDifficulty := instance.GetDifficultyByHeight(randomx.SeedHeight(tip.Main.Coinbase.GenHeight))
|
||||
|
||||
expectedBlocks := int(mainDifficulty.Mul64(2).Div(tip.Side.Difficulty).Lo) + 20
|
||||
if expectedBlocks < 100 {
|
||||
expectedBlocks = 100
|
||||
}
|
||||
if expectedBlocks > int(instance.Consensus().ChainWindowSize) {
|
||||
expectedBlocks = int(instance.Consensus().ChainWindowSize)
|
||||
}
|
||||
if tip.ShareVersion() == sidechain.ShareVersion_V1 {
|
||||
expectedBlocks = int(instance.Consensus().ChainWindowSize)
|
||||
}
|
||||
|
||||
var windowLock sync.RWMutex
|
||||
window := make(sidechain.UniquePoolBlockSlice, 0, expectedBlocks)
|
||||
window = append(window, tip)
|
||||
|
||||
getByTemplateId := func(id types.Hash) *sidechain.PoolBlock {
|
||||
if b := func() *sidechain.PoolBlock {
|
||||
windowLock.RLock()
|
||||
defer windowLock.RUnlock()
|
||||
if b := window.Get(id); b != nil {
|
||||
return b
|
||||
}
|
||||
return nil
|
||||
}(); b != nil {
|
||||
return b
|
||||
}
|
||||
windowLock.Lock()
|
||||
defer windowLock.Unlock()
|
||||
if bs := archiveCache.LoadByTemplateId(id); len(bs) > 0 {
|
||||
window = append(window, bs[0])
|
||||
return bs[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
preAllocatedShares := preAllocatedSharesPool.Get()
|
||||
defer preAllocatedSharesPool.Put(preAllocatedShares)
|
||||
|
||||
if _, err = tip.PreProcessBlock(instance.Consensus(), instance.SideChain().DerivationCache(), preAllocatedShares, instance.GetDifficultyByHeight, getByTemplateId); err == nil {
|
||||
result := p2pooltypes.P2PoolSideChainStateResult{
|
||||
TipHeight: tip.Side.Height,
|
||||
TipId: tip.SideTemplateId(instance.Consensus()),
|
||||
Chain: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, expectedBlocks),
|
||||
Uncles: make([]p2pooltypes.P2PoolBinaryBlockResult, 0, expectedBlocks/5),
|
||||
}
|
||||
|
||||
var topError error
|
||||
|
||||
topError = sidechain.IterateBlocksInPPLNSWindow(tip, instance.Consensus(), instance.GetDifficultyByHeight, getByTemplateId, nil, func(e sidechain.PoolBlockWindowSlot) {
|
||||
if _, err = e.Block.PreProcessBlock(instance.Consensus(), instance.SideChain().DerivationCache(), preAllocatedShares, instance.GetDifficultyByHeight, getByTemplateId); err != nil {
|
||||
topError = err
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(e.Block.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else if blob, err := e.Block.AppendBinaryFlags(make([]byte, 0, e.Block.BufferLength()), false, false); err != nil {
|
||||
topError = err
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(e.Block.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Chain = append(result.Chain, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(e.Block.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
|
||||
for _, u := range e.Uncles {
|
||||
if _, err = u.PreProcessBlock(instance.Consensus(), instance.SideChain().DerivationCache(), preAllocatedShares, instance.GetDifficultyByHeight, getByTemplateId); err != nil {
|
||||
topError = err
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(u.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else if blob, err := u.AppendBinaryFlags(make([]byte, 0, u.BufferLength()), false, false); err != nil {
|
||||
topError = err
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(u.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result.Uncles = append(result.Uncles, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(u.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if topError == nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
})
|
||||
serveMux.HandleFunc("/archive/store_alternate", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if buf, err := io.ReadAll(request.Body); err != nil {
|
||||
return
|
||||
} else {
|
||||
b := &sidechain.PoolBlock{}
|
||||
|
||||
if err = b.UnmarshalBinary(instance.Consensus(), instance.SideChain().DerivationCache(), buf); err != nil {
|
||||
return
|
||||
}
|
||||
if b.NeedsPreProcess() {
|
||||
return
|
||||
}
|
||||
templateId := b.SideTemplateId(instance.Consensus())
|
||||
if bytes.Compare(b.CoinbaseExtra(sidechain.SideTemplateId), templateId[:]) != 0 {
|
||||
return
|
||||
}
|
||||
if archiveCache.LoadByMainId(b.MainId()) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
existingBlock := archiveCache.LoadByTemplateId(templateId)
|
||||
|
||||
if len(existingBlock) == 0 {
|
||||
return
|
||||
}
|
||||
tempData, _ := existingBlock[0].MarshalBinary()
|
||||
|
||||
newBlock := &sidechain.PoolBlock{}
|
||||
if err = newBlock.UnmarshalBinary(instance.Consensus(), instance.SideChain().DerivationCache(), tempData); err != nil {
|
||||
return
|
||||
}
|
||||
//set extra nonce and nonce
|
||||
newBlock.Main.Coinbase.Extra[1] = b.Main.Coinbase.Extra[1]
|
||||
newBlock.Main.Nonce = b.Main.Nonce
|
||||
newBlock.Depth.Store(math.MaxUint64)
|
||||
|
||||
if !newBlock.IsProofHigherThanDifficulty(instance.Consensus().GetHasher(), func(height uint64) (hash types.Hash) {
|
||||
seedHeight := randomx.SeedHeight(height)
|
||||
if h := instance.GetMinimalBlockHeaderByHeight(seedHeight); h != nil {
|
||||
return h.Id
|
||||
} else {
|
||||
return types.ZeroHash
|
||||
}
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
||||
//finally store alternate
|
||||
archiveCache.Store(newBlock)
|
||||
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/archive/blocks_by_template_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if templateId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
result := make([]p2pooltypes.P2PoolBinaryBlockResult, 0, 1)
|
||||
for _, b := range archiveCache.LoadByTemplateId(templateId) {
|
||||
if err := archiveCache.ProcessBlock(b); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("[]"))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/archive/blocks_by_side_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
result := make([]p2pooltypes.P2PoolBinaryBlockResult, 0, 1)
|
||||
for _, b := range archiveCache.LoadBySideChainHeight(height) {
|
||||
if err := archiveCache.ProcessBlock(b); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("[]"))
|
||||
}
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/light_blocks_by_main_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
if blocks := archiveCache.LoadByMainChainHeight(height); len(blocks) > 0 {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
if blocks == nil {
|
||||
blocks = make(sidechain.UniquePoolBlockSlice, 0)
|
||||
}
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/light_blocks_by_side_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
if blocks := archiveCache.LoadBySideChainHeight(height); len(blocks) > 0 {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
if blocks == nil {
|
||||
blocks = make(sidechain.UniquePoolBlockSlice, 0)
|
||||
}
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/light_blocks_by_template_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if mainId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
if blocks := archiveCache.LoadByTemplateId(mainId); len(blocks) > 0 {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
if blocks == nil {
|
||||
blocks = make(sidechain.UniquePoolBlockSlice, 0)
|
||||
}
|
||||
_ = cmdutils.EncodeJson(request, writer, blocks)
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/light_block_by_main_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
params := request.URL.Query()
|
||||
|
||||
var templateIdHint types.Hash
|
||||
if params.Has("templateIdHint") {
|
||||
if h, err := types.HashFromString(params.Get("templateIdHint")); err == nil {
|
||||
templateIdHint = h
|
||||
}
|
||||
}
|
||||
|
||||
if mainId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
var b *sidechain.PoolBlock
|
||||
if templateIdHint != types.ZeroHash {
|
||||
// Fast lookup on sidechain
|
||||
if b = instance.SideChain().GetPoolBlockByTemplateId(templateIdHint); b == nil || b.MainId() != mainId {
|
||||
b = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
if b == nil {
|
||||
b = archiveCache.LoadByMainId(mainId)
|
||||
}
|
||||
|
||||
if b != nil {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, b)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = writer.Write([]byte("{}"))
|
||||
}
|
||||
})
|
||||
|
||||
serveMux.HandleFunc("/archive/block_by_main_id/{id:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
params := request.URL.Query()
|
||||
|
||||
var templateIdHint types.Hash
|
||||
if params.Has("templateIdHint") {
|
||||
if h, err := types.HashFromString(params.Get("templateIdHint")); err == nil {
|
||||
templateIdHint = h
|
||||
}
|
||||
}
|
||||
|
||||
if mainId, err := types.HashFromString(mux.Vars(request)["id"]); err == nil {
|
||||
var result p2pooltypes.P2PoolBinaryBlockResult
|
||||
var b *sidechain.PoolBlock
|
||||
if templateIdHint != types.ZeroHash {
|
||||
// Fast lookup on sidechain
|
||||
if b = instance.SideChain().GetPoolBlockByTemplateId(templateIdHint); b == nil || b.MainId() != mainId {
|
||||
b = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
if b == nil {
|
||||
b = archiveCache.LoadByMainId(mainId)
|
||||
}
|
||||
|
||||
if b != nil {
|
||||
result.Version = int(b.ShareVersion())
|
||||
if err := archiveCache.ProcessBlock(b); err != nil {
|
||||
result.Error = err.Error()
|
||||
} else {
|
||||
if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result.Error = err.Error()
|
||||
} else {
|
||||
result.Blob = blob
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte(""))
|
||||
}
|
||||
})
|
||||
serveMux.HandleFunc("/archive/blocks_by_main_height/{height:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if height, err := strconv.ParseUint(mux.Vars(request)["height"], 10, 0); err == nil {
|
||||
result := make([]p2pooltypes.P2PoolBinaryBlockResult, 0, 1)
|
||||
for _, b := range archiveCache.LoadByMainChainHeight(height) {
|
||||
if err := archiveCache.ProcessBlock(b); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else if blob, err := b.AppendBinaryFlags(make([]byte, 0, b.BufferLength()), false, false); err != nil {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Error: err.Error(),
|
||||
})
|
||||
} else {
|
||||
result = append(result, p2pooltypes.P2PoolBinaryBlockResult{
|
||||
Version: int(b.ShareVersion()),
|
||||
Blob: blob,
|
||||
})
|
||||
}
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_ = cmdutils.EncodeJson(request, writer, result)
|
||||
} else {
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("[]"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return serveMux
|
||||
}
|
BIN
default.pgo
Normal file
BIN
default.pgo
Normal file
Binary file not shown.
37
go.mod
Normal file
37
go.mod
Normal file
|
@ -0,0 +1,37 @@
|
|||
module git.gammaspectra.live/P2Pool/go-p2pool
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.0.1
|
||||
git.gammaspectra.live/P2Pool/observer-cache-archive v0.0.0-20240403175902-82f1b2cbe6d9
|
||||
git.gammaspectra.live/P2Pool/observer-cmd-utils v0.0.0-20240403175747-82cff4d3bd01
|
||||
github.com/gorilla/mux v1.8.1
|
||||
)
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33 // indirect
|
||||
git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523 // indirect
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7 // indirect
|
||||
git.gammaspectra.live/P2Pool/moneroutil v0.0.0-20230722215223-18ecc51ae61e // indirect
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71 // indirect
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.0.0-20230604092430-04fe7dc6439a // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/dolthub/swiss v0.2.1 // indirect
|
||||
github.com/floatdrop/lru v1.3.0 // indirect
|
||||
github.com/go-zeromq/goczmq/v4 v4.2.2 // indirect
|
||||
github.com/go-zeromq/zmq4 v0.16.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/holiman/uint256 v1.2.4 // indirect
|
||||
github.com/jxskiss/base62 v1.1.0 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/goccy/go-json => github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887
|
62
go.sum
Normal file
62
go.sum
Normal file
|
@ -0,0 +1,62 @@
|
|||
git.gammaspectra.live/P2Pool/consensus/v3 v3.0.1 h1:ibdNiP5uw6yfsnqld7yYTkcnlr+EL6ifEaTP6fUkh3s=
|
||||
git.gammaspectra.live/P2Pool/consensus/v3 v3.0.1/go.mod h1:wgvMwy3c+rztPojdRaRUH6Hly+DRHy6Kv9KRIos3FxU=
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33 h1:BPV7iIiv8T+X7gg9/JfNmEBoH4HXOkw8CR7FN6bBwB8=
|
||||
git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33/go.mod h1:336HUKX25mQ1qUtzkwV9Wrqi153tTgUOKcIhpYuF2ts=
|
||||
git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523 h1:oIJzm7kQyASS0xlJ79VSWRvvfXp2Qt7M05+E20o9gwE=
|
||||
git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523/go.mod h1:TAOAAV972JNDkCzyV5SkbYkKCRvcfhvvFa8LHH4Dg6g=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7 h1:bzHDuu1IgETKqPBOlIdCE2LaZIJ+ZpROSprNn+fnzd8=
|
||||
git.gammaspectra.live/P2Pool/go-randomx v0.0.0-20221027085532-f46adfce03a7/go.mod h1:3kT0v4AMwT/OdorfH2gRWPwoOrUX/LV03HEeBsaXG1c=
|
||||
git.gammaspectra.live/P2Pool/moneroutil v0.0.0-20230722215223-18ecc51ae61e h1:ropqS9niQR/ZKCUrlmWe+uDH0fLIyAnCIjkEjyTDgA8=
|
||||
git.gammaspectra.live/P2Pool/moneroutil v0.0.0-20230722215223-18ecc51ae61e/go.mod h1:Wn5QI7XIMHMpEu10pPspW9h3eGmXQPJwh/4/+Gi3G1U=
|
||||
git.gammaspectra.live/P2Pool/observer-cache-archive v0.0.0-20240403175902-82f1b2cbe6d9 h1:xxFQ7gJB4a0wTZv8InZADAF65e4WKAlBCt2JgmQq//w=
|
||||
git.gammaspectra.live/P2Pool/observer-cache-archive v0.0.0-20240403175902-82f1b2cbe6d9/go.mod h1:N3CQRDaVmZkTnRYbyNsmB9HGVcRnCSpAAKo6UNtxmFo=
|
||||
git.gammaspectra.live/P2Pool/observer-cmd-utils v0.0.0-20240403175747-82cff4d3bd01 h1:K4N+NZ0XdJQqeQwxgkgyISv7+/1Afy2RgMbyykJbARk=
|
||||
git.gammaspectra.live/P2Pool/observer-cmd-utils v0.0.0-20240403175747-82cff4d3bd01/go.mod h1:BPFII6OaDqWYVp5uX9SFsUMQA6gd63KsH3x+kFWG6is=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71 h1:MgeHHcF+GnCJBWMSzq8XAbc8p/UhNwFruEKCPPJ74YQ=
|
||||
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20230514082649-9c5f18cd5a71/go.mod h1:KQaYHIxGXNHNMQELC7xGLu8xouwvP/dN7iGk681BXmk=
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.0.0-20230604092430-04fe7dc6439a h1:c24MHv/z+aBYpYNsQHcJqmFuaYInGVixJZgDCXA/4bs=
|
||||
git.gammaspectra.live/P2Pool/sha3 v0.0.0-20230604092430-04fe7dc6439a/go.mod h1:6wZ0+whl+HZdcRve4R6Rq6jV1fmL1xCYO8Wty6lR008=
|
||||
github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887 h1:P01nqSM+0b6zlPasOFYsqnQSP2dTRVZkanAnY9q/Bcc=
|
||||
github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||
github.com/dolthub/swiss v0.2.1 h1:gs2osYs5SJkAaH5/ggVJqXQxRXtWshF6uE0lgR/Y3Gw=
|
||||
github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0=
|
||||
github.com/floatdrop/lru v1.3.0 h1:83abtaKjXcWrPmtzTAk2Ggq8DUKqI29YzrTrB8+vu0c=
|
||||
github.com/floatdrop/lru v1.3.0/go.mod h1:83zlXKA06Bm32JImNINCiTr0ldadvdAjUe5jSwIaw0s=
|
||||
github.com/go-zeromq/goczmq/v4 v4.2.2 h1:HAJN+i+3NW55ijMJJhk7oWxHKXgAuSBkoFfvr8bYj4U=
|
||||
github.com/go-zeromq/goczmq/v4 v4.2.2/go.mod h1:Sm/lxrfxP/Oxqs0tnHD6WAhwkWrx+S+1MRrKzcxoaYE=
|
||||
github.com/go-zeromq/zmq4 v0.16.0 h1:D6oIPWSdkY/4DJu4tBUmo28P3WRq4F4Ji4/iQ/fJHc0=
|
||||
github.com/go-zeromq/zmq4 v0.16.0/go.mod h1:8c3aXloJBRPba1AqWMJK4vypniM+yC+JKqi8KpRaDFc=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
|
||||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
|
||||
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
318
main.go
Normal file
318
main.go
Normal file
|
@ -0,0 +1,318 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/p2p"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
currentConsensus := sidechain.ConsensusDefault
|
||||
|
||||
//monerod related
|
||||
moneroHost := flag.String("host", "127.0.0.1", "IP address of your Monero node")
|
||||
moneroRpcPort := flag.Uint("rpc-port", 18081, "monerod RPC API port number")
|
||||
moneroZmqPort := flag.Uint("zmq-port", 18083, "monerod ZMQ pub port number")
|
||||
|
||||
// consensus related
|
||||
consensusConfigFile := flag.String("consensus-config", "", "Name of the p2pool consensus config file")
|
||||
useMiniSidechain := flag.Bool("mini", false, "Connect to p2pool-mini sidechain. Note that it will also change default p2p port.")
|
||||
|
||||
//p2p peering related
|
||||
p2pListen := flag.String("p2p", fmt.Sprintf("0.0.0.0:%d", currentConsensus.DefaultPort()), "IP:port for p2p server to listen on.")
|
||||
p2pExternalPort := flag.Uint64("p2p-external-port", 0, "Port number that your router uses for mapping to your local p2p port. Use it if you are behind a NAT and still want to accept incoming connections")
|
||||
outPeers := flag.Uint64("out-peers", 10, "Maximum number of outgoing connections for p2p server (any value between 10 and 450)")
|
||||
inPeers := flag.Uint64("in-peers", 10, "Maximum number of incoming connections for p2p server (any value between 10 and 450)")
|
||||
addPeers := flag.String("addpeers", "", "Comma-separated list of IP:port of other p2pool nodes to connect to")
|
||||
addSelf := flag.Bool("add-self-peer", false, "Adds itself to the peer list regularly, based on found local interfaces for IPv4/IPv6")
|
||||
peerList := flag.String("peer-list", "p2pool_peers.txt", "Either a path or an URL to obtain peer lists from. If it is a path, new peers will be saved to this path. Set to empty to disable")
|
||||
|
||||
//other settings
|
||||
lightMode := flag.Bool("light-mode", false, "Don't allocate RandomX dataset, saves 2GB of RAM")
|
||||
noDns := flag.Bool("no-dns", false, "Disable DNS queries, use only IP addresses to connect to peers (seed node DNS will be unavailable too)")
|
||||
memoryLimitInGiB := flag.Uint64("memory-limit", 0, "Memory limit for go managed sections in GiB, set 0 to disable")
|
||||
|
||||
apiBind := flag.String("api-bind", "", "Bind to this address to serve blocks, and other utility methods. If -archive is specified, serve archived blocks.")
|
||||
createArchive := flag.String("archive", "", "If specified, create an archive store of sidechain blocks on this path.")
|
||||
blockCache := flag.String("block-cache", "p2pool.cache", "Block cache for faster startups. Set to empty to disable")
|
||||
|
||||
//testing settings
|
||||
doDebug := flag.Bool("debug", false, "Log more details. Default false")
|
||||
ipv6Only := flag.Bool("ipv6-only", false, "Use only IPv6. Default false")
|
||||
|
||||
debugListen := flag.String("debug-listen", "", "Provide a bind address and port to expose a pprof HTTP API on it.")
|
||||
|
||||
//TODO extend verbosity to debug flag
|
||||
flag.Parse()
|
||||
|
||||
if *doDebug {
|
||||
utils.LogFile = true
|
||||
}
|
||||
|
||||
if buildInfo, _ := debug.ReadBuildInfo(); buildInfo != nil {
|
||||
utils.Logf("P2Pool", "Consensus Software %s %s (go version %s)", types.CurrentSoftwareId, types.CurrentSoftwareVersion, buildInfo.GoVersion)
|
||||
} else {
|
||||
utils.Logf("P2Pool", "Consensus Software %s %s (go version %s)", types.CurrentSoftwareId, types.CurrentSoftwareVersion, runtime.Version())
|
||||
}
|
||||
|
||||
client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort))
|
||||
|
||||
debug.SetTraceback("all")
|
||||
if *memoryLimitInGiB != 0 {
|
||||
debug.SetMemoryLimit(int64(*memoryLimitInGiB) * 1024 * 1024 * 1024)
|
||||
}
|
||||
|
||||
settings := make(map[string]string)
|
||||
settings["listen"] = *p2pListen
|
||||
|
||||
changeConsensus := func(newConsensus *sidechain.Consensus) {
|
||||
oldListen := netip.MustParseAddrPort(settings["listen"])
|
||||
// if default exists, change port to match
|
||||
if settings["listen"] == fmt.Sprintf("%s:%d", oldListen.Addr().String(), currentConsensus.DefaultPort()) {
|
||||
settings["listen"] = fmt.Sprintf("%s:%d", oldListen.Addr().String(), newConsensus.DefaultPort())
|
||||
}
|
||||
currentConsensus = newConsensus
|
||||
}
|
||||
|
||||
settings["rpc-url"] = fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)
|
||||
settings["zmq-url"] = fmt.Sprintf("tcp://%s:%d", *moneroHost, *moneroZmqPort)
|
||||
if *useMiniSidechain {
|
||||
changeConsensus(sidechain.ConsensusMini)
|
||||
}
|
||||
|
||||
if *consensusConfigFile != "" {
|
||||
consensusData, err := os.ReadFile(*consensusConfigFile)
|
||||
if err != nil {
|
||||
utils.Panic(err)
|
||||
}
|
||||
|
||||
if newConsensus, err := sidechain.NewConsensusFromJSON(consensusData); err != nil {
|
||||
utils.Panic(err)
|
||||
} else {
|
||||
changeConsensus(newConsensus)
|
||||
}
|
||||
}
|
||||
|
||||
settings["out-peers"] = strconv.FormatUint(*outPeers, 10)
|
||||
settings["in-peers"] = strconv.FormatUint(*inPeers, 10)
|
||||
settings["external-port"] = strconv.FormatUint(*p2pExternalPort, 10)
|
||||
|
||||
if *ipv6Only {
|
||||
settings["ipv6-only"] = "true"
|
||||
}
|
||||
|
||||
if *blockCache != "" {
|
||||
settings["cache"] = *blockCache
|
||||
}
|
||||
|
||||
if *createArchive != "" {
|
||||
settings["archive"] = *createArchive
|
||||
}
|
||||
|
||||
if !*lightMode {
|
||||
settings["full-mode"] = "true"
|
||||
}
|
||||
|
||||
if instance, err := NewP2Pool(currentConsensus, settings); err != nil {
|
||||
utils.Fatalf("Could not start p2pool: %s", err)
|
||||
} else {
|
||||
defer instance.Close(nil)
|
||||
|
||||
if *apiBind != "" {
|
||||
|
||||
serveMux := getServerMux(instance)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: *apiBind,
|
||||
ReadTimeout: time.Second * 2,
|
||||
Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != "GET" && request.Method != "HEAD" && request.Method != "POST" {
|
||||
writer.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Logf("API", "Handling %s %s", request.Method, request.URL.String())
|
||||
|
||||
serveMux.ServeHTTP(writer, request)
|
||||
}),
|
||||
}
|
||||
|
||||
go func() {
|
||||
//TODO: context/wait
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
utils.Panic(err)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
if *debugListen != "" {
|
||||
go func() {
|
||||
if err := http.ListenAndServe(*debugListen, nil); err != nil {
|
||||
utils.Panic(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var connectList []netip.AddrPort
|
||||
for _, peerAddr := range strings.Split(*addPeers, ",") {
|
||||
if peerAddr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
//TODO: dns resolution of hosts
|
||||
|
||||
if addrPort, err := netip.ParseAddrPort(peerAddr); err != nil {
|
||||
utils.Panic(err)
|
||||
} else {
|
||||
instance.Server().AddToPeerList(addrPort)
|
||||
connectList = append(connectList, addrPort)
|
||||
}
|
||||
}
|
||||
|
||||
if !*noDns && currentConsensus.SeedNode() != "" {
|
||||
utils.Logf("P2Pool", "Loading seed peers from %s", currentConsensus.SeedNode())
|
||||
records, _ := net.LookupTXT(currentConsensus.SeedNode())
|
||||
if len(records) > 0 {
|
||||
for _, r := range records {
|
||||
for _, e := range strings.Split(r, ",") {
|
||||
if seedNodeAddr, err := netip.ParseAddrPort(e); err == nil {
|
||||
instance.Server().AddToPeerList(seedNodeAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ips, _ := net.LookupIP(currentConsensus.SeedNode())
|
||||
for _, seedNodeIp := range ips {
|
||||
seedNodeAddr := netip.AddrPortFrom(netip.MustParseAddr(seedNodeIp.String()), currentConsensus.DefaultPort())
|
||||
instance.Server().AddToPeerList(seedNodeAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *peerList != "" {
|
||||
utils.Logf("P2Pool", "Loading peers from %s", *peerList)
|
||||
if len(*peerList) > 4 && (*peerList)[:4] == "http" {
|
||||
func() {
|
||||
r, err := http.DefaultClient.Get(*peerList)
|
||||
if err == nil {
|
||||
defer r.Body.Close()
|
||||
scanner := bufio.NewScanner(r.Body)
|
||||
for scanner.Scan() {
|
||||
if addrPort, err := netip.ParseAddrPort(strings.TrimSpace(scanner.Text())); err == nil {
|
||||
instance.Server().AddToPeerList(addrPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
func() {
|
||||
f, err := os.Open(*peerList)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
if addrPort, err := netip.ParseAddrPort(strings.TrimSpace(scanner.Text())); err == nil {
|
||||
instance.Server().AddToPeerList(addrPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
contents := make([]byte, 0, 4096)
|
||||
for range utils.ContextTick(instance.Context(), time.Minute*1) {
|
||||
contents = contents[:0]
|
||||
peerListEntries := instance.Server().PeerList()
|
||||
slices.SortFunc(peerListEntries, func(a, b *p2p.PeerListEntry) int {
|
||||
return a.AddressPort.Compare(b.AddressPort)
|
||||
})
|
||||
for _, addrPort := range peerListEntries {
|
||||
contents = append(contents, []byte(addrPort.AddressPort.String())...)
|
||||
contents = append(contents, '\n')
|
||||
}
|
||||
if err := os.WriteFile(*peerList, contents, 0644); err != nil {
|
||||
utils.Errorf("P2Pool", "error writing peer list %s: %s", *peerList, err.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
if *addSelf {
|
||||
if addrs, err := utils.GetOutboundIPv6(); err != nil {
|
||||
utils.Errorf("P2Pool", "Could not get interface ipv6 addresses: %s", err)
|
||||
} else {
|
||||
for _, addr := range addrs {
|
||||
if addr.Is6() {
|
||||
//use own port directly?
|
||||
instance.Server().AddToPeerList(netip.AddrPortFrom(addr, instance.Server().ListenPort()))
|
||||
} else {
|
||||
instance.Server().AddToPeerList(netip.AddrPortFrom(addr, instance.Server().ExternalListenPort()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
for !instance.Started() {
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, addrPort := range connectList {
|
||||
wg.Add(1)
|
||||
go func(addrPort netip.AddrPort) {
|
||||
defer wg.Done()
|
||||
if err := instance.Server().Connect(addrPort); err != nil {
|
||||
utils.Errorf("error connecting to initial peer %s: %s", addrPort.String(), err.Error())
|
||||
}
|
||||
}(addrPort)
|
||||
}
|
||||
wg.Wait()
|
||||
instance.Server().UpdateClientConnections()
|
||||
}()
|
||||
|
||||
sigHandler := make(chan os.Signal, 1)
|
||||
signal.Notify(sigHandler, syscall.SIGINT)
|
||||
go func() {
|
||||
for s := range sigHandler {
|
||||
if s == syscall.SIGKILL || s == syscall.SIGINT {
|
||||
instance.Close(nil)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := instance.Run(); err != nil {
|
||||
instance.Close(err)
|
||||
if instance.Started() {
|
||||
instance.WaitUntilClosed()
|
||||
}
|
||||
if closeError := instance.CloseError(); closeError != nil {
|
||||
utils.Panic(err)
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
627
p2pool.go
Normal file
627
p2pool.go
Normal file
|
@ -0,0 +1,627 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/block"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/client/zmq"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/cache"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/cache/legacy"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/mainchain"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/mempool"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/p2p"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
|
||||
p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/types"
|
||||
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
|
||||
archive "git.gammaspectra.live/P2Pool/observer-cache-archive"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EventListener struct {
|
||||
ListenerId uint64
|
||||
Tip func(tip *sidechain.PoolBlock)
|
||||
Broadcast func(b *sidechain.PoolBlock)
|
||||
Found func(data *sidechain.ChainMain, b *sidechain.PoolBlock)
|
||||
MainData func(data *sidechain.ChainMain)
|
||||
MinerData func(data *p2pooltypes.MinerData)
|
||||
MempoolData func(data mempool.Mempool)
|
||||
}
|
||||
|
||||
type P2Pool struct {
|
||||
consensus *sidechain.Consensus
|
||||
sidechain *sidechain.SideChain
|
||||
mainchain *mainchain.MainChain
|
||||
archive cache.AddressableCache
|
||||
cache cache.HeapCache
|
||||
server *p2p.Server
|
||||
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
|
||||
rpcClient *client.Client
|
||||
zmqClient *zmq.Client
|
||||
|
||||
recentSubmittedBlocks *utils.CircularBuffer[types.Hash]
|
||||
|
||||
listenersLock sync.RWMutex
|
||||
listeners []EventListener
|
||||
nextListenerId uint64
|
||||
|
||||
started atomic.Bool
|
||||
closeError error
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
// AddListener Registers listener to several events produced centrally.
|
||||
// Note that you should process events called as fast as possible, or spawn a new goroutine to not block
|
||||
func (p *P2Pool) AddListener(tip func(tip *sidechain.PoolBlock), broadcast func(b *sidechain.PoolBlock), found func(data *sidechain.ChainMain, b *sidechain.PoolBlock), mainData func(data *sidechain.ChainMain), minerData func(data *p2pooltypes.MinerData), mempoolData func(data mempool.Mempool)) (listenerId uint64) {
|
||||
p.listenersLock.Lock()
|
||||
p.listenersLock.Unlock()
|
||||
|
||||
listenerId = p.nextListenerId
|
||||
p.nextListenerId++
|
||||
p.listeners = append(p.listeners, EventListener{
|
||||
ListenerId: listenerId,
|
||||
Tip: tip,
|
||||
Broadcast: broadcast,
|
||||
Found: found,
|
||||
MainData: mainData,
|
||||
MinerData: minerData,
|
||||
MempoolData: mempoolData,
|
||||
})
|
||||
return listenerId
|
||||
}
|
||||
|
||||
func (p *P2Pool) RemoveListener(listenerId uint64) bool {
|
||||
p.listenersLock.Lock()
|
||||
p.listenersLock.Unlock()
|
||||
if i := slices.IndexFunc(p.listeners, func(listener EventListener) bool {
|
||||
return listener.ListenerId == listenerId
|
||||
}); i != -1 {
|
||||
p.listeners = slices.Delete(p.listeners, i, i+1)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *P2Pool) GetBlob(key []byte) (blob []byte, err error) {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
func (p *P2Pool) SetBlob(key, blob []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) RemoveBlob(key []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) AddressableCache() cache.AddressableCache {
|
||||
return p.archive
|
||||
}
|
||||
|
||||
func (p *P2Pool) Cache() cache.Cache {
|
||||
return p.cache
|
||||
}
|
||||
|
||||
func (p *P2Pool) CloseError() error {
|
||||
return p.closeError
|
||||
}
|
||||
|
||||
func (p *P2Pool) WaitUntilClosed() {
|
||||
<-p.closed
|
||||
}
|
||||
|
||||
func (p *P2Pool) Close(err error) {
|
||||
started := p.started.Swap(false)
|
||||
|
||||
if started {
|
||||
p.closeError = err
|
||||
}
|
||||
|
||||
p.ctxCancel()
|
||||
_ = p.zmqClient.Close()
|
||||
|
||||
if p.server != nil {
|
||||
p.server.Close()
|
||||
}
|
||||
if p.cache != nil {
|
||||
p.cache.Close()
|
||||
}
|
||||
if p.archive != nil {
|
||||
p.archive.Close()
|
||||
}
|
||||
|
||||
if started && err != nil {
|
||||
close(p.closed)
|
||||
utils.Panicf("[P2Pool] Closed due to error %s", err)
|
||||
} else if started {
|
||||
close(p.closed)
|
||||
utils.Logf("P2Pool", "Closed")
|
||||
} else if err != nil {
|
||||
utils.Logf("P2Pool", "Received extra error during closing %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewP2Pool(consensus *sidechain.Consensus, settings map[string]string) (*P2Pool, error) {
|
||||
|
||||
if settings["full-mode"] == "true" {
|
||||
if err := consensus.InitHasher(2, randomx.FlagSecure, randomx.FlagFullMemory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := consensus.InitHasher(2, randomx.FlagSecure); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pool := &P2Pool{
|
||||
consensus: consensus,
|
||||
recentSubmittedBlocks: utils.NewCircularBuffer[types.Hash](8),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
var err error
|
||||
|
||||
pool.ctx, pool.ctxCancel = context.WithCancel(context.Background())
|
||||
|
||||
listenAddress := fmt.Sprintf("0.0.0.0:%d", pool.Consensus().DefaultPort())
|
||||
|
||||
if pool.rpcClient, err = client.NewClient(settings["rpc-url"]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pool.zmqClient = zmq.NewClient(settings["zmq-url"], zmq.TopicFullChainMain, zmq.TopicFullMinerData, zmq.TopicMinimalTxPoolAdd)
|
||||
|
||||
pool.sidechain = sidechain.NewSideChain(pool)
|
||||
|
||||
pool.mainchain = mainchain.NewMainChain(pool.sidechain, pool)
|
||||
|
||||
if pool.mainchain == nil {
|
||||
return nil, errors.New("could not create MainChain")
|
||||
}
|
||||
|
||||
if archivePath, ok := settings["archive"]; ok {
|
||||
if pool.archive, err = archive.NewCache(archivePath, pool.consensus, pool.GetDifficultyByHeight); err != nil {
|
||||
return nil, fmt.Errorf("could not create cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cachePath, ok := settings["cache"]; ok {
|
||||
if pool.cache, err = legacy.NewCache(consensus, cachePath); err != nil {
|
||||
return nil, fmt.Errorf("could not create cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if addr, ok := settings["listen"]; ok {
|
||||
listenAddress = addr
|
||||
}
|
||||
|
||||
maxOutgoingPeers := uint64(10)
|
||||
if outgoingPeers, ok := settings["out-peers"]; ok {
|
||||
maxOutgoingPeers, _ = strconv.ParseUint(outgoingPeers, 10, 0)
|
||||
}
|
||||
|
||||
maxIncomingPeers := uint64(450)
|
||||
if incomingPeers, ok := settings["in-peers"]; ok {
|
||||
maxIncomingPeers, _ = strconv.ParseUint(incomingPeers, 10, 0)
|
||||
}
|
||||
|
||||
externalListenPort := uint64(0)
|
||||
if externalPort, ok := settings["external-port"]; ok {
|
||||
externalListenPort, _ = strconv.ParseUint(externalPort, 10, 0)
|
||||
}
|
||||
|
||||
useIPv4, useIPv6 := true, true
|
||||
if b, ok := settings["ipv6-only"]; ok && b == "true" {
|
||||
useIPv4 = false
|
||||
useIPv6 = true
|
||||
}
|
||||
|
||||
if pool.server, err = p2p.NewServer(pool, listenAddress, uint16(externalListenPort), uint32(maxOutgoingPeers), uint32(maxIncomingPeers), useIPv4, useIPv6, pool.ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) GetChainMainByHash(hash types.Hash) *sidechain.ChainMain {
|
||||
return p.mainchain.GetChainMainByHash(hash)
|
||||
}
|
||||
|
||||
func (p *P2Pool) GetChainMainByHeight(height uint64) *sidechain.ChainMain {
|
||||
return p.mainchain.GetChainMainByHeight(height)
|
||||
}
|
||||
|
||||
func (p *P2Pool) GetChainMainTip() *sidechain.ChainMain {
|
||||
return p.mainchain.GetChainMainTip()
|
||||
}
|
||||
|
||||
func (p *P2Pool) GetMinerDataTip() *p2pooltypes.MinerData {
|
||||
return p.mainchain.GetMinerDataTip()
|
||||
}
|
||||
|
||||
// GetMinimalBlockHeaderByHeight Only Id / Height / Timestamp are assured
|
||||
func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header {
|
||||
lowerThanTip := height <= p.mainchain.GetChainMainTip().Height
|
||||
if chainMain := p.mainchain.GetChainMainByHeight(height); chainMain != nil && chainMain.Id != types.ZeroHash {
|
||||
prev := p.mainchain.GetChainMainByHeight(height - 1)
|
||||
if prev != nil {
|
||||
return &block.Header{
|
||||
Timestamp: chainMain.Timestamp,
|
||||
Height: chainMain.Height,
|
||||
Reward: chainMain.Reward,
|
||||
Difficulty: chainMain.Difficulty,
|
||||
Id: chainMain.Id,
|
||||
PreviousId: prev.Id,
|
||||
}
|
||||
} else {
|
||||
return &block.Header{
|
||||
Timestamp: chainMain.Timestamp,
|
||||
Height: chainMain.Height,
|
||||
Reward: chainMain.Reward,
|
||||
Difficulty: chainMain.Difficulty,
|
||||
Id: chainMain.Id,
|
||||
}
|
||||
}
|
||||
} else if lowerThanTip {
|
||||
if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil {
|
||||
return nil
|
||||
} else {
|
||||
prevHash, _ := types.HashFromString(header.BlockHeader.PrevHash)
|
||||
h, _ := types.HashFromString(header.BlockHeader.Hash)
|
||||
blockHeader := &block.Header{
|
||||
MajorVersion: uint8(header.BlockHeader.MajorVersion),
|
||||
MinorVersion: uint8(header.BlockHeader.MinorVersion),
|
||||
Timestamp: uint64(header.BlockHeader.Timestamp),
|
||||
PreviousId: prevHash,
|
||||
Height: header.BlockHeader.Height,
|
||||
Nonce: uint32(header.BlockHeader.Nonce),
|
||||
Reward: header.BlockHeader.Reward,
|
||||
Id: h,
|
||||
Difficulty: types.DifficultyFrom64(header.BlockHeader.Difficulty),
|
||||
}
|
||||
return blockHeader
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (p *P2Pool) GetDifficultyByHeight(height uint64) types.Difficulty {
|
||||
lowerThanTip := height <= p.mainchain.GetChainMainTip().Height
|
||||
if chainMain := p.mainchain.GetChainMainByHeight(height); chainMain != nil && chainMain.Difficulty != types.ZeroDifficulty {
|
||||
return chainMain.Difficulty
|
||||
} else if lowerThanTip {
|
||||
//TODO cache
|
||||
if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil {
|
||||
return types.ZeroDifficulty
|
||||
} else {
|
||||
prevHash, _ := types.HashFromString(header.BlockHeader.PrevHash)
|
||||
h, _ := types.HashFromString(header.BlockHeader.Hash)
|
||||
blockHeader := &block.Header{
|
||||
MajorVersion: uint8(header.BlockHeader.MajorVersion),
|
||||
MinorVersion: uint8(header.BlockHeader.MinorVersion),
|
||||
Timestamp: uint64(header.BlockHeader.Timestamp),
|
||||
PreviousId: prevHash,
|
||||
Height: header.BlockHeader.Height,
|
||||
Nonce: uint32(header.BlockHeader.Nonce),
|
||||
Reward: header.BlockHeader.Reward,
|
||||
Id: h,
|
||||
Difficulty: types.DifficultyFrom64(header.BlockHeader.Difficulty),
|
||||
}
|
||||
return blockHeader.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
return types.ZeroDifficulty
|
||||
}
|
||||
|
||||
// GetMinimalBlockHeaderByHash Only Id / Height / Timestamp are assured
|
||||
func (p *P2Pool) GetMinimalBlockHeaderByHash(hash types.Hash) *block.Header {
|
||||
if chainMain := p.mainchain.GetChainMainByHash(hash); chainMain != nil && chainMain.Id != types.ZeroHash {
|
||||
return &block.Header{
|
||||
Timestamp: chainMain.Timestamp,
|
||||
Height: chainMain.Height,
|
||||
Reward: chainMain.Reward,
|
||||
Difficulty: chainMain.Difficulty,
|
||||
Id: chainMain.Id,
|
||||
}
|
||||
} else {
|
||||
if header, err := p.ClientRPC().GetBlockHeaderByHash(hash, p.ctx); err != nil || header == nil {
|
||||
return nil
|
||||
} else {
|
||||
prevHash, _ := types.HashFromString(header.PrevHash)
|
||||
h, _ := types.HashFromString(header.Hash)
|
||||
blockHeader := &block.Header{
|
||||
MajorVersion: uint8(header.MajorVersion),
|
||||
MinorVersion: uint8(header.MinorVersion),
|
||||
Timestamp: uint64(header.Timestamp),
|
||||
PreviousId: prevHash,
|
||||
Height: header.Height,
|
||||
Nonce: uint32(header.Nonce),
|
||||
Reward: header.Reward,
|
||||
Id: h,
|
||||
Difficulty: types.DifficultyFrom64(header.Difficulty),
|
||||
}
|
||||
return blockHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) ClientRPC() *client.Client {
|
||||
return p.rpcClient
|
||||
}
|
||||
|
||||
func (p *P2Pool) ClientZMQ() *zmq.Client {
|
||||
return p.zmqClient
|
||||
}
|
||||
|
||||
func (p *P2Pool) Context() context.Context {
|
||||
return p.ctx
|
||||
}
|
||||
|
||||
func (p *P2Pool) SideChain() *sidechain.SideChain {
|
||||
return p.sidechain
|
||||
}
|
||||
|
||||
func (p *P2Pool) MainChain() *mainchain.MainChain {
|
||||
return p.mainchain
|
||||
}
|
||||
|
||||
func (p *P2Pool) Server() *p2p.Server {
|
||||
return p.server
|
||||
}
|
||||
|
||||
func (p *P2Pool) Run() (err error) {
|
||||
|
||||
if err = p.getInfo(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = p.getVersion(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = p.getInfo(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.getMinerData(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
//TODO: redo listen
|
||||
if err := p.mainchain.Listen(); err != nil {
|
||||
p.Close(err)
|
||||
}
|
||||
}()
|
||||
|
||||
//TODO: move peer list loading here
|
||||
|
||||
if p.cache != nil {
|
||||
p.cache.LoadAll(p.Server())
|
||||
}
|
||||
|
||||
p.started.Store(true)
|
||||
|
||||
if err = p.Server().Listen(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) getInfo() error {
|
||||
if info, err := p.ClientRPC().GetInfo(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if info.BusySyncing {
|
||||
utils.Logf("P2Pool", "monerod is busy syncing, trying again in 5 seconds")
|
||||
time.Sleep(time.Second * 5)
|
||||
return p.getInfo()
|
||||
} else if !info.Synchronized {
|
||||
utils.Logf("P2Pool", "monerod is not synchronized, trying again in 5 seconds")
|
||||
time.Sleep(time.Second * 5)
|
||||
return p.getInfo()
|
||||
}
|
||||
|
||||
networkType := sidechain.NetworkInvalid
|
||||
if info.Mainnet {
|
||||
networkType = sidechain.NetworkMainnet
|
||||
} else if info.Testnet {
|
||||
networkType = sidechain.NetworkTestnet
|
||||
} else if info.Stagenet {
|
||||
networkType = sidechain.NetworkStagenet
|
||||
}
|
||||
|
||||
if p.sidechain.Consensus().NetworkType != networkType {
|
||||
return fmt.Errorf("monerod is on %d, but you're mining to a %d sidechain", networkType, p.sidechain.Consensus().NetworkType)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) getVersion() error {
|
||||
if version, err := p.ClientRPC().GetVersion(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if version.Version < monero.RequiredMoneroVersion {
|
||||
return fmt.Errorf("monerod RPC v%d.%d is incompatible, update to RPC >= v%d.%d (Monero %s or newer)", version.Version>>16, version.Version&0xffff, monero.RequiredMajor, monero.RequiredMinor, monero.RequiredMoneroString)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P2Pool) getMinerData() error {
|
||||
if minerData, err := p.ClientRPC().GetMinerData(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
prevId, _ := types.HashFromString(minerData.PrevId)
|
||||
seedHash, _ := types.HashFromString(minerData.SeedHash)
|
||||
diff, _ := types.DifficultyFromString(minerData.Difficulty)
|
||||
|
||||
data := &p2pooltypes.MinerData{
|
||||
MajorVersion: minerData.MajorVersion,
|
||||
Height: minerData.Height,
|
||||
PrevId: prevId,
|
||||
SeedHash: seedHash,
|
||||
Difficulty: diff,
|
||||
MedianWeight: minerData.MedianWeight,
|
||||
AlreadyGeneratedCoins: minerData.AlreadyGeneratedCoins,
|
||||
MedianTimestamp: minerData.MedianTimestamp,
|
||||
TimeReceived: time.Now(),
|
||||
}
|
||||
data.TxBacklog = make(mempool.Mempool, len(minerData.TxBacklog))
|
||||
for i, e := range minerData.TxBacklog {
|
||||
txId, _ := types.HashFromString(e.Id)
|
||||
|
||||
data.TxBacklog[i] = &mempool.MempoolEntry{
|
||||
Id: txId,
|
||||
BlobSize: e.BlobSize,
|
||||
Weight: e.Weight,
|
||||
Fee: e.Fee,
|
||||
}
|
||||
}
|
||||
p.mainchain.HandleMinerData(data)
|
||||
|
||||
return p.mainchain.DownloadBlockHeaders(minerData.Height)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) Consensus() *sidechain.Consensus {
|
||||
return p.consensus
|
||||
}
|
||||
|
||||
func (p *P2Pool) UpdateMainData(data *sidechain.ChainMain) {
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].MainData != nil {
|
||||
p.listeners[i].MainData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) UpdateMempoolData(data mempool.Mempool) {
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].MempoolData != nil {
|
||||
p.listeners[i].MempoolData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) UpdateMinerData(data *p2pooltypes.MinerData) {
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].MinerData != nil {
|
||||
p.listeners[i].MinerData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) UpdateBlockFound(data *sidechain.ChainMain, block *sidechain.PoolBlock) {
|
||||
utils.Logf("P2Pool", "BLOCK FOUND: main chain block at height %d, id %s was mined by this p2pool", data.Height, data.Id)
|
||||
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].Found != nil {
|
||||
p.listeners[i].Found(data, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) SubmitBlock(b *block.Block) {
|
||||
|
||||
go func() {
|
||||
//do not submit multiple monero blocks to monerod
|
||||
if !p.recentSubmittedBlocks.PushUnique(b.Id()) {
|
||||
return
|
||||
}
|
||||
|
||||
if blob, err := b.MarshalBinary(); err == nil {
|
||||
var templateId types.Hash
|
||||
var extraNonce uint32
|
||||
if t := b.Coinbase.Extra.GetTag(transaction.TxExtraTagMergeMining); t != nil {
|
||||
templateId = types.HashFromBytes(t.Data)
|
||||
}
|
||||
if t := b.Coinbase.Extra.GetTag(transaction.TxExtraTagNonce); t != nil {
|
||||
extraNonce = binary.LittleEndian.Uint32(t.Data)
|
||||
}
|
||||
utils.Logf("P2Pool", "submit_block: height = %d, template id = %s, nonce = %d, extra_nonce = %d, blob = %d bytes", b.Coinbase.GenHeight, templateId.String(), b.Nonce, extraNonce, len(blob))
|
||||
|
||||
if result, err := p.ClientRPC().SubmitBlock(blob); err != nil {
|
||||
utils.Logf("P2Pool", "submit_block: daemon returned error: %s, height = %d, template id = %s, nonce = %d, extra_nonce = %d", err, b.Coinbase.GenHeight, templateId.String(), b.Nonce, extraNonce)
|
||||
} else {
|
||||
if result.Status == "OK" {
|
||||
utils.Logf("P2Pool", "submit_block: BLOCK ACCEPTED at height = %d, template id = %s", b.Coinbase.GenHeight, templateId.String())
|
||||
} else {
|
||||
utils.Logf("P2Pool", "submit_block: daemon sent unrecognizable reply: %s, height = %d, template id = %s, nonce = %d, extra_nonce = %d", result.Status, b.Coinbase.GenHeight, templateId.String(), b.Nonce, extraNonce)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *P2Pool) Store(block *sidechain.PoolBlock) {
|
||||
if p.cache != nil {
|
||||
p.cache.Store(block)
|
||||
}
|
||||
if p.archive != nil {
|
||||
p.archive.Store(block)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) ClearCachedBlocks() {
|
||||
p.server.ClearCachedBlocks()
|
||||
}
|
||||
|
||||
func (p *P2Pool) Started() bool {
|
||||
return p.started.Load()
|
||||
}
|
||||
|
||||
func (p *P2Pool) UpdateTip(tip *sidechain.PoolBlock) {
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].Tip != nil {
|
||||
p.listeners[i].Tip(tip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *P2Pool) Broadcast(block *sidechain.PoolBlock) {
|
||||
minerData := p.GetMinerDataTip()
|
||||
if (block.Main.Coinbase.GenHeight)+2 < minerData.Height {
|
||||
utils.Logf("P2Pool", "Trying to broadcast a stale block %s (mainchain height %d, current height is %d)", block.SideTemplateId(p.consensus), block.Main.Coinbase.GenHeight, minerData.Height)
|
||||
return
|
||||
}
|
||||
|
||||
if block.Main.Coinbase.GenHeight > (minerData.Height + 2) {
|
||||
utils.Logf("P2Pool", "Trying to broadcast a block %s ahead on mainchain (mainchain height %d, current height is %d)", block.SideTemplateId(p.consensus), block.Main.Coinbase.GenHeight, minerData.Height)
|
||||
return
|
||||
}
|
||||
|
||||
p.server.Broadcast(block)
|
||||
|
||||
p.listenersLock.RLock()
|
||||
defer p.listenersLock.RUnlock()
|
||||
for i := range p.listeners {
|
||||
if p.listeners[i].Broadcast != nil {
|
||||
p.listeners[i].Broadcast(block)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue