diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index db3875e..0000000 --- a/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.git/ -.env -.env.example -.gitignore -testdata/ -default.pgo.tmp -docker/nginx -docker/postgres -docker-compose.override.yml -p2pool.cache -p2pool_peers.txt -README.md \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index b6c7cf0..0000000 --- a/.env.example +++ /dev/null @@ -1,33 +0,0 @@ -#GOPROXY=direct - -# Monerod p2pool compatible node with open RPC and ZMQ ports -MONEROD_HOST=p2pmd.xmrvsbeast.com -MONEROD_RPC_PORT=18081 -MONEROD_ZMQ_PORT=18083 - -# Tor private key and public address. You can generate vanity ones via https://github.com/cathugger/mkp224o -TOR_SERVICE_KEY= -TOR_SERVICE_ADDRESS= -NET_SERVICE_ADDRESS= - -# Other APIs to lookup for transaction sweep besides local, for example, https://p2pool.observer. Separate by commas. -TRANSACTION_LOOKUP_OTHER= - -SITE_TITLE= -# This port will be presented externally -SITE_PORT=8189 - -# If specified, these IRC details will be shown on site. If the network is supported a Matrix link will be provided. -#SITE_IRC_URL=ircs://irc.libera.chat:6697/#p2pool-observer/monero.social - -# Change for mini if you want public reachable address -P2POOL_PORT=37889 -# This port is what should be reachable externally, and will be reachable via Tor as well -P2POOL_EXTERNAL_PORT=37889 -P2POOL_OUT_PEERS=24 -P2POOL_IN_PEERS=64 - -# Extra arg examples -#P2POOL_EXTRA_ARGS=-light-mode -#P2POOL_EXTRA_ARGS=-mini -#P2POOL_EXTRA_ARGS=-consensus-config /data/consensus.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index bd981e3..ae53916 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .idea p2pool.cache p2pool_peers.txt -docker-compose.override.yml -.env default.pgo.tmp \ No newline at end of file diff --git a/README.md b/README.md index 8bc16db..c742e3c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Other general tools to work with Monero cryptography are also included. ## Reporting issues You can give feedback or report / discuss issues on: -* [The issue tracker on git.gammaspectra.live/P2Pool/p2pool-observer](https://git.gammaspectra.live/P2Pool/p2pool-observer/issues?state=open) +* [The issue tracker on git.gammaspectra.live/P2Pool/consensus](https://git.gammaspectra.live/P2Pool/consensus/issues?state=open) * Via IRC on [#p2pool-observer@libera.chat](ircs://irc.libera.chat/#p2pool-observer), or via [Matrix](https://matrix.to/#/#p2pool-observer:monero.social) * Any of the relevant rooms for the specific observer instances listed below. @@ -42,7 +42,7 @@ If desired each tool can be run individually, but that is left to the user to co ### Initial setup ```bash -$ git clone https://git.gammaspectra.live/P2Pool/p2pool-observer.git test-instance +$ git clone https://git.gammaspectra.live/P2Pool/consensus.git test-instance $ cd test-instance $ cp .env.example .env ``` @@ -67,7 +67,7 @@ It is recommended to run `docker system prune` regularly or after update to clea When a new instance starts with previously imported archives you might want to backfill sweep transactions. For new instances this is not necessary, and you can also skip this step and just rely on future data. ```bash $ docker-compose exec --workdir /usr/src/p2pool daemon \ -go run -v git.gammaspectra.live/P2Pool/p2pool-observer/cmd/scansweeps \ +go run -v git.gammaspectra.live/P2Pool/consensus/cmd/scansweeps \ -host MONEROD_HOST -rpc-port MONEROD_RPC_PORT \ -api-host "http://p2pool:3131" \ -db="host=db port=5432 dbname=p2pool user=p2pool password=p2pool sslmode=disable" diff --git a/cmd/api/api.go b/cmd/api/api.go deleted file mode 100644 index 0da2cb9..0000000 --- a/cmd/api/api.go +++ /dev/null @@ -1,2128 +0,0 @@ -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - p2poolapi "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/gorilla/mux" - "io" - "math" - "net/http" - _ "net/http/pprof" - "net/netip" - "net/url" - "os" - "slices" - "strconv" - "strings" - "sync/atomic" - "time" - "unicode" -) - -func main() { - torHost := os.Getenv("TOR_SERVICE_ADDRESS") - 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") - dbString := flag.String("db", "", "") - p2poolApiHost := flag.String("api-host", "", "Host URL for p2pool go observer consensus") - debugListen := flag.String("debug-listen", "", "Provide a bind address and port to expose a pprof HTTP API on it.") - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - p2api := p2poolapi.NewP2PoolApi(*p2poolApiHost) - - if err := p2api.WaitSyncStart(); err != nil { - utils.Panic(err) - } - consensus := p2api.Consensus() - - indexDb, err := index.OpenIndex(*dbString, consensus, p2api.DifficultyByHeight, p2api.SeedByHeight, p2api.ByTemplateId) - if err != nil { - utils.Panic(err) - } - defer indexDb.Close() - - var lastPoolInfo atomic.Pointer[cmdutils.PoolInfoResult] - - fillMainCoinbaseOutputs := func(params url.Values, outputs index.MainCoinbaseOutputs) (result index.MainCoinbaseOutputs) { - result = make(index.MainCoinbaseOutputs, 0, len(outputs)) - fillMiner := !params.Has("noMiner") - for _, output := range outputs { - if fillMiner { - miner := indexDb.GetMiner(output.Miner) - output.MinerAddress = miner.Address() - output.MinerAlias = miner.Alias() - } - result = append(result, output) - } - return result - } - - fillFoundBlockResult := func(fillMiner bool, foundBlock *index.FoundBlock) (result *index.FoundBlock) { - if fillMiner { - miner := indexDb.GetMiner(foundBlock.Miner) - foundBlock.MinerAddress = miner.Address() - foundBlock.MinerAlias = miner.Alias() - } - return foundBlock - } - - 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 { - index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId), func(_ int, u *index.SideBlock) (stop bool) { - sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{ - TemplateId: u.TemplateId, - Miner: u.Miner, - SideHeight: u.SideHeight, - Difficulty: u.Difficulty, - }) - return false - }) - } - return sideBlock - } - - getBlockWithUncles := func(id types.Hash) (block *sidechain.PoolBlock, uncles []*sidechain.PoolBlock) { - block = p2api.ByTemplateId(id) - if block == nil { - return nil, nil - } - for _, uncleId := range block.Side.Uncles { - if u := p2api.ByTemplateId(uncleId); u == nil { - return nil, nil - } else { - uncles = append(uncles, u) - } - } - return block, uncles - } - _ = getBlockWithUncles - - serveMux := mux.NewRouter() - - getPoolInfo := func() { - - oldPoolInfo := lastPoolInfo.Load() - - tip := indexDb.GetSideBlockTip() - - if oldPoolInfo != nil && oldPoolInfo.SideChain.LastBlock.TemplateId == tip.TemplateId { - //no changes! - - //this race is ok - oldPoolInfo.SideChain.SecondsSinceLastBlock = max(0, time.Now().Unix()-int64(tip.Timestamp)) - return - } - - var bottom *index.SideBlock - var bottomUncles []*index.SideBlock - blockCount := 0 - uncleCount := 0 - - miners := make(map[uint64]uint64) - - versions := make([]cmdutils.SideChainVersionEntry, 0) - - var pplnsWeight types.Difficulty - - if err := index.IterateSideBlocksInPPLNSWindowFast(indexDb, tip, indexDb.GetDifficultyByHeight, func(b *index.SideBlock, weight types.Difficulty) { - miners[indexDb.GetMiner(b.Miner).Id()]++ - pplnsWeight = pplnsWeight.Add(weight) - - if i := slices.IndexFunc(versions, func(entry cmdutils.SideChainVersionEntry) bool { - return entry.SoftwareId == b.SoftwareId && entry.SoftwareVersion == b.SoftwareVersion - }); i != -1 { - versions[i].Weight = versions[i].Weight.Add(weight) - versions[i].Count++ - } else { - versions = append(versions, cmdutils.SideChainVersionEntry{ - Weight: weight, - Count: 1, - SoftwareId: b.SoftwareId, - SoftwareVersion: b.SoftwareVersion, - SoftwareString: fmt.Sprintf("%s %s", b.SoftwareId, b.SoftwareVersion), - }) - } - }, func(slot index.SideBlockWindowSlot) { - blockCount++ - bottomUncles = slot.Uncles - uncleCount += len(slot.Uncles) - bottom = slot.Block - }); err != nil { - utils.Errorf("", "error scanning PPLNS window: %s", err) - - if oldPoolInfo != nil { - // got error, just update last pool - - //this race is ok - oldPoolInfo.SideChain.SecondsSinceLastBlock = max(0, time.Now().Unix()-int64(tip.Timestamp)) - } - return - } - - slices.SortFunc(versions, func(a, b cmdutils.SideChainVersionEntry) int { - return a.Weight.Cmp(b.Weight) * -1 - }) - - for i := range versions { - versions[i].Share = float64(versions[i].Weight.Mul64(100).Lo) / float64(pplnsWeight.Lo) - } - - type totalKnownResult struct { - blocksFound uint64 - minersKnown uint64 - } - - totalKnown := cacheResult(CacheTotalKnownBlocksAndMiners, time.Minute, func() any { - result := &totalKnownResult{} - if err := indexDb.Query("SELECT (SELECT COUNT(*) FROM "+indexDb.GetView("found_main_blocks")+") as found, (SELECT COUNT(*) FROM miners) as miners;", func(row index.RowScanInterface) error { - return row.Scan(&result.blocksFound, &result.minersKnown) - }); err != nil { - return nil - } - - return result - }).(*totalKnownResult) - - lastBlocksFound := index.QueryIterateToSlice(indexDb.GetFoundBlocks("", 201)) - - mainTip := indexDb.GetMainBlockTip() - networkDifficulty := types.DifficultyFrom64(mainTip.Difficulty) - minerData := p2api.MinerData() - expectedBaseBlockReward := block.GetBlockReward(minerData.MedianWeight, minerData.MedianWeight, minerData.AlreadyGeneratedCoins, minerData.MajorVersion) - minerDifficulty := minerData.Difficulty - - getBlockEffort := func(blockCumulativeDifficulty, previousBlockCumulativeDifficulty, networkDifficulty types.Difficulty) float64 { - return float64(blockCumulativeDifficulty.Sub(previousBlockCumulativeDifficulty).Mul64(100).Lo) / float64(networkDifficulty.Lo) - } - - var lastDiff types.Difficulty - if len(lastBlocksFound) > 0 { - lastDiff = lastBlocksFound[0].CumulativeDifficulty - } - - tipCumDiff := tip.CumulativeDifficulty - if lastDiff.Cmp(tipCumDiff) > 0 { - tipCumDiff = lastDiff - } - - currentEffort := getBlockEffort(tipCumDiff, lastDiff, networkDifficulty) - - if currentEffort <= 0 || lastDiff.Cmp64(0) == 0 { - currentEffort = 0 - } - - var blockEfforts []cmdutils.PoolInfoResultSideChainEffortLastEntry - for i, b := range lastBlocksFound { - if i < (len(lastBlocksFound)-1) && b.CumulativeDifficulty.Cmp64(0) > 0 && lastBlocksFound[i+1].CumulativeDifficulty.Cmp64(0) > 0 { - blockEfforts = append(blockEfforts, cmdutils.PoolInfoResultSideChainEffortLastEntry{ - Id: b.MainBlock.Id, - Effort: getBlockEffort(b.CumulativeDifficulty, lastBlocksFound[i+1].CumulativeDifficulty, types.DifficultyFrom64(b.MainBlock.Difficulty)), - }) - } - } - - averageEffort := func(limit int) (result float64) { - maxI := min(limit, len(blockEfforts)) - for i, e := range blockEfforts { - result += e.Effort - if i+1 == maxI { - break - } - } - return result / float64(maxI) - } - - result := &cmdutils.PoolInfoResult{ - SideChain: cmdutils.PoolInfoResultSideChain{ - Consensus: consensus, - 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), - Average50: averageEffort(50), - Average200: averageEffort(200), - Last: blockEfforts, - }, - Window: cmdutils.PoolInfoResultSideChainWindow{ - Miners: len(miners), - Blocks: blockCount, - 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 - }(), - }, - - 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{ - BlockTime: monero.BlockTime, - TransactionUnlockTime: monero.TransactionUnlockTime, - MinerRewardUnlockTime: monero.MinerRewardUnlockTime, - HardForkSupportedVersion: monero.HardForkSupportedVersion, - HardForks: sidechain.NetworkHardFork(consensus), - }, - Id: mainTip.Id, - CoinbaseId: mainTip.CoinbaseId, - Height: mainTip.Height, - Difficulty: networkDifficulty, - Reward: mainTip.Reward, - BaseReward: expectedBaseBlockReward, - NextDifficulty: minerDifficulty, - - BlockTime: monero.BlockTime, - }, - Versions: struct { - P2Pool cmdutils.VersionInfo `json:"p2pool"` - Monero cmdutils.VersionInfo `json:"monero"` - }{P2Pool: getP2PoolVersion(), Monero: getMoneroVersion()}, - } - - if len(lastBlocksFound) > 0 { - result.SideChain.LastFound = fillFoundBlockResult(false, lastBlocksFound[0]) - } - - lastPoolInfo.Store(result) - } - - getPoolInfo() - - go func() { - for range time.Tick(time.Second * 2) { - getPoolInfo() - } - }() - - serveMux.HandleFunc("/api/pool_info", func(writer http.ResponseWriter, request *http.Request) { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, lastPoolInfo.Load()) - }) - - serveMux.HandleFunc("/api/miner_info/{miner:[^ ]+}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - params := request.URL.Query() - - var miner *index.Miner - if len(minerId) > 10 && minerId[0] == '4' { - miner = indexDb.GetMinerByStringAddress(minerId) - } - - if miner == nil { - miner = indexDb.GetMinerByAlias(minerId) - } - - if miner == nil { - if i, err := strconv.Atoi(minerId); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - var lastShareHeight uint64 - var lastShareTimestamp uint64 - - var foundBlocksData [index.InclusionCount]cmdutils.MinerInfoBlockData - if !params.Has("noShares") { - if params.Has("shareEstimates") { - // estimate counts - _ = indexDb.Query(fmt.Sprintf("SELECT count_estimate('SELECT 1 FROM side_blocks WHERE side_blocks.miner = %d AND uncle_of IS NULL AND inclusion = %d;') AS shares_estimate, count_estimate('SELECT 1 FROM side_blocks WHERE side_blocks.miner = %d AND uncle_of IS NOT NULL AND inclusion = %d;') uncles_estimate, side_height AS last_height, timestamp AS last_timestamp FROM side_blocks WHERE side_blocks.miner = %d AND inclusion = %d ORDER BY side_height DESC LIMIT 1;", miner.Id(), index.InclusionInVerifiedChain, miner.Id(), index.InclusionInVerifiedChain, miner.Id(), index.InclusionInVerifiedChain), func(row index.RowScanInterface) error { - var shareEstimate, uncleEstimate, lastHeight, lastTimestamp uint64 - if err := row.Scan(&shareEstimate, &uncleEstimate, &lastHeight, &lastTimestamp); err != nil { - return err - } - foundBlocksData[index.InclusionInVerifiedChain].ShareCount = shareEstimate - foundBlocksData[index.InclusionInVerifiedChain].UncleCount = uncleEstimate - foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight = lastHeight - lastShareHeight = lastHeight - lastShareTimestamp = lastTimestamp - return nil - }) - } else { - _ = indexDb.Query("SELECT COUNT(*) FILTER (WHERE uncle_of IS NULL) AS share_count, COUNT(*) FILTER (WHERE uncle_of IS NOT NULL) AS uncle_count, coalesce(MAX(side_height), 0) AS last_height, inclusion FROM side_blocks WHERE side_blocks.miner = $1 GROUP BY side_blocks.inclusion ORDER BY inclusion ASC;", func(row index.RowScanInterface) error { - var d cmdutils.MinerInfoBlockData - var inclusion index.BlockInclusion - if err := row.Scan(&d.ShareCount, &d.UncleCount, &d.LastShareHeight, &inclusion); err != nil { - return err - } - if inclusion < index.InclusionCount { - foundBlocksData[inclusion] = d - } - return nil - }, miner.Id()) - } - } - - if lastShareHeight == 0 && foundBlocksData[index.InclusionInVerifiedChain].ShareCount > 0 && foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight > lastShareHeight { - lastShareHeight = foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight - } - - if lastShareTimestamp == 0 && lastShareHeight > 0 { - lastShareTimestamp = indexDb.GetTipSideBlockByHeight(lastShareHeight).Timestamp - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - - if lastShareTimestamp < uint64(time.Now().Unix()-3600) { - writer.Header().Set("cache-control", "public; max-age=600") - } else { - writer.Header().Set("cache-control", "public; max-age=60") - } - - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, cmdutils.MinerInfoResult{ - Id: miner.Id(), - Address: miner.Address(), - Alias: miner.Alias(), - Shares: foundBlocksData, - LastShareHeight: lastShareHeight, - LastShareTimestamp: lastShareTimestamp, - }) - }) - - serveMux.HandleFunc("/api/global_indices_lookup", func(writer http.ResponseWriter, request *http.Request) { - if request.Method != "POST" { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusMethodNotAllowed) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_allowed", - }) - _, _ = writer.Write(buf) - return - } - - buf, err := io.ReadAll(request.Body) - 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: "bad_request", - }) - _, _ = writer.Write(buf) - return - } - var indices []uint64 - if err = utils.UnmarshalJSON(buf, &indices); err != nil || len(indices) == 0 { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "bad_request", - }) - _, _ = writer.Write(buf) - return - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, indexDb.QueryGlobalOutputIndices(indices)) - }) - - serveMux.HandleFunc("/api/sweeps_by_spending_global_output_indices", func(writer http.ResponseWriter, request *http.Request) { - if request.Method != "POST" { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusMethodNotAllowed) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_allowed", - }) - _, _ = writer.Write(buf) - return - } - - buf, err := io.ReadAll(request.Body) - 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: "bad_request", - }) - _, _ = writer.Write(buf) - return - } - var indices []uint64 - if err = utils.UnmarshalJSON(buf, &indices); err != nil || len(indices) == 0 { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "bad_request", - }) - _, _ = writer.Write(buf) - return - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, indexDb.GetMainLikelySweepTransactionBySpendingGlobalOutputIndices(indices...)) - }) - - serveMux.HandleFunc("/api/transaction_lookup/{txid:[0-9a-f]+}", func(writer http.ResponseWriter, request *http.Request) { - txId, err := types.HashFromString(mux.Vars(request)["txid"]) - if err != nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - var otherLookupHostFunc func(ctx context.Context, indices []uint64) []*index.MatchedOutput - - if os.Getenv("TRANSACTION_LOOKUP_OTHER") != "" { - otherLookupHostFunc = func(ctx context.Context, indices []uint64) (result []*index.MatchedOutput) { - data, _ := utils.MarshalJSON(indices) - - result = make([]*index.MatchedOutput, len(indices)) - - for _, host := range strings.Split(os.Getenv("TRANSACTION_LOOKUP_OTHER"), ",") { - host = strings.TrimSpace(host) - if host == "" { - continue - } - uri, _ := url.Parse(host + "/api/global_indices_lookup") - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "POST", - URL: uri, - Body: io.NopCloser(bytes.NewReader(data)), - }); err == nil { - func() { - defer response.Body.Close() - if response.StatusCode == http.StatusOK { - if data, err := io.ReadAll(response.Body); err == nil { - r := make([]*index.MatchedOutput, 0, len(indices)) - if utils.UnmarshalJSON(data, &r) == nil && len(r) == len(indices) { - for i := range r { - if result[i] == nil { - result[i] = r[i] - } - } - } - } - } - }() - } - } - return result - } - } - - results := cmdutils.LookupTransactions(otherLookupHostFunc, indexDb, request.Context(), 0, txId) - - if len(results) == 0 { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - indicesToLookup := make([]uint64, 0, len(results[0])) - for _, i := range results[0] { - indicesToLookup = append(indicesToLookup, i.Input.KeyOffsets...) - } - - if outs, err := client.GetDefaultClient().GetOuts(indicesToLookup...); 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: "bad_request", - }) - _, _ = writer.Write(buf) - return - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - for i, out := range outs { - if mb := indexDb.GetMainBlockByHeight(out.Height); mb != nil { - outs[i].Timestamp = mb.Timestamp - } - } - _ = httputils.EncodeJson(request, writer, cmdutils.TransactionLookupResult{ - Id: txId, - Inputs: results[0], - Outs: outs, - Match: results[0].Match(), - }) - } - - }) - - serveMux.HandleFunc("/api/miner_webhooks/{miner:4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - miner := indexDb.GetMinerByStringAddress(minerId) - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "miner_not_found", - }) - _, _ = writer.Write(buf) - return - } - - wh, _ := indexDb.GetMinerWebHooks(miner.Id()) - defer wh.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.MinerWebHook) { - i, w := wh.Next() - if w == nil { - return 0, nil - } - - newHook := &index.MinerWebHook{ - Miner: w.Miner, - Type: w.Type, - } - // Sanitize private data by hashing - newHook.Url = types.Hash(sha256.Sum256([]byte(w.Url))).String() - settings := make(map[string]string) - for k, v := range w.Settings { - if strings.HasPrefix(k, "send_") { - settings[k] = v - } - } - newHook.Settings = settings - return i, newHook - }) - }) - - serveMux.HandleFunc("/api/miner_signed_action/{miner:4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - miner := indexDb.GetMinerByStringAddress(minerId) - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "miner_not_found", - }) - _, _ = writer.Write(buf) - return - } - - params := request.URL.Query() - - sig := strings.TrimSpace(params.Get("signature")) - - var signedAction *cmdutils.SignedAction - if err := utils.UnmarshalJSON([]byte(strings.TrimSpace(params.Get("message"))), &signedAction); err != nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: err.Error(), - }) - _, _ = writer.Write(buf) - return - } else if signedAction == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "invalid_message", - }) - _, _ = writer.Write(buf) - return - } - - result := signedAction.VerifyFallbackToZero(os.Getenv("NET_SERVICE_ADDRESS"), miner.Address(), sig) - if result == address.ResultFail { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusUnauthorized) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "signature_verify_fail", - }) - _, _ = writer.Write(buf) - return - } else if result == address.ResultFailZeroSpend || result == address.ResultFailZeroView { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusUnauthorized) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "signature_verify_fail_zero_private_key", - }) - _, _ = writer.Write(buf) - return - } else if result != address.ResultSuccessSpend && result != address.ResultSuccessView { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusUnauthorized) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "signature_verify_fail_unknown", - }) - _, _ = writer.Write(buf) - return - } - - switch signedAction.Action { - case "set_miner_alias": - if result == address.ResultSuccessView { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusUnauthorized) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "signature_verify_view_signature", - }) - _, _ = writer.Write(buf) - return - } - - if newAlias, ok := signedAction.Get("alias"); ok { - if len(newAlias) > 20 || len(newAlias) < 3 || !func() bool { - for _, c := range newAlias { - if !(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && c != '_' && c != '-' && c != '.' { - return false - } - } - - return true - }() || !unicode.IsLetter(rune(newAlias[0])) { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "invalid_alias", - }) - _, _ = writer.Write(buf) - return - } - - if indexDb.SetMinerAlias(miner.Id(), newAlias) != nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "duplicate_message", - }) - _, _ = writer.Write(buf) - return - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - return - } - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "invalid_alias", - }) - _, _ = writer.Write(buf) - return - } - case "unset_miner_alias": - if result == address.ResultSuccessView { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusUnauthorized) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "signature_verify_view_signature", - }) - _, _ = writer.Write(buf) - return - } - - if indexDb.SetMinerAlias(miner.Id(), "") != nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "duplicate_message", - }) - _, _ = writer.Write(buf) - return - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - return - } - case "add_webhook": - whType, _ := signedAction.Get("type") - whUrl, _ := signedAction.Get("url") - wh := &index.MinerWebHook{ - Miner: miner.Id(), - Type: index.WebHookType(whType), - Url: whUrl, - Settings: make(map[string]string), - } - for _, e := range signedAction.Data { - if strings.HasPrefix(e.Key, "send_") { - wh.Settings[e.Key] = e.Value - } - } - - err := wh.Verify() - 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 - } - err = indexDb.InsertOrUpdateMinerWebHook(wh) - 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 - } - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - return - case "remove_webhook": - urlHash, ok := signedAction.Get("url_hash") - urlType, ok2 := signedAction.Get("type") - if !ok || !ok2 || urlHash == "" { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "invalid_url", - }) - _, _ = writer.Write(buf) - return - } - - it, err := indexDb.GetMinerWebHooks(miner.Id()) - 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: "internal_error", - }) - _, _ = writer.Write(buf) - return - } - - it.All(func(key int, value *index.MinerWebHook) (stop bool) { - if string(value.Type) == urlType && types.Hash(sha256.Sum256([]byte(value.Url))).String() == urlHash { - _ = indexDb.DeleteMinerWebHook(miner.Id(), value.Type) - } - return false - }) - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - return - default: - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "invalid_action", - }) - _, _ = writer.Write(buf) - return - } - }) - - serveMux.HandleFunc("/api/side_blocks_in_window", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - - tip := indexDb.GetSideBlockTip() - - window := uint64(tip.WindowDepth) - if params.Has("window") { - if i, err := strconv.Atoi(params.Get("window")); err == nil { - if i <= int(consensus.ChainWindowSize*4*7) { - window = uint64(i) - } - } - } - - var from uint64 - if params.Has("from") { - if i, err := strconv.Atoi(params.Get("from")); err == nil { - if i >= 0 { - from = uint64(i) - } - } - } - - if from == 0 { - from = tip.SideHeight - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - fillUncles := !params.Has("noUncles") - fillMined := !params.Has("noMainStatus") - fillMiner := !params.Has("noMiner") - - sideBlocks := indexDb.GetSideBlocksInWindow(from, window) - defer sideBlocks.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) { - i, v := sideBlocks.Next() - if v != nil { - v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v) - } - return i, v - }) - }) - - serveMux.HandleFunc("/api/side_blocks_in_window/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - var miner *index.Miner - if len(minerId) > 10 && minerId[0] == '4' { - miner = indexDb.GetMinerByStringAddress(minerId) - } - - if miner == nil { - if i, err := strconv.Atoi(minerId); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - params := request.URL.Query() - - tip := indexDb.GetSideBlockTip() - - window := uint64(tip.WindowDepth) - if params.Has("window") { - if i, err := strconv.Atoi(params.Get("window")); err == nil { - if i <= int(consensus.ChainWindowSize*4*7) { - window = uint64(i) - } - } - } - - var from uint64 - if params.Has("from") { - if i, err := strconv.Atoi(params.Get("from")); err == nil { - if i >= 0 { - from = uint64(i) - } - } - } - - if from == 0 { - from = tip.SideHeight - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - fillUncles := !params.Has("noUncles") - fillMined := !params.Has("noMainStatus") - fillMiner := !params.Has("noMiner") - - sideBlocks := indexDb.GetSideBlocksByMinerIdInWindow(miner.Id(), from, window) - defer sideBlocks.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) { - i, v := sideBlocks.Next() - if v != nil { - v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v) - } - return i, v - }) - }) - - serveMux.HandleFunc("/api/sweeps", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var limit uint64 = 10 - - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - - if limit > 200 { - limit = 200 - } else if limit <= 0 { - limit = 10 - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - result, err := indexDb.GetMainLikelySweepTransactions(limit) - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, result.Next) - }) - - serveMux.HandleFunc("/api/sweeps/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - var miner *index.Miner - if len(minerId) > 10 && minerId[0] == '4' { - miner = indexDb.GetMinerByStringAddress(minerId) - } - - if miner == nil { - if i, err := strconv.Atoi(minerId); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - params := request.URL.Query() - - var limit uint64 = 10 - - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - result, err := indexDb.GetMainLikelySweepTransactionsByAddress(miner.Address(), limit) - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, result.Next) - }) - - serveMux.HandleFunc("/api/payouts/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$}", func(writer http.ResponseWriter, request *http.Request) { - minerId := mux.Vars(request)["miner"] - var miner *index.Miner - if len(minerId) > 10 && minerId[0] == '4' { - miner = indexDb.GetMinerByStringAddress(minerId) - } - - if miner == nil { - if i, err := strconv.Atoi(minerId); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - params := request.URL.Query() - - var limit, timestamp, height uint64 = 10, 0, 0 - - if params.Has("search_limit") { - if i, err := strconv.Atoi(params.Get("search_limit")); err == nil { - limit = uint64(i) - } - } - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - if params.Has("from_timestamp") { - if i, err := strconv.Atoi(params.Get("from_timestamp")); err == nil { - timestamp = uint64(i) - } - } - if params.Has("from_height") { - if i, err := strconv.Atoi(params.Get("from_height")); err == nil { - height = uint64(i) - } - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - var result index.QueryIterator[index.Payout] - var err error - - if timestamp > 0 { - result, err = indexDb.GetPayoutsByMinerIdFromTimestamp(miner.Id(), timestamp) - } else if height > 0 { - result, err = indexDb.GetPayoutsByMinerIdFromHeight(miner.Id(), height) - } else { - result, err = indexDb.GetPayoutsByMinerId(miner.Id(), limit) - } - - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, result.Next) - }) - - serveMux.HandleFunc("/api/redirect/block/{main_height:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) { - http.Redirect(writer, request, fmt.Sprintf("%s/explorer/block/%d", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), utils.DecodeBinaryNumber(mux.Vars(request)["main_height"])), http.StatusFound) - }) - serveMux.HandleFunc("/api/redirect/transaction/{tx_id:.?[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) { - txId := utils.DecodeHexBinaryNumber(mux.Vars(request)["tx_id"]) - if len(txId) != types.HashSize*2 { - writer.WriteHeader(http.StatusNotFound) - return - } - - http.Redirect(writer, request, fmt.Sprintf("%s/explorer/tx/%s", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), txId), http.StatusFound) - }) - serveMux.HandleFunc("/api/redirect/coinbase/{coinbase:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) { - foundTarget := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["coinbase"]))) - if foundTarget == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - http.Redirect(writer, request, fmt.Sprintf("%s/explorer/tx/%s", cmdutils.GetSiteUrl(cmdutils.SiteKeyP2PoolIo, request.Host == torHost), foundTarget.MainBlock.Id.String()), http.StatusFound) - }) - serveMux.HandleFunc("/api/redirect/share/{height:[0-9]+|.?[0-9A-Za-z]+$}", func(writer http.ResponseWriter, request *http.Request) { - c := utils.DecodeBinaryNumber(mux.Vars(request)["height"]) - - blockHeight := c >> 16 - blockIdStart := c & 0xFFFF - - blockStart := []byte{byte((blockIdStart >> 8) & 0xFF), byte(blockIdStart & 0xFF)} - sideBlocks, err := indexDb.GetSideBlocksByQuery("WHERE side_height = $1;", blockHeight) - if err != nil { - panic(err) - } - - var b *index.SideBlock - index.QueryIterate(sideBlocks, func(_ int, s *index.SideBlock) (stop bool) { - if bytes.Compare(s.MainId[:2], blockStart) == 0 { - b = s - return true - } - - if bytes.Compare(s.TemplateId[:2], blockStart) == 0 { - b = s - return true - } - return false - }) - - if b == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - http.Redirect(writer, request, fmt.Sprintf("/share/%s", b.TemplateId.String()), http.StatusFound) - }) - - serveMux.HandleFunc("/api/redirect/prove/{height_index:[0-9]+|.[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) { - i := utils.DecodeBinaryNumber(mux.Vars(request)["height_index"]) - n := uint64(math.Ceil(math.Log2(float64(consensus.ChainWindowSize * 4)))) - - height := i >> n - outputIndex := i & ((1 << n) - 1) - - b := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, height)) - var tx *index.MainCoinbaseOutput - if b != nil { - tx = indexDb.GetMainCoinbaseOutputByIndex(b.MainBlock.CoinbaseId, outputIndex) - } - - if b == nil || tx == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b.MainBlock.Id.String(), tx.Index), http.StatusFound) - }) - - serveMux.HandleFunc("/api/redirect/prove/{height:[0-9]+|.[0-9A-Za-z]+}/{miner:[0-9]+|.?[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) { - b := index.QueryFirstResult(indexDb.GetFoundBlocks("WHERE side_height = $1", 1, utils.DecodeBinaryNumber(mux.Vars(request)["height"]))) - miner := indexDb.GetMiner(utils.DecodeBinaryNumber(mux.Vars(request)["miner"])) - if b == nil || miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - tx := indexDb.GetMainCoinbaseOutputByMinerId(b.MainBlock.Id, miner.Id()) - - if tx == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - http.Redirect(writer, request, fmt.Sprintf("/proof/%s/%d", b.MainBlock.Id.String(), tx.Index), http.StatusFound) - }) - - serveMux.HandleFunc("/api/redirect/miner/{miner:[0-9]+|.?[0-9A-Za-z]+}", func(writer http.ResponseWriter, request *http.Request) { - miner := indexDb.GetMiner(utils.DecodeBinaryNumber(mux.Vars(request)["miner"])) - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - http.Redirect(writer, request, fmt.Sprintf("/miner/%s", miner.Address().ToBase58()), http.StatusFound) - }) - - //other redirects - serveMux.HandleFunc("/api/redirect/last_found{kind:|/full|/light|/raw|/info|/payouts|/coinbase}", func(writer http.ResponseWriter, request *http.Request) { - b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1)) - if b == nil { - writer.WriteHeader(http.StatusNotFound) - return - } - http.Redirect(writer, request, fmt.Sprintf("/api/block_by_id/%s%s?%s", b.MainBlock.SideTemplateId.String(), mux.Vars(request)["kind"], request.URL.RawQuery), http.StatusFound) - }) - serveMux.HandleFunc("/api/redirect/tip{kind:|/full|/light|/raw|/info|/payouts|/coinbase}", func(writer http.ResponseWriter, request *http.Request) { - http.Redirect(writer, request, fmt.Sprintf("/api/block_by_id/%s%s?%s", indexDb.GetSideBlockTip().TemplateId.String(), mux.Vars(request)["kind"], request.URL.RawQuery), http.StatusFound) - }) - - serveMux.HandleFunc("/api/found_blocks", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var limit uint64 = 50 - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - - var minerId uint64 - - if params.Has("miner") { - id := params.Get("miner") - var miner *index.Miner - - if len(id) > 10 && id[0] == '4' { - miner = indexDb.GetMinerByStringAddress(id) - } - - if miner == nil { - if i, err := strconv.Atoi(id); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - minerId = miner.Id() - } - - if limit > 1000 { - limit = 1000 - } - - if limit == 0 { - limit = 50 - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - var result index.QueryIterator[index.FoundBlock] - var err error - - if minerId != 0 { - result, err = indexDb.GetFoundBlocks("WHERE miner = $1", limit, minerId) - } else { - result, err = indexDb.GetFoundBlocks("", limit) - } - - fillMiner := !params.Has("noMiner") - - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.FoundBlock) { - i, v := result.Next() - if v != nil { - v = fillFoundBlockResult(fillMiner, v) - } - return i, v - }) - }) - - serveMux.HandleFunc("/api/side_blocks", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var limit uint64 = 50 - inclusion := index.InclusionInVerifiedChain - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - if params.Has("inclusion") { - if i, err := strconv.Atoi(params.Get("inclusion")); err == nil { - inclusion = index.BlockInclusion(i) - } - } - - if limit > consensus.ChainWindowSize*4*7 { - limit = consensus.ChainWindowSize * 4 * 7 - } - if limit == 0 { - limit = 50 - } - - onlyBlocks := params.Has("onlyBlocks") - - var minerId uint64 - - var miner *index.Miner - if params.Has("miner") { - id := params.Get("miner") - - if len(id) > 10 && id[0] == '4' { - miner = indexDb.GetMinerByStringAddress(id) - } - - if miner == nil { - if i, err := strconv.Atoi(id); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - minerId = miner.Id() - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - fillUncles := !params.Has("noUncles") - fillMined := !params.Has("noMainStatus") - fillMiner := !params.Has("noMiner") - - result, err := indexDb.GetShares(limit, minerId, onlyBlocks, inclusion) - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) { - i, v := result.Next() - if v != nil { - v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v) - } - return i, v - }) - }) - - serveMux.HandleFunc("/api/shares", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var limit uint64 = 50 - if params.Has("limit") { - if i, err := strconv.Atoi(params.Get("limit")); err == nil { - limit = uint64(i) - } - } - - if limit > consensus.ChainWindowSize*4*7 { - limit = consensus.ChainWindowSize * 4 * 7 - } - if limit == 0 { - limit = 50 - } - - onlyBlocks := params.Has("onlyBlocks") - - var minerId uint64 - - if params.Has("miner") { - id := params.Get("miner") - var miner *index.Miner - - if len(id) > 10 && id[0] == '4' { - miner = indexDb.GetMinerByStringAddress(id) - } - - if miner == nil { - if i, err := strconv.Atoi(id); err == nil { - miner = indexDb.GetMiner(uint64(i)) - } - } - - if miner == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - minerId = miner.Id() - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - fillUncles := !params.Has("noUncles") - fillMined := !params.Has("noMainStatus") - fillMiner := !params.Has("noMiner") - - result, err := indexDb.GetShares(limit, minerId, onlyBlocks, index.InclusionInVerifiedChain) - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, func() (int, *index.SideBlock) { - i, v := result.Next() - if v != nil { - v = fillSideBlockResult(fillUncles, fillMined, fillMiner, v) - } - return i, v - }) - }) - - serveMux.HandleFunc("/api/main_block_by_{by:id|height}/{block:[0-9a-f]+|[0-9]+}", func(writer http.ResponseWriter, request *http.Request) { - - var block *index.MainBlock - if mux.Vars(request)["by"] == "id" { - if id, err := types.HashFromString(mux.Vars(request)["block"]); err == nil { - if b := indexDb.GetMainBlockById(id); b != nil { - block = b - } - } - } else if mux.Vars(request)["by"] == "height" { - if height, err := strconv.ParseUint(mux.Vars(request)["block"], 10, 0); err == nil { - if b := indexDb.GetMainBlockByHeight(height); b != nil { - block = b - } - } - } - - if block == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, block) - }) - - serveMux.HandleFunc("/api/main_difficulty_by_{by:id|height}/{block:[0-9a-f]+|[0-9]+}", func(writer http.ResponseWriter, request *http.Request) { - - var difficulty types.Difficulty - if mux.Vars(request)["by"] == "id" { - if id, err := types.HashFromString(mux.Vars(request)["block"]); err == nil { - if b := indexDb.GetMainBlockById(id); b != nil { - difficulty = types.DifficultyFrom64(b.Difficulty) - } else if header := p2api.MainHeaderById(id); header != nil && !header.Difficulty.IsZero() { - difficulty = header.Difficulty - } - } - } else if mux.Vars(request)["by"] == "height" { - if height, err := strconv.ParseUint(mux.Vars(request)["block"], 10, 0); err == nil { - if b := indexDb.GetMainBlockByHeight(height); b != nil { - difficulty = types.DifficultyFrom64(b.Difficulty) - } else if diff := p2api.MainDifficultyByHeight(height); !diff.IsZero() { - difficulty = diff - } - } - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, difficulty) - }) - - serveMux.HandleFunc("/api/block_by_{by:id|height}/{block:[0-9a-f]+|[0-9]+}{kind:|/full|/light|/raw|/payouts|/coinbase}", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - blockId := mux.Vars(request)["block"] - - var sideBlock *index.SideBlock - if mux.Vars(request)["by"] == "id" { - if id, err := types.HashFromString(blockId); err == nil { - if b := indexDb.GetSideBlockByMainId(id); b != nil { - sideBlock = b - } else if b = indexDb.GetTipSideBlockByTemplateId(id); b != nil { - sideBlock = b - } else if bs := index.QueryIterateToSlice(indexDb.GetSideBlocksByTemplateId(id)); len(bs) != 0 { - sideBlock = bs[0] - } else if mb := indexDb.GetMainBlockByCoinbaseId(id); mb != nil { - if b = indexDb.GetSideBlockByMainId(mb.Id); b != nil { - sideBlock = b - } - } - } - } else if mux.Vars(request)["by"] == "height" { - if height, err := strconv.ParseUint(blockId, 10, 0); err == nil { - if b := indexDb.GetTipSideBlockByHeight(height); b != nil { - sideBlock = b - } - } - } - - if sideBlock == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - requestKind := mux.Vars(request)["kind"] - - switch requestKind { - case "/light", "/full", "/raw": - raw := p2api.LightByMainIdWithHint(sideBlock.MainId, sideBlock.TemplateId) - - if raw == nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } - - if requestKind == "/full" || requestKind == "/raw" { - // Process block if needed - if _, err := raw.PreProcessBlockWithOutputs(func(h types.Hash) *sidechain.PoolBlock { - b := p2api.LightByTemplateId(h) - if len(b) > 0 { - return b[0] - } - return nil - }, func() (outputs transaction.Outputs, bottomHeight uint64) { - preAllocatedShares := sidechain.PreAllocateShares(consensus.ChainWindowSize * 2) - preAllocatedRewards := make([]uint64, 0, len(preAllocatedShares)) - return Outputs(p2api, indexDb, sideBlock, indexDb.DerivationCache(), preAllocatedShares, preAllocatedRewards) - }); err != nil { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "could_not_process", - }) - _, _ = writer.Write(buf) - return - } - } - - if requestKind == "/raw" { - writer.Header().Set("Content-Type", "text/plain") - writer.WriteHeader(http.StatusOK) - buf, _ := raw.MarshalBinary() - _, _ = writer.Write(buf) - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, raw) - } - case "/payouts": - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - result, err := indexDb.GetPayoutsBySideBlock(sideBlock) - if err != nil { - panic(err) - } - defer result.Close() - _ = httputils.StreamJsonIterator(request, writer, result.Next) - case "/coinbase": - foundBlock := indexDb.GetMainBlockById(sideBlock.MainId) - if foundBlock == nil { - shares, _ := PayoutHint(p2api, indexDb, sideBlock) - if shares != nil { - poolBlock := p2api.LightByMainId(sideBlock.MainId) - if poolBlock != nil { - - addresses := make(map[address.PackedAddress]*index.MainCoinbaseOutput, len(shares)) - for minerId, amount := range PayoutAmountHint(shares, poolBlock.Main.Coinbase.TotalReward) { - miner := indexDb.GetMiner(minerId) - addresses[miner.Address().ToPackedAddress()] = &index.MainCoinbaseOutput{ - Id: types.ZeroHash, - Miner: miner.Id(), - MinerAddress: miner.Address(), - MinerAlias: miner.Alias(), - Value: amount, - GlobalOutputIndex: 0, - } - } - - sortedAddresses := utils.Keys(addresses) - - //Sort based on addresses - slices.SortFunc(sortedAddresses, func(a address.PackedAddress, b address.PackedAddress) int { - return a.Compare(&b) - }) - - //Shuffle shares - sidechain.ShuffleShares(sortedAddresses, poolBlock.ShareVersion(), poolBlock.Side.CoinbasePrivateKeySeed) - - result := make(index.MainCoinbaseOutputs, len(sortedAddresses)) - - for i, key := range sortedAddresses { - addresses[key].Index = uint32(i) - result[i] = *addresses[key] - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, result) - return - } - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusNotFound) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_found", - }) - _, _ = writer.Write(buf) - return - } else { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - mainOutputs, err := indexDb.GetMainCoinbaseOutputs(foundBlock.CoinbaseId) - if err != nil { - panic(err) - } - defer mainOutputs.Close() - _ = httputils.EncodeJson(request, writer, fillMainCoinbaseOutputs(params, index.IterateToSliceWithoutPointer[index.MainCoinbaseOutput](mainOutputs))) - } - default: - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, fillSideBlockResult(!params.Has("noUncles"), !params.Has("noMainStatus"), !params.Has("noMiner"), sideBlock)) - } - }) - - type difficultyStatResult struct { - Height uint64 `json:"height"` - Timestamp uint64 `json:"timestamp"` - Difficulty uint64 `json:"difficulty"` - CumulativeDifficultyTop64 uint64 `json:"cumulative_difficulty_top64"` - CumulativeDifficulty uint64 `json:"cumulative_difficulty"` - } - - type minerSeenResult struct { - Height uint64 `json:"height"` - Timestamp uint64 `json:"timestamp"` - Miners uint64 `json:"miners"` - } - - serveMux.HandleFunc("/api/stats/{kind:difficulty|miner_seen|miner_seen_window|version_seen$}", func(writer http.ResponseWriter, request *http.Request) { - switch mux.Vars(request)["kind"] { - case "difficulty": - result := make(chan *difficultyStatResult) - - go func() { - defer close(result) - _ = indexDb.Query("SELECT side_height,timestamp,difficulty,cumulative_difficulty FROM side_blocks WHERE side_height % $1 = 0 ORDER BY side_height DESC;", func(row index.RowScanInterface) error { - r := &difficultyStatResult{} - - var cumDiff types.Difficulty - if err := row.Scan(&r.Height, &r.Timestamp, &r.Difficulty, &cumDiff); err != nil { - return err - } - r.CumulativeDifficulty = cumDiff.Lo - r.CumulativeDifficultyTop64 = cumDiff.Hi - - result <- r - return nil - }, 3600/consensus.TargetBlockTime) - }() - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.StreamJsonChan(request, writer, result) - - case "miner_seen": - result := make(chan *minerSeenResult) - - go func() { - defer close(result) - _ = indexDb.Query("SELECT side_height,timestamp,(SELECT COUNT(DISTINCT(b.miner)) FROM side_blocks b WHERE b.side_height <= side_blocks.side_height AND b.side_height > (side_blocks.side_height - $2)) as count FROM side_blocks WHERE side_height % $1 = 0 ORDER BY side_height DESC;", func(row index.RowScanInterface) error { - r := &minerSeenResult{} - - if err := row.Scan(&r.Height, &r.Timestamp, &r.Miners); err != nil { - return err - } - - result <- r - return nil - }, (3600*24)/consensus.TargetBlockTime, (3600*24*7)/consensus.TargetBlockTime) - }() - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.StreamJsonChan(request, writer, result) - - case "miner_seen_window": - result := make(chan *minerSeenResult) - - go func() { - defer close(result) - _ = indexDb.Query("SELECT side_height,timestamp,(SELECT COUNT(DISTINCT(b.miner)) FROM side_blocks b WHERE b.side_height <= side_blocks.side_height AND b.side_height > (side_blocks.side_height - $2)) as count FROM side_blocks WHERE side_height % $1 = 0 ORDER BY side_height DESC;", func(row index.RowScanInterface) error { - r := &minerSeenResult{} - - if err := row.Scan(&r.Height, &r.Timestamp, &r.Miners); err != nil { - return err - } - - result <- r - return nil - }, consensus.ChainWindowSize, consensus.ChainWindowSize) - }() - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.StreamJsonChan(request, writer, result) - } - }) - - // p2pool consensus server api - serveMux.HandleFunc("/api/consensus/peer_list", func(writer http.ResponseWriter, request *http.Request) { - - writer.Header().Set("Content-Type", "text/plain") - writer.WriteHeader(http.StatusOK) - _, _ = writer.Write(p2api.PeerList()) - }) - - serveMux.HandleFunc("/api/consensus/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 - } - - addrPort = netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()) - - if !addrPort.IsValid() || addrPort.Addr().IsLoopback() || addrPort.Addr().IsMulticast() || addrPort.Addr().IsInterfaceLocalMulticast() || addrPort.Addr().IsLinkLocalMulticast() || addrPort.Addr().IsPrivate() || addrPort.Addr().IsUnspecified() || addrPort.Addr().IsLinkLocalUnicast() { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_valid_ip", - }) - _, _ = writer.Write(buf) - return - } - - if addrPort.Port() != consensus.DefaultPort() && addrPort.Port() < 10000 { - //do not allow low port numbers to prevent targeting - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusBadRequest) - buf, _ := utils.MarshalJSON(struct { - Error string `json:"error"` - }{ - Error: "not_valid_port", - }) - _, _ = writer.Write(buf) - return - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, p2api.ConnectionCheck(addrPort)) - }) - - // p2pool disk / p2pool.io emulation - serveMux.HandleFunc("/api/network/stats", func(writer http.ResponseWriter, request *http.Request) { - type networkStats struct { - Difficulty uint64 `json:"difficulty"` - Hash types.Hash `json:"hash"` - Height uint64 `json:"height"` - Reward uint64 `json:"reward"` - Timestamp uint64 `json:"timestamp"` - } - - mainTip := indexDb.GetMainBlockTip() - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, networkStats{ - Difficulty: mainTip.Difficulty, - Hash: mainTip.Id, - Height: mainTip.Height, - Reward: mainTip.Reward, - Timestamp: mainTip.Timestamp, - }) - }) - serveMux.HandleFunc("/api/pool/blocks", func(writer http.ResponseWriter, request *http.Request) { - type poolBlock struct { - Height uint64 `json:"height"` - Hash types.Hash `json:"hash"` - Difficulty uint64 `json:"difficulty"` - TotalHashes uint64 `json:"totalHashes"` - Timestamp uint64 `json:"ts"` - } - - blocks := make([]poolBlock, 0, 200) - - result, err := indexDb.GetFoundBlocks("", 200) - if err != nil { - panic(err) - } - - index.QueryIterate(result, func(_ int, b *index.FoundBlock) (stop bool) { - blocks = append(blocks, poolBlock{ - Height: b.MainBlock.Height, - Hash: b.MainBlock.Id, - Difficulty: b.MainBlock.Difficulty, - TotalHashes: b.CumulativeDifficulty.Lo, - Timestamp: b.MainBlock.Timestamp, - }) - return false - }) - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _ = httputils.EncodeJson(request, writer, blocks) - }) - serveMux.HandleFunc("/api/pool/stats", func(writer http.ResponseWriter, request *http.Request) { - type poolStats struct { - PoolList []string `json:"pool_list"` - PoolStatistics struct { - HashRate uint64 `json:"hashRate"` - Miners uint64 `json:"miners"` - TotalHashes uint64 `json:"totalHashes"` - LastBlockFoundTime uint64 `json:"lastBlockFoundTime"` - LastBlockFound uint64 `json:"lastBlockFound"` - TotalBlocksFound uint64 `json:"totalBlocksFound"` - PPLNSWindowSize uint64 `json:"pplnsWindowSize"` - SidechainDifficulty uint64 `json:"sidechainDifficulty"` - SidechainHeight uint64 `json:"sidechainHeight"` - } `json:"pool_statistics"` - } - - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - - poolInfo := lastPoolInfo.Load() - - var lastBlockFound, lastBlockFoundTime uint64 - b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1)) - if b != nil { - lastBlockFound = b.MainBlock.Height - lastBlockFoundTime = b.MainBlock.Timestamp - } - - _ = httputils.EncodeJson(request, writer, poolStats{ - PoolList: []string{"pplns"}, - PoolStatistics: struct { - HashRate uint64 `json:"hashRate"` - Miners uint64 `json:"miners"` - TotalHashes uint64 `json:"totalHashes"` - LastBlockFoundTime uint64 `json:"lastBlockFoundTime"` - LastBlockFound uint64 `json:"lastBlockFound"` - TotalBlocksFound uint64 `json:"totalBlocksFound"` - PPLNSWindowSize uint64 `json:"pplnsWindowSize"` - SidechainDifficulty uint64 `json:"sidechainDifficulty"` - SidechainHeight uint64 `json:"sidechainHeight"` - }{ - HashRate: poolInfo.SideChain.LastBlock.Difficulty / consensus.TargetBlockTime, - Miners: poolInfo.SideChain.Miners, - TotalHashes: poolInfo.SideChain.LastBlock.CumulativeDifficulty.Lo, - LastBlockFound: lastBlockFound, - LastBlockFoundTime: lastBlockFoundTime, - TotalBlocksFound: poolInfo.SideChain.Found, - PPLNSWindowSize: uint64(poolInfo.SideChain.Window.Blocks), - SidechainDifficulty: poolInfo.SideChain.LastBlock.Difficulty, - SidechainHeight: poolInfo.SideChain.LastBlock.SideHeight, - }, - }) - }) - - serveMux.HandleFunc("/api/config", func(writer http.ResponseWriter, request *http.Request) { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(http.StatusOK) - _, _ = writer.Write([]byte("{\"pplns_fee\":0,\"min_wallet_payout\":300000000,\"dev_donation\":0,\"pool_dev_donation\":0,\"maturity_depth\":60,\"min_denom\":1}")) - }) - - serveMux.HandleFunc("/api/stats_mod", func(writer http.ResponseWriter, request *http.Request) { - - mainTip := indexDb.GetMainBlockTip() - - poolInfo := lastPoolInfo.Load() - - var lastBlockFound, lastBlockFoundTime uint64 - var lastBlockFoundHash types.Hash - var lastBlockCumulativeDifficulty types.Difficulty - b := index.QueryFirstResult(indexDb.GetFoundBlocks("", 1)) - if b != nil { - lastBlockFound = b.MainBlock.Height - lastBlockFoundTime = b.MainBlock.Timestamp - lastBlockFoundHash = b.MainBlock.Id - lastBlockCumulativeDifficulty = b.CumulativeDifficulty - } - - 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.LastBlock.Difficulty/consensus.TargetBlockTime, - poolInfo.SideChain.LastBlock.CumulativeDifficulty.Sub(lastBlockCumulativeDifficulty).Lo, - ))) - }) - - server := &http.Server{ - Addr: "0.0.0.0:8080", - 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 - } - - serveMux.ServeHTTP(writer, request) - }), - } - - if *debugListen != "" { - go func() { - if err := http.ListenAndServe(*debugListen, nil); err != nil { - utils.Panic(err) - } - }() - } - - if err := server.ListenAndServe(); err != nil { - utils.Panic(err) - } -} diff --git a/cmd/api/cache.go b/cmd/api/cache.go deleted file mode 100644 index 44c91c2..0000000 --- a/cmd/api/cache.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "sync" - "time" -) - -const ( - CacheTotalKnownBlocksAndMiners = iota - totalSize -) - -var cache = make([]*cachedResult, totalSize) -var cacheLock sync.RWMutex - -type cachedResult struct { - t time.Time - result any -} - -func cacheResult(k uint, cacheTime time.Duration, result func() any) any { - if k >= totalSize { - return result() - } - if r := func() any { - if cacheTime > 0 { - cacheLock.RLock() - defer cacheLock.RUnlock() - - if r := cache[k]; r != nil && r.t.Add(cacheTime).After(time.Now()) { - return r.result - } - } - - return nil - }(); r != nil { - return r - } - - r := result() - - if cacheTime > 0 && r != nil { - cacheLock.Lock() - defer cacheLock.Unlock() - cache[k] = &cachedResult{ - t: time.Now(), - result: r, - } - } - - return r -} diff --git a/cmd/api/default.pgo b/cmd/api/default.pgo deleted file mode 100644 index 7ab8516..0000000 Binary files a/cmd/api/default.pgo and /dev/null differ diff --git a/cmd/api/go.mod b/cmd/api/go.mod deleted file mode 100644 index 23867bf..0000000 --- a/cmd/api/go.mod +++ /dev/null @@ -1,42 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/api - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils v0.0.0 => ../httputils - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 - 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/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/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/api/go.sum b/cmd/api/go.sum deleted file mode 100644 index c3a2da6..0000000 --- a/cmd/api/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/api/payout_hint.go b/cmd/api/payout_hint.go deleted file mode 100644 index daad902..0000000 --- a/cmd/api/payout_hint.go +++ /dev/null @@ -1,124 +0,0 @@ -package main - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "slices" -) - -func PayoutAmountHint(shares map[uint64]types.Difficulty, reward uint64) map[uint64]uint64 { - amountShares := make(map[uint64]uint64) - totalWeight := types.DifficultyFrom64(0) - for _, w := range shares { - totalWeight = totalWeight.Add(w) - } - - w := types.DifficultyFrom64(0) - rewardGiven := types.DifficultyFrom64(0) - - for miner, weight := range shares { - w = w.Add(weight) - nextValue := w.Mul64(reward).Div(totalWeight) - amountShares[miner] = nextValue.Sub(rewardGiven).Lo - rewardGiven = nextValue - } - return amountShares -} - -func Outputs(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock, derivationCache sidechain.DerivationCacheInterface, preAllocatedShares sidechain.Shares, preAllocatedRewards []uint64) (outputs transaction.Outputs, bottomHeight uint64) { - if tip == nil { - return nil, 0 - } - - poolBlock := p2api.LightByMainId(tip.MainId) - if poolBlock != nil { - window := index.QueryIterateToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip)) - if len(window) == 0 { - return nil, 0 - } - var hintIndex int - - getByTemplateIdFull := func(h types.Hash) *index.SideBlock { - if i := slices.IndexFunc(window, func(e *index.SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex = i - return window[i] - } - return nil - } - getByTemplateId := func(h types.Hash) *index.SideBlock { - //fast lookup first - if i := slices.IndexFunc(window[hintIndex:], func(e *index.SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex += i - return window[hintIndex] - } - return getByTemplateIdFull(h) - } - - getUnclesOf := func(h types.Hash) index.QueryIterator[index.SideBlock] { - parentEffectiveHeight := window[hintIndex].EffectiveHeight - if window[hintIndex].TemplateId != h { - parentEffectiveHeight = 0 - } - - startIndex := 0 - - return &index.FakeQueryResult[index.SideBlock]{ - NextFunction: func() (int, *index.SideBlock) { - for _, b := range window[startIndex+hintIndex:] { - if b.UncleOf == h { - startIndex++ - return startIndex, b - } - startIndex++ - - if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight { - //early exit - break - } - } - return 0, nil - }, - } - } - - return index.CalculateOutputs(indexDb, - tip, - poolBlock.GetTransactionOutputType(), - poolBlock.Main.Coinbase.TotalReward, - &poolBlock.Side.CoinbasePrivateKey, - poolBlock.Side.CoinbasePrivateKeySeed, - p2api.MainDifficultyByHeight, - getByTemplateId, - getUnclesOf, - derivationCache, - preAllocatedShares, - preAllocatedRewards, - ) - } else { - return nil, 0 - } -} - -func PayoutHint(p2api *api.P2PoolApi, indexDb *index.Index, tip *index.SideBlock) (shares map[uint64]types.Difficulty, blockDepth uint64) { - shares = make(map[uint64]types.Difficulty) - - blockDepth, err := index.BlocksInPPLNSWindowFast(indexDb, tip, p2api.MainDifficultyByHeight, func(b *index.SideBlock, weight types.Difficulty) { - if _, ok := shares[b.Miner]; !ok { - shares[b.Miner] = types.DifficultyFrom64(0) - } - shares[b.Miner] = shares[b.Miner].Add(weight) - }) - - if err != nil { - return nil, blockDepth - } - - return shares, blockDepth -} diff --git a/cmd/api/versions.go b/cmd/api/versions.go deleted file mode 100644 index 427c28e..0000000 --- a/cmd/api/versions.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "sync" - "time" -) - -var moneroVersion, p2poolVersion cmdutils.VersionInfo -var moneroVersionLock, p2poolVersionLock sync.Mutex - -const versionCheckInterval = 3600 - -func getMoneroVersion() cmdutils.VersionInfo { - moneroVersionLock.Lock() - defer moneroVersionLock.Unlock() - now := time.Now().Unix() - if (moneroVersion.CheckedTimestamp + versionCheckInterval) >= now { - return moneroVersion - } - response, err := http.DefaultClient.Get("https://api.github.com/repos/monero-project/monero/releases/latest") - if err != nil { - return moneroVersion - } - defer response.Body.Close() - - var releaseData cmdutils.ReleaseDataJson - if data, err := io.ReadAll(response.Body); err != nil { - return moneroVersion - } else if err = utils.UnmarshalJSON(data, &releaseData); err != nil { - return moneroVersion - } else { - moneroVersion.Version = releaseData.TagName - moneroVersion.Timestamp = releaseData.PublishedAt.Unix() - moneroVersion.Link = "https://github.com/monero-project/monero/releases/tag/" + releaseData.TagName - moneroVersion.CheckedTimestamp = now - } - - return moneroVersion -} - -func getP2PoolVersion() cmdutils.VersionInfo { - p2poolVersionLock.Lock() - defer p2poolVersionLock.Unlock() - now := time.Now().Unix() - if (p2poolVersion.CheckedTimestamp + versionCheckInterval) >= now { - return p2poolVersion - } - response, err := http.DefaultClient.Get("https://api.github.com/repos/SChernykh/p2pool/releases/latest") - if err != nil { - return p2poolVersion - } - defer response.Body.Close() - - var releaseData cmdutils.ReleaseDataJson - if data, err := io.ReadAll(response.Body); err != nil { - return p2poolVersion - } else if err = utils.UnmarshalJSON(data, &releaseData); err != nil { - return p2poolVersion - } else { - p2poolVersion.Version = releaseData.TagName - p2poolVersion.Timestamp = releaseData.PublishedAt.Unix() - p2poolVersion.Link = "https://github.com/SChernykh/p2pool/releases/tag/" + releaseData.TagName - p2poolVersion.CheckedTimestamp = now - } - - return p2poolVersion -} diff --git a/cmd/apitocache/api.go b/cmd/apitocache/api.go deleted file mode 100644 index 8ab1012..0000000 --- a/cmd/apitocache/api.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "net/url" -) - -var apiUrl string - -func getTypeFromAPI[T any](method string) *T { - uri, _ := url.Parse(apiUrl + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - var result T - decoder := utils.NewJSONDecoder(response.Body) - if decoder.Decode(&result) != nil { - return nil - } else { - return &result - } - } else { - return nil - } - } -} - -func getSliceFromAPI[T any](method string) []T { - - uri, _ := url.Parse(apiUrl + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - var result []T - decoder := utils.NewJSONDecoder(response.Body) - if decoder.Decode(&result) != nil { - return nil - } else { - return result - } - } else { - return nil - } - } -} - -func getStreamFromAPI[T any](method string) <-chan T { - result := make(chan T, 1) - - go func() { - defer close(result) - uri, _ := url.Parse(apiUrl + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - - if response.StatusCode == http.StatusOK { - - var err error - - // Read opening - var b [1]byte - for { - if _, err = response.Body.Read(b[:]); err != nil { - return - } - if b[0] == '[' { - break - } else if b[0] != ' ' && b[0] != 0xa { - return - } - } - - decoder := utils.NewJSONDecoder(response.Body) - for decoder.More() { - var item T - if err := decoder.Decode(&item); err != nil { - return - } else { - result <- item - } - } - } - } - }() - - return result -} - -func getFromAPIRaw(method string) []byte { - uri, _ := url.Parse(apiUrl + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - if data, err := io.ReadAll(response.Body); err != nil { - return nil - } else { - return data - } - } else { - return nil - } - } -} diff --git a/cmd/apitocache/apitocache.go b/cmd/apitocache/apitocache.go deleted file mode 100644 index e045382..0000000 --- a/cmd/apitocache/apitocache.go +++ /dev/null @@ -1,92 +0,0 @@ -package main - -import ( - "flag" - "fmt" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/legacy" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "slices" - "strconv" -) - -func main() { - selectedApiUrl := flag.String("api", "", "Input API url, for example, https://p2pool.observer/api/") - outputFile := flag.String("output", "p2pool.cache", "Output p2pool.cache path") - fromBlock := flag.String("from", "tip", "Block to start from. Can be an ID or a height") - - flag.Parse() - - apiUrl = *selectedApiUrl - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info") - if poolInfo == nil { - panic("could not fetch consensus") - } - - consensusData, _ := utils.MarshalJSON(poolInfo.SideChain.Consensus) - consensus, err := sidechain.NewConsensusFromJSON(consensusData) - if err != nil { - utils.Panic(err) - } - - utils.Logf("Consensus", "Consensus id = %s", consensus.Id) - - cache, err := legacy.NewCache(consensus, *outputFile) - if err != nil { - utils.Panic(err) - } - defer cache.Close() - - var toFetchUrls []string - - if *fromBlock == "tip" { - toFetchUrls = append(toFetchUrls, fmt.Sprintf("redirect/tip/raw")) - } else if n, err := strconv.ParseUint(*fromBlock, 10, 0); err == nil { - toFetchUrls = append(toFetchUrls, fmt.Sprintf("block_by_height/%d/raw", n)) - } else { - toFetchUrls = append(toFetchUrls, fmt.Sprintf("block_by_id/%s/raw", *fromBlock)) - } - - var fetches int - - addBlockId := func(h types.Hash) { - k := fmt.Sprintf("block_by_id/%s/raw", h) - if slices.Contains(toFetchUrls, k) { - return - } - toFetchUrls = append(toFetchUrls, k) - } - - for len(toFetchUrls) > 0 { - nextUrl := toFetchUrls[0] - fmt.Printf("[%d] fetching %s\n", fetches, nextUrl) - toFetchUrls = slices.Delete(toFetchUrls, 0, 1) - - rawBlock := getFromAPIRaw(nextUrl) - b := &sidechain.PoolBlock{} - err := b.UnmarshalBinary(consensus, &sidechain.NilDerivationCache{}, rawBlock) - if err != nil { - panic(fmt.Errorf("could not fetch block from url %s: %w", nextUrl, err)) - } - - cache.Store(b) - - for _, u := range b.Side.Uncles { - addBlockId(u) - } - addBlockId(b.Side.Parent) - - fetches++ - - if fetches >= legacy.NumBlocks { - print("reached max limit of block cache, exiting\n") - break - } - - } - - cache.Flush() -} diff --git a/cmd/apitocache/go.mod b/cmd/apitocache/go.mod deleted file mode 100644 index cf5755d..0000000 --- a/cmd/apitocache/go.mod +++ /dev/null @@ -1,41 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/apitocache - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 -) - -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/p2pool-observer/cmd/index v0.0.0 // 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/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/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/apitocache/go.sum b/cmd/apitocache/go.sum deleted file mode 100644 index 66d7043..0000000 --- a/cmd/apitocache/go.sum +++ /dev/null @@ -1,46 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/archivetoarchive/archivetoarchive.go b/cmd/archivetoarchive/archivetoarchive.go deleted file mode 100644 index 28291ac..0000000 --- a/cmd/archivetoarchive/archivetoarchive.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "math" - "os" -) - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputArchive := flag.String("input", "", "Input path for archive database") - outputArchive := flag.String("output", "", "Output path for archive database") - 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") - - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - cf, err := os.ReadFile(*inputConsensus) - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - - difficultyCache := lru.New[uint64, types.Difficulty](1024) - - getDifficultyByHeight := func(height uint64) types.Difficulty { - if v := difficultyCache.Get(height); v == nil { - if r, err := client.GetDefaultClient().GetBlockHeaderByHeight(height, context.Background()); err == nil { - d := types.DifficultyFrom64(r.BlockHeader.Difficulty) - difficultyCache.Set(height, d) - return d - } - return types.ZeroDifficulty - } else { - return *v - } - } - - inputCache, err := archive.NewCache(*inputArchive, consensus, getDifficultyByHeight) - if err != nil { - utils.Panic(err) - } - defer inputCache.Close() - - outputCache, err := archive.NewCache(*outputArchive, consensus, getDifficultyByHeight) - if err != nil { - utils.Panic(err) - } - defer outputCache.Close() - - derivationCache := sidechain.NewDerivationLRUCache() - - blockCache := lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)) - - getByTemplateId := func(h types.Hash) *sidechain.PoolBlock { - if v := blockCache.Get(h); v == nil { - if bs := inputCache.LoadByTemplateId(h); len(bs) != 0 { - bs[0].Depth.Store(math.MaxUint64) - blockCache.Set(h, bs[0]) - return bs[0] - } else if bs = outputCache.LoadByTemplateId(h); len(bs) != 0 { - bs[0].Depth.Store(math.MaxUint64) - blockCache.Set(h, bs[0]) - return bs[0] - } else { - return nil - } - } else { - return *v - } - } - - preAllocatedShares := sidechain.PreAllocateShares(consensus.ChainWindowSize * 4) - blocksProcessed := 0 - for blocksAtHeight := range inputCache.ScanHeights(0, math.MaxUint64) { - for i, b := range blocksAtHeight { - blocksProcessed++ - if _, err := b.PreProcessBlock(consensus, derivationCache, preAllocatedShares, getDifficultyByHeight, getByTemplateId); err != nil { - utils.Errorf("", "error processing block %s at %d, %s", types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId)), b.Side.Height, err) - } else { - if parent := getByTemplateId(b.Side.Parent); parent != nil { - topDepth := parent.Depth.Load() - if topDepth == math.MaxUint64 { - b.Depth.Store(consensus.ChainWindowSize * 2) - } else if topDepth == 0 { - b.Depth.Store(0) - } else { - b.Depth.Store(topDepth - 1) - } - } else { - utils.Errorf("", "block %s at %d, could not get parent, fallback", types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId)), b.Side.Height) - b.Depth.Store(math.MaxUint64) - } - outputCache.Store(b) - if i == 0 { - blockCache.Set(types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId)), b) - } - } - } - } - - utils.Logf("Archive", "blocks processed %d", blocksProcessed) -} diff --git a/cmd/archivetoarchive/go.mod b/cmd/archivetoarchive/go.mod deleted file mode 100644 index 190c4df..0000000 --- a/cmd/archivetoarchive/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/archivetoarchive - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - github.com/floatdrop/lru v1.3.0 -) - -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/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.9 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/archivetoarchive/go.sum b/cmd/archivetoarchive/go.sum deleted file mode 100644 index 188f34f..0000000 --- a/cmd/archivetoarchive/go.sum +++ /dev/null @@ -1,47 +0,0 @@ -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/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/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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/archivetoindex/archivetoindex.go b/cmd/archivetoindex/archivetoindex.go deleted file mode 100644 index 50bb3d0..0000000 --- a/cmd/archivetoindex/archivetoindex.go +++ /dev/null @@ -1,347 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "math" - "os" - "sync" -) - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputArchive := flag.String("input", "", "Input path for archive database") - 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") - connString := flag.String("conn", "", "Connection string for postgres database") - - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - client.GetDefaultClient().SetThrottle(1000) - - cf, err := os.ReadFile(*inputConsensus) - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - if err = consensus.InitHasher(2, randomx.FlagSecure, randomx.FlagFullMemory); err != nil { - utils.Panic(err) - } - - var headerCacheLock sync.RWMutex - headerByHeightCache := make(map[uint64]*block.Header) - headerByIdCache := make(map[types.Hash]*block.Header) - - getHeaderByHeight := func(height uint64) *block.Header { - if v := func() *block.Header { - headerCacheLock.RLock() - defer headerCacheLock.RUnlock() - return headerByHeightCache[height] - }(); v == nil { - if r, err := client.GetDefaultClient().GetBlockHeaderByHeight(height, context.Background()); err == nil { - headerCacheLock.Lock() - defer headerCacheLock.Unlock() - prevHash, _ := types.HashFromString(r.BlockHeader.PrevHash) - h, _ := types.HashFromString(r.BlockHeader.Hash) - header := &block.Header{ - MajorVersion: uint8(r.BlockHeader.MajorVersion), - MinorVersion: uint8(r.BlockHeader.MinorVersion), - Timestamp: uint64(r.BlockHeader.Timestamp), - PreviousId: prevHash, - Height: r.BlockHeader.Height, - Nonce: uint32(r.BlockHeader.Nonce), - Reward: r.BlockHeader.Reward, - Id: h, - Difficulty: types.DifficultyFrom64(r.BlockHeader.Difficulty), - } - headerByIdCache[header.Id] = header - headerByHeightCache[header.Height] = header - return header - } - return nil - } else { - return v - } - } - - getDifficultyByHeight := func(height uint64) types.Difficulty { - if v := getHeaderByHeight(height); v != nil { - return v.Difficulty - } - return types.ZeroDifficulty - } - - getSeedByHeight := func(height uint64) (hash types.Hash) { - seedHeight := randomx.SeedHeight(height) - if v := getHeaderByHeight(seedHeight); v != nil { - return v.Id - } - return types.ZeroHash - } - - archiveCache, err := archive.NewCache(*inputArchive, consensus, getDifficultyByHeight) - if err != nil { - utils.Panic(err) - } - defer archiveCache.Close() - - blockCache := lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)) - - derivationCache := sidechain.NewDerivationLRUCache() - getByTemplateIdDirect := func(h types.Hash) *sidechain.PoolBlock { - if v := blockCache.Get(h); v == nil { - if bs := archiveCache.LoadByTemplateId(h); len(bs) != 0 { - blockCache.Set(h, bs[0]) - return bs[0] - } else { - return nil - } - } else { - return *v - } - } - - processBlock := func(b *sidechain.PoolBlock) error { - var preAllocatedShares sidechain.Shares - if len(b.Main.Coinbase.Outputs) == 0 { - //cannot use SideTemplateId() as it might not be proper to calculate yet. fetch from coinbase only here - if b2 := getByTemplateIdDirect(types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId))); b2 != nil && len(b2.Main.Coinbase.Outputs) != 0 { - b.Main.Coinbase.Outputs = b2.Main.Coinbase.Outputs - } else { - preAllocatedShares = sidechain.PreAllocateShares(consensus.ChainWindowSize * 2) - } - } - - _, err := b.PreProcessBlock(consensus, derivationCache, preAllocatedShares, getDifficultyByHeight, getByTemplateIdDirect) - return err - } - - getByTemplateId := func(h types.Hash) *sidechain.PoolBlock { - if v := getByTemplateIdDirect(h); v != nil { - if processBlock(v) != nil { - return nil - } - return v - } else { - return nil - } - } - - indexDb, err := index.OpenIndex(*connString, consensus, getDifficultyByHeight, getSeedByHeight, getByTemplateIdDirect) - if err != nil { - utils.Panic(err) - } - defer indexDb.Close() - - totalStored := 0 - - var lastRangeHeight, rangeStart uint64 = math.MaxUint64, math.MaxUint64 - var lastTipEntries []*sidechain.PoolBlock - - type rangeEntry struct { - //todo: check time of range - startHeight uint64 - tipHeight uint64 - tipEntries []*sidechain.PoolBlock - } - - var heightRanges []rangeEntry - - /*id, _ := types.HashFromString("b83a96d30c7db3b15a65fa43ddcf6914bb4e176dea49a94a5263c9adca93d4cc") - heightRanges = append(heightRanges, rangeEntry{ - startHeight: 3087255, - tipHeight: 4728558, - tipEntries: []*sidechain.PoolBlock{getByTemplateId(id)}, - })*/ - - if len(heightRanges) == 0 { - for blocksAtHeight := range archiveCache.ScanHeights(0, math.MaxUint64) { - if len(blocksAtHeight) == 0 { - utils.Panicf("no blocks at %d + 1?", lastRangeHeight) - } - if lastRangeHeight == math.MaxUint64 { - rangeStart = blocksAtHeight[0].Side.Height - } else if blocksAtHeight[0].Side.Height != (lastRangeHeight + 1) { // new range - heightRanges = append(heightRanges, rangeEntry{ - startHeight: rangeStart, - tipHeight: lastRangeHeight, - tipEntries: lastTipEntries, - }) - utils.Logf("", "range %d -> %d, total of %d height(s)", rangeStart, lastRangeHeight, lastRangeHeight-rangeStart+1) - utils.Logf("", "missing %d -> %d, total of %d height(s)", lastRangeHeight+1, blocksAtHeight[0].Side.Height-1, (blocksAtHeight[0].Side.Height-1)-(lastRangeHeight+1)+1) - rangeStart = blocksAtHeight[0].Side.Height - } - lastRangeHeight = blocksAtHeight[0].Side.Height - totalStored += len(blocksAtHeight) - lastTipEntries = blocksAtHeight - } - utils.Logf("", "range %d -> %d, total of %d height(s)", rangeStart, lastRangeHeight, lastRangeHeight-rangeStart+1) - heightRanges = append(heightRanges, rangeEntry{ - startHeight: rangeStart, - tipHeight: lastRangeHeight, - tipEntries: lastTipEntries, - }) - - utils.Logf("", "total stored %d", totalStored) - } - - var lastTime, lastHeight uint64 = math.MaxUint64, math.MaxUint64 - - for i := len(heightRanges) - 1; i >= 0; i-- { - r := heightRanges[i] - - var bestTip *sidechain.PoolBlock - if len(r.tipEntries) == 0 { - bestTip = r.tipEntries[0] - } else { - for _, b := range r.tipEntries { - if (b.Main.Timestamp - 60*5) > lastTime { //2m offset of max time drift - continue - } - - if bestTip == nil { - bestTip = b - } else if bestTip.Main.Coinbase.GenHeight < b.Main.Coinbase.GenHeight { - bestTip = b - } else if bestTip.Main.Timestamp < b.Main.Timestamp { - bestTip = b - } - } - } - - if bestTip == nil { - utils.Logf("", "skipped range %d to %d due to: nil tip", r.startHeight, r.tipHeight) - continue - } else if bestTip.Main.Coinbase.GenHeight > lastHeight { - utils.Logf("", "skipped range %d to %d due to: main height %d > %d", r.startHeight, r.tipHeight, bestTip.Main.Coinbase.GenHeight, lastHeight) - continue - } else if (bestTip.Main.Timestamp - 60*5) > lastTime { - utils.Logf("", "skipped range %d to %d due to: timestamp %d > %d", r.startHeight, r.tipHeight, bestTip.Main.Timestamp, lastTime) - continue - } - - if err := processBlock(bestTip); err != nil { - utils.Logf("", "skipped range %d to %d due to: could not process tip: %s", r.startHeight, r.tipHeight, err) - continue - } - - lastTime = bestTip.Main.Timestamp - lastHeight = bestTip.Main.Coinbase.GenHeight - - if r.startHeight-r.tipHeight > consensus.ChainWindowSize*2 && - indexDb.GetTipSideBlockByTemplateId(bestTip.SideTemplateId(consensus)) != nil && - indexDb.GetTipSideBlockByHeight(r.startHeight+consensus.ChainWindowSize+1) != nil && - index.QueryHasResults(indexDb.GetSideBlocksByHeight(r.startHeight)) { - continue - } - - // skip inserted heights - for bestTip != nil && indexDb.GetTipSideBlockByTemplateId(bestTip.SideTemplateId(consensus)) != nil { - utils.Logf("", "skip id = %s, template id = %s, height = %d", bestTip.MainId(), bestTip.SideTemplateId(consensus), bestTip.Side.Height) - bestTip = getByTemplateId(bestTip.Side.Parent) - } - - for cur := bestTip; cur != nil; cur = getByTemplateId(cur.Side.Parent) { - utils.Logf("", "id = %s, template id = %s, height = %d", cur.MainId(), cur.SideTemplateId(consensus), cur.Side.Height) - - if err = indexDb.InsertOrUpdatePoolBlock(cur, index.InclusionInVerifiedChain); err != nil { - utils.Errorf("", "error inserting %s, %s at %d: %s", cur.SideTemplateId(consensus), cur.MainId(), cur.Side.Height, err) - break - } - - lastTime = cur.Main.Timestamp - lastHeight = cur.Main.Coinbase.GenHeight - - curId := cur.SideTemplateId(consensus) - - for _, e := range archiveCache.LoadBySideChainHeight(cur.Side.Height) { - if cur.FullId() == e.FullId() { - continue - } - timeDiff := int64(e.Main.Timestamp) - int64(cur.Main.Timestamp) - if timeDiff < 0 { - timeDiff = -timeDiff - } - if timeDiff > 3600*12 { //More than 12 hours difference, do not include - continue - } - if processBlock(e) != nil { - utils.Errorf("", "error processing orphan/alternate %s, %s at %d: %s", e.SideTemplateId(consensus), e.MainId(), e.Side.Height, err) - continue - } - if indexDb.GetSideBlockByMainId(e.MainId()) != nil { - continue - } - if curId == e.SideTemplateId(consensus) { - if err = indexDb.InsertOrUpdatePoolBlock(e, index.InclusionAlternateInVerifiedChain); err != nil { - utils.Panicf("error inserting alternate %s, %s at %d: %s", e.SideTemplateId(consensus), e.MainId(), e.Side.Height, err) - break - } - } else { - if err = indexDb.InsertOrUpdatePoolBlock(e, index.InclusionOrphan); err != nil { - utils.Panicf("error inserting orphan %s, %s at %d: %s", e.SideTemplateId(consensus), e.MainId(), e.Side.Height, err) - break - } - } - } - } - - blockCache = lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)) - headerByIdCache = make(map[types.Hash]*block.Header) - headerByHeightCache = make(map[uint64]*block.Header) - derivationCache.Clear() - } - - var maxHeight, minHeight uint64 - if err := indexDb.Query("SELECT MAX(main_height), MIN(main_height) FROM side_blocks WHERE inclusion = $1;", func(row index.RowScanInterface) error { - return row.Scan(&maxHeight, &minHeight) - }, index.InclusionInVerifiedChain); err != nil { - utils.Panic(err) - } - - heightCount := maxHeight - minHeight + 1 - - const strideSize = 1000 - strides := heightCount / strideSize - - ctx := context.Background() - - for stride := uint64(0); stride <= strides; stride++ { - start := minHeight + stride*strideSize - end := min(maxHeight, minHeight+stride*strideSize+strideSize) - utils.Logf("", "checking %d to %d", start, end) - if headers, err := client.GetDefaultClient().GetBlockHeadersRangeResult(start, end, ctx); err != nil { - utils.Panic(err) - } else { - for _, h := range headers.Headers { - if err := cmdutils.FindAndInsertMainHeader(h, indexDb, func(b *sidechain.PoolBlock) { - archiveCache.Store(b) - }, client.GetDefaultClient(), getDifficultyByHeight, getByTemplateIdDirect, archiveCache.LoadByMainId, archiveCache.LoadByMainChainHeight, processBlock); err != nil { - utils.Panic(err) - continue - } - } - } - } - - mainBlocks, _ := indexDb.GetMainBlocksByQuery("WHERE side_template_id IS NOT NULL ORDER BY height DESC;") - index.QueryIterate(mainBlocks, func(_ int, mb *index.MainBlock) (stop bool) { - if err := cmdutils.FindAndInsertMainHeaderOutputs(mb, indexDb, client.GetDefaultClient(), getDifficultyByHeight, getByTemplateIdDirect, archiveCache.LoadByMainId, archiveCache.LoadByMainChainHeight, processBlock); err != nil { - utils.Error(err) - } - return false - }) -} diff --git a/cmd/archivetoindex/go.mod b/cmd/archivetoindex/go.mod deleted file mode 100644 index 4693c1e..0000000 --- a/cmd/archivetoindex/go.mod +++ /dev/null @@ -1,42 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/archivetoindex - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - github.com/floatdrop/lru v1.3.0 -) - -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/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/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - go.etcd.io/bbolt v1.3.9 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/archivetoindex/go.sum b/cmd/archivetoindex/go.sum deleted file mode 100644 index 6a981f8..0000000 --- a/cmd/archivetoindex/go.sum +++ /dev/null @@ -1,49 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/cachetoarchive/cachetoarchive.go b/cmd/cachetoarchive/cachetoarchive.go deleted file mode 100644 index 2b68455..0000000 --- a/cmd/cachetoarchive/cachetoarchive.go +++ /dev/null @@ -1,104 +0,0 @@ -package main - -import ( - "context" - "flag" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/legacy" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "math" - "os" -) - -type loadee struct { - c *sidechain.Consensus - cb func(block *sidechain.PoolBlock) -} - -func (l *loadee) Consensus() *sidechain.Consensus { - return l.c -} - -func (l *loadee) AddCachedBlock(block *sidechain.PoolBlock) { - l.cb(block) -} - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputFile := flag.String("input", "p2pool.cache", "Input p2pool.cache path") - outputArchive := flag.String("output", "", "Output path for archive database") - - flag.Parse() - - cf, err := os.ReadFile(*inputConsensus) - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - - cache, err := legacy.NewCache(consensus, *inputFile) - if err != nil { - utils.Panic(err) - } - defer cache.Close() - - difficultyCache := lru.New[uint64, types.Difficulty](1024) - - getDifficultyByHeight := func(height uint64) types.Difficulty { - if v := difficultyCache.Get(height); v == nil { - if r, err := client.GetDefaultClient().GetBlockHeaderByHeight(height, context.Background()); err == nil { - d := types.DifficultyFrom64(r.BlockHeader.Difficulty) - difficultyCache.Set(height, d) - return d - } - return types.ZeroDifficulty - } else { - return *v - } - } - - archiveCache, err := archive.NewCache(*outputArchive, consensus, getDifficultyByHeight) - if err != nil { - utils.Panic(err) - } - defer archiveCache.Close() - - cachedBlocks := make(map[types.Hash]*sidechain.PoolBlock) - - l := &loadee{ - c: consensus, - cb: func(block *sidechain.PoolBlock) { - expectedBlockId := types.HashFromBytes(block.CoinbaseExtra(sidechain.SideTemplateId)) - calculatedBlockId := block.SideTemplateId(consensus) - - if expectedBlockId != calculatedBlockId { - utils.Errorf("", "block height %d, template id %s, expected %s", block.Side.Height, calculatedBlockId, expectedBlockId) - } else { - cachedBlocks[expectedBlockId] = block - } - }, - } - - cache.LoadAll(l) - - var storeBlock func(b *sidechain.PoolBlock) - storeBlock = func(b *sidechain.PoolBlock) { - if parent := cachedBlocks[b.Side.Parent]; parent != nil { - b.FillTransactionParentIndices(parent) - storeBlock(parent) - } - b.Depth.Store(math.MaxUint64) - archiveCache.Store(b) - } - for _, b := range cachedBlocks { - if b.Depth.Load() == math.MaxUint64 { - continue - } - storeBlock(b) - } -} diff --git a/cmd/cachetoarchive/go.mod b/cmd/cachetoarchive/go.mod deleted file mode 100644 index 5a61fa1..0000000 --- a/cmd/cachetoarchive/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cachetoarchive - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - github.com/floatdrop/lru v1.3.0 -) - -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/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.9 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/cachetoarchive/go.sum b/cmd/cachetoarchive/go.sum deleted file mode 100644 index 188f34f..0000000 --- a/cmd/cachetoarchive/go.sum +++ /dev/null @@ -1,47 +0,0 @@ -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/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/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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/daemon/daemon.go b/cmd/daemon/daemon.go deleted file mode 100644 index 4af2eb8..0000000 --- a/cmd/daemon/daemon.go +++ /dev/null @@ -1,353 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - p2poolapi "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "net/http" - _ "net/http/pprof" - "sync" - "sync/atomic" - "time" -) - -func blockId(b *sidechain.PoolBlock) types.Hash { - return types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId)) -} - -var sideBlocksLock sync.RWMutex - -func main() { - - 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") - startFromHeight := flag.Uint64("from", 0, "Start sync from this height") - dbString := flag.String("db", "", "") - p2poolApiHost := flag.String("api-host", "", "Host URL for p2pool go observer consensus") - fullMode := flag.Bool("full-mode", false, "Allocate RandomX dataset, uses 2GB of RAM") - debugListen := flag.String("debug-listen", "", "Provide a bind address and port to expose a pprof HTTP API on it.") - hookProxy := flag.String("hook-proxy", "", "socks5 proxy host:port for webhook requests") - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - if *hookProxy != "" { - cmdutils.SetWebHookProxy(*hookProxy) - } - - p2api := p2poolapi.NewP2PoolApi(*p2poolApiHost) - - if err := p2api.WaitSync(); err != nil { - utils.Panic(err) - } - - if *fullMode { - if err := p2api.Consensus().InitHasher(1, randomx.FlagSecure, randomx.FlagFullMemory); err != nil { - utils.Panic(err) - } - } else { - if err := p2api.Consensus().InitHasher(1, randomx.FlagSecure); err != nil { - utils.Panic(err) - } - } - - indexDb, err := index.OpenIndex(*dbString, p2api.Consensus(), p2api.DifficultyByHeight, p2api.SeedByHeight, p2api.ByTemplateId) - if err != nil { - utils.Panic(err) - } - defer indexDb.Close() - - dbTip := indexDb.GetSideBlockTip() - - var tipHeight uint64 - if dbTip != nil { - tipHeight = dbTip.SideHeight - } - utils.Logf("CHAIN", "Last known database tip is %d\n", tipHeight) - - window, uncles := p2api.StateFromTip() - if *startFromHeight != 0 { - tip := p2api.BySideHeight(*startFromHeight) - if len(tip) != 0 { - window, uncles = p2api.WindowFromTemplateId(blockId(tip[0])) - } else { - tip = p2api.BySideHeight(*startFromHeight + p2api.Consensus().ChainWindowSize) - if len(tip) != 0 { - window, uncles = p2api.WindowFromTemplateId(blockId(tip[0])) - } - } - } - - insertFromTip := func(tip *sidechain.PoolBlock) { - if indexDb.GetTipSideBlockByTemplateId(blockId(tip)) != nil { - //reached old tip - return - } - for cur := tip; cur != nil; cur = p2api.ByTemplateId(cur.Side.Parent) { - utils.Logf("CHAIN", "Inserting share %s at height %d\n", blockId(cur).String(), cur.Side.Height) - for _, u := range cur.Side.Uncles { - utils.Logf("CHAIN", "Inserting uncle %s at parent height %d\n", u.String(), cur.Side.Height) - } - if err := indexDb.InsertOrUpdatePoolBlock(cur, index.InclusionInVerifiedChain); err != nil { - utils.Panic(err) - } - if indexDb.GetTipSideBlockByTemplateId(cur.Side.Parent) != nil { - //reached old tip - break - } - } - } - - var backfillScan []types.Hash - - for len(window) > 0 { - utils.Logf("CHAIN", "Found range %d -> %d (%s to %s), %d shares, %d uncles", window[0].Side.Height, window[len(window)-1].Side.Height, window[0].SideTemplateId(p2api.Consensus()), window[len(window)-1].SideTemplateId(p2api.Consensus()), len(window), len(uncles)) - for _, b := range window { - indexDb.CachePoolBlock(b) - } - for _, u := range uncles { - indexDb.CachePoolBlock(u) - } - for _, b := range window { - if indexDb.GetTipSideBlockByTemplateId(b.SideTemplateId(p2api.Consensus())) != nil { - //reached old tip - window = nil - break - } - utils.Logf("CHAIN", "Inserting share %s at height %d\n", blockId(b).String(), b.Side.Height) - for _, u := range b.Side.Uncles { - utils.Logf("CHAIN", "Inserting uncle %s at parent height %d\n", u.String(), b.Side.Height) - } - if err := indexDb.InsertOrUpdatePoolBlock(b, index.InclusionInVerifiedChain); err != nil { - utils.Panic(err) - } - - if b.IsProofHigherThanMainDifficulty(p2api.Consensus().GetHasher(), indexDb.GetDifficultyByHeight, indexDb.GetSeedByHeight) { - backfillScan = append(backfillScan, b.MainId()) - } - - for _, uncleId := range b.Side.Uncles { - if u := uncles.Get(uncleId); u != nil && u.IsProofHigherThanMainDifficulty(p2api.Consensus().GetHasher(), indexDb.GetDifficultyByHeight, indexDb.GetSeedByHeight) { - backfillScan = append(backfillScan, u.MainId()) - } - } - } - - if len(window) == 0 { - break - } - - parent := p2api.ByTemplateId(window[len(window)-1].Side.Parent) - if parent == nil { - break - } - window, uncles = p2api.WindowFromTemplateId(blockId(parent)) - if len(window) == 0 { - insertFromTip(parent) - break - } - } - - var maxHeight, currentHeight uint64 - if err = indexDb.Query("SELECT (SELECT MAX(main_height) FROM side_blocks) AS max_height, (SELECT MAX(height) FROM main_blocks) AS current_height;", func(row index.RowScanInterface) error { - return row.Scan(&maxHeight, ¤tHeight) - }); err != nil { - utils.Panic(err) - } - - ctx := context.Background() - - scanHeader := func(h daemon.BlockHeader) error { - if err := cmdutils.FindAndInsertMainHeader(h, indexDb, func(b *sidechain.PoolBlock) { - p2api.InsertAlternate(b) - }, client.GetDefaultClient(), indexDb.GetDifficultyByHeight, indexDb.GetByTemplateId, p2api.ByMainId, p2api.LightByMainHeight, func(b *sidechain.PoolBlock) error { - _, err := b.PreProcessBlock(p2api.Consensus(), &sidechain.NilDerivationCache{}, sidechain.PreAllocateShares(p2api.Consensus().ChainWindowSize*2), indexDb.GetDifficultyByHeight, indexDb.GetByTemplateId) - return err - }); err != nil { - return err - } - return nil - } - - heightCount := maxHeight - 1 - currentHeight + 1 - - const strideSize = 1000 - strides := heightCount / strideSize - - //backfill headers - for stride := uint64(0); stride <= strides; stride++ { - start := currentHeight + stride*strideSize - end := min(maxHeight-1, currentHeight+stride*strideSize+strideSize) - utils.Logf("", "checking %d to %d", start, end) - if headers, err := client.GetDefaultClient().GetBlockHeadersRangeResult(start, end, ctx); err != nil { - utils.Panic(err) - } else { - for _, h := range headers.Headers { - if err := scanHeader(h); err != nil { - utils.Panic(err) - continue - } - } - } - } - - // backfill any missing headers when p2pool was down - for _, mainId := range backfillScan { - utils.Logf("", "checking backfill %s", mainId) - if header, err := client.GetDefaultClient().GetBlockHeaderByHash(mainId, ctx); err != nil { - utils.Errorf("", "not found %s", mainId) - } else { - if err := scanHeader(*header); err != nil { - utils.Panic(err) - continue - } - } - } - - setupEventHandler(p2api, indexDb) - - var doCheckOfOldBlocks atomic.Bool - - doCheckOfOldBlocks.Store(true) - - go func() { - //do deep scan for any missed main headers or deep reorgs every once in a while - for range time.Tick(time.Second * monero.BlockTime) { - if !doCheckOfOldBlocks.Load() { - continue - } - mainTip := indexDb.GetMainBlockTip() - for h := mainTip.Height; h >= 0 && h >= (mainTip.Height-monero.TransactionUnlockTime); h-- { - header := indexDb.GetMainBlockByHeight(h) - if header == nil { - break - } - cur, _ := client.GetDefaultClient().GetBlockHeaderByHash(header.Id, ctx) - if cur == nil { - break - } - go func() { - sideBlocksLock.Lock() - defer sideBlocksLock.Unlock() - if err := scanHeader(*cur); err != nil { - utils.Panic(err) - } - }() - } - } - }() - - go func() { - //process older full blocks and sweeps - for range time.Tick(time.Second * monero.BlockTime) { - - actualTip := indexDb.GetMainBlockTip() - mainTip := actualTip - maxDepth := mainTip.Height - randomx.SeedHashEpochBlocks*4 - - //find top start height - for h := mainTip.Height - monero.TransactionUnlockTime; h >= maxDepth; h-- { - mainTip = indexDb.GetMainBlockByHeight(h) - if mainTip == nil { - continue - } - if isProcessed, ok := mainTip.GetMetadata("processed").(bool); ok && isProcessed { - break - } - } - - if mainTip.Height == maxDepth { - utils.Logf("", "Reached maxdepth %d: Use scansweeps to backfill data", maxDepth) - } - - for h := mainTip.Height - monero.MinerRewardUnlockTime; h <= actualTip.Height-monero.TransactionUnlockTime; h++ { - b := indexDb.GetMainBlockByHeight(h) - if b == nil { - continue - } - if isProcessed, ok := b.GetMetadata("processed").(bool); ok && isProcessed { - continue - } - - if err := cmdutils.ProcessFullBlock(b, indexDb); err != nil { - utils.Logf("", "error processing block %s at %d: %s", b.Id, b.Height, err) - } - } - } - }() - - if *debugListen != "" { - go func() { - if err := http.ListenAndServe(*debugListen, nil); err != nil { - utils.Panic(err) - } - }() - } - - for range time.Tick(time.Second * 1) { - currentTip := indexDb.GetSideBlockTip() - currentMainTip := indexDb.GetMainBlockTip() - - tip := p2api.Tip() - mainTip := p2api.MainTip() - - if tip == nil || mainTip == nil { - utils.Errorf("", "could not fetch tip or main tip") - continue - } - - if blockId(tip) != currentTip.TemplateId { - if tip.Side.Height < currentTip.SideHeight { - //wtf - utils.Panicf("tip height less than ours, abort: %d < %d", tip.Side.Height, currentTip.SideHeight) - } else { - func() { - sideBlocksLock.Lock() - defer sideBlocksLock.Unlock() - insertFromTip(tip) - }() - } - } - - if mainTip.Id != currentMainTip.Id { - if mainTip.Height < currentMainTip.Height { - //wtf - utils.Panicf("main tip height less than ours, abort: %d < %d", mainTip.Height, currentMainTip.Height) - } else { - var prevHash types.Hash - for cur, _ := client.GetDefaultClient().GetBlockHeaderByHash(mainTip.Id, ctx); cur != nil; cur, _ = client.GetDefaultClient().GetBlockHeaderByHash(prevHash, ctx) { - curHash, _ := types.HashFromString(cur.Hash) - curDb := indexDb.GetMainBlockByHeight(cur.Height) - if curDb != nil { - if curDb.Id == curHash { - break - } else { //there has been a swap - doCheckOfOldBlocks.Store(true) - } - } - utils.Logf("MAIN", "Insert main block %d, id %s", cur.Height, curHash) - func() { - sideBlocksLock.Lock() - defer sideBlocksLock.Unlock() - doCheckOfOldBlocks.Store(true) - - if err := scanHeader(*cur); err != nil { - utils.Panic(err) - } - prevHash, _ = types.HashFromString(cur.PrevHash) - }() - } - } - } - } -} diff --git a/cmd/daemon/default.pgo b/cmd/daemon/default.pgo deleted file mode 100644 index 36597d7..0000000 Binary files a/cmd/daemon/default.pgo and /dev/null differ diff --git a/cmd/daemon/go.mod b/cmd/daemon/go.mod deleted file mode 100644 index 44dc614..0000000 --- a/cmd/daemon/go.mod +++ /dev/null @@ -1,39 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/daemon - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -require ( - git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523 - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 - nhooyr.io/websocket v1.8.10 -) - -require ( - git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33 // 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/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/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/daemon/go.sum b/cmd/daemon/go.sum deleted file mode 100644 index de40167..0000000 --- a/cmd/daemon/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= -nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= -nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/cmd/daemon/ws.go b/cmd/daemon/ws.go deleted file mode 100644 index c02e694..0000000 --- a/cmd/daemon/ws.go +++ /dev/null @@ -1,519 +0,0 @@ -package main - -import ( - "context" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api" - types2 "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "net/http" - "nhooyr.io/websocket" - "slices" - "sync" - "sync/atomic" - "time" -) - -type listener struct { - ListenerId uint64 - Write func(buf []byte) - Context context.Context - Cancel func() -} - -type SortedBuf[T any] struct { - buf []T - compare func(a, b T) int -} - -func NewSortedBuf[T any](size int, compare func(a, b T) int) *SortedBuf[T] { - return &SortedBuf[T]{ - buf: make([]T, size, size+1), - compare: compare, - } -} - -func (b *SortedBuf[T]) Insert(value T) bool { - if b.Has(value) { - return false - } - b.buf = append(b.buf, value) - slices.SortFunc(b.buf, func(i, j T) int { - //keep highest value at 0 - return b.compare(i, j) * -1 - }) - b.buf = b.buf[:len(b.buf)-1] - return b.Has(value) -} - -func (b *SortedBuf[T]) Remove(value T) { - var zeroValue T - for i, v := range b.buf { - if b.compare(value, v) == 0 { - b.buf[i] = zeroValue - } - } - slices.SortFunc(b.buf, func(i, j T) int { - //keep highest value at index 0 - return b.compare(i, j) * -1 - }) -} - -func (b *SortedBuf[T]) Has(value T) bool { - for _, v := range b.buf { - if b.compare(value, v) == 0 { - //value inserted - return true - } - } - return false -} - -var listenerLock sync.RWMutex -var listenerIdCounter atomic.Uint64 -var listeners []*listener - -func setupEventHandler(p2api *api.P2PoolApi, indexDb *index.Index) { - - server := &http.Server{ - Addr: "0.0.0.0:8787", - ReadTimeout: time.Second * 2, - Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - requestTime := time.Now() - c, err := websocket.Accept(writer, request, nil) - if err != nil { - writer.WriteHeader(http.StatusInternalServerError) - return - } - listenerId := listenerIdCounter.Add(1) - defer func() { - listenerLock.Lock() - defer listenerLock.Unlock() - if i := slices.IndexFunc(listeners, func(listener *listener) bool { - return listener.ListenerId == listenerId - }); i != -1 { - listeners = slices.Delete(listeners, i, i+1) - } - utils.Logf("WS", "Client %d detached after %.02f seconds", listenerId, time.Now().Sub(requestTime).Seconds()) - }() - - ctx, cancel := context.WithCancel(request.Context()) - defer cancel() - func() { - listenerLock.Lock() - defer listenerLock.Unlock() - listeners = append(listeners, &listener{ - ListenerId: listenerId, - Write: func(buf []byte) { - ctx2, cancel2 := context.WithTimeout(ctx, time.Second*5) - defer cancel2() - if c.Write(ctx2, websocket.MessageText, buf) != nil { - cancel() - } - }, - Context: ctx, - Cancel: cancel, - }) - utils.Logf("WS", "Client %d attached", listenerId) - }() - defer c.Close(websocket.StatusInternalError, "closing") - - c.CloseRead(context.Background()) - select { - case <-ctx.Done(): - //wait - } - - }), - } - - go func() { - if err := server.ListenAndServe(); err != nil { - utils.Panic(err) - } - }() - - //remember last few template for events - sideBlockBuffer := NewSortedBuf[*index.SideBlock](int(p2api.Consensus().ChainWindowSize*2), index.SortSideBlock) - - //remember last few found main for events - foundBlockBuffer := NewSortedBuf[*index.FoundBlock](10, func(a, b *index.FoundBlock) int { - if a == b { - return 0 - } - if a == nil { - return -1 - } else if b == nil { - return 1 - } - - if a.MainBlock.Height < b.MainBlock.Height { - return -1 - } else if a.MainBlock.Height > b.MainBlock.Height { - return 1 - } - - if a.SideHeight < b.SideHeight { - return -1 - } else if a.SideHeight > b.SideHeight { - return 1 - } - //same height, sort by main id - return a.MainBlock.Id.Compare(b.MainBlock.Id) - }) - - //initialize - tip := indexDb.GetSideBlockTip() - for cur := tip; cur != nil; cur = indexDb.GetTipSideBlockByTemplateId(cur.ParentTemplateId) { - if (cur.EffectiveHeight + p2api.Consensus().ChainWindowSize) < tip.EffectiveHeight { - break - } - sideBlockBuffer.Insert(cur) - index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(cur.TemplateId), func(_ int, u *index.SideBlock) (stop bool) { - sideBlockBuffer.Insert(u) - return false - }) - } - - func() { - foundBlocks, _ := indexDb.GetFoundBlocks("", 5) - index.QueryIterate(foundBlocks, func(_ int, b *index.FoundBlock) (stop bool) { - foundBlockBuffer.Insert(b) - return false - }) - }() - - fillMainCoinbaseOutputs := func(outputs index.MainCoinbaseOutputs) (result index.MainCoinbaseOutputs) { - result = make(index.MainCoinbaseOutputs, 0, len(outputs)) - for _, output := range outputs { - miner := indexDb.GetMiner(output.Miner) - output.MinerAddress = miner.Address() - output.MinerAlias = miner.Alias() - result = append(result, output) - } - return result - } - - fillFoundBlockResult := func(foundBlock *index.FoundBlock) *index.FoundBlock { - miner := indexDb.GetMiner(foundBlock.Miner) - foundBlock.MinerAddress = miner.Address() - foundBlock.MinerAlias = miner.Alias() - return foundBlock - } - - var minerData *types2.MinerData - var minerDataLock sync.Mutex - getMinerData := func(expectedHeight uint64) *types2.MinerData { - minerDataLock.Lock() - defer minerDataLock.Unlock() - if minerData == nil || minerData.Height < expectedHeight { - minerData = p2api.MinerData() - } - return minerData - } - - fillSideBlockResult := func(mainTip *index.MainBlock, sideBlock *index.SideBlock) *index.SideBlock { - - miner := indexDb.GetMiner(sideBlock.Miner) - sideBlock.MinerAddress = miner.Address() - sideBlock.MinerAlias = miner.Alias() - - mainTipAtHeight := indexDb.GetMainBlockByHeight(sideBlock.MainHeight) - if mainTipAtHeight != nil { - sideBlock.MinedMainAtHeight = mainTipAtHeight.Id == sideBlock.MainId - sideBlock.MainDifficulty = mainTipAtHeight.Difficulty - } else { - minerData := getMinerData(sideBlock.MainHeight) - if minerData.Height == sideBlock.MainHeight { - sideBlock.MainDifficulty = minerData.Difficulty.Lo - } else if mainTip.Height == sideBlock.MainHeight { - sideBlock.MainDifficulty = mainTip.Difficulty - } - } - - index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(sideBlock.TemplateId), func(_ int, u *index.SideBlock) (stop bool) { - sideBlock.Uncles = append(sideBlock.Uncles, index.SideBlockUncleEntry{ - TemplateId: u.TemplateId, - Miner: u.Miner, - SideHeight: u.SideHeight, - Difficulty: u.Difficulty, - }) - return false - }) - return sideBlock - - } - - go func() { - var blocksToReport []*index.SideBlock - for range time.Tick(time.Second * 1) { - //reuse - blocksToReport = blocksToReport[:0] - - func() { - sideBlocksLock.RLock() - defer sideBlocksLock.RUnlock() - mainTip := indexDb.GetMainBlockTip() - tip := indexDb.GetSideBlockTip() - for cur := tip; cur != nil; cur = indexDb.GetTipSideBlockByTemplateId(cur.ParentTemplateId) { - if (cur.EffectiveHeight + p2api.Consensus().ChainWindowSize) < tip.EffectiveHeight { - break - } - var pushedNew bool - - index.QueryIterate(indexDb.GetSideBlocksByUncleOfId(cur.TemplateId), func(_ int, u *index.SideBlock) (stop bool) { - if sideBlockBuffer.Insert(u) { - //first time seen - pushedNew = true - blocksToReport = append(blocksToReport, fillSideBlockResult(mainTip, u)) - } - return false - }) - if sideBlockBuffer.Insert(cur) { - //first time seen - pushedNew = true - blocksToReport = append(blocksToReport, fillSideBlockResult(mainTip, cur)) - } - - if !pushedNew { - break - } - } - }() - - //sort for proper order - slices.SortFunc(blocksToReport, func(a, b *index.SideBlock) int { - if a.EffectiveHeight < b.EffectiveHeight { - return -1 - } else if a.EffectiveHeight > b.EffectiveHeight { - return 1 - } - if a.SideHeight < b.SideHeight { - return -1 - } else if a.SideHeight > b.SideHeight { - return 1 - } - //same height, sort by main id - return a.MainId.Compare(b.MainId) - }) - - func() { - listenerLock.RLock() - defer listenerLock.RUnlock() - for _, b := range blocksToReport { - buf, err := utils.MarshalJSON(&cmdutils.JSONEvent{ - Type: cmdutils.JSONEventSideBlock, - SideBlock: b, - FoundBlock: nil, - MainCoinbaseOutputs: nil, - }) - if err != nil { - continue - } - for _, l := range listeners { - select { - case <-l.Context.Done(): - default: - l.Write(buf) - } - } - - ts := time.Now().Unix() - - // Send webhooks - go func(b *index.SideBlock) { - q, _ := indexDb.GetMinerWebHooks(b.Miner) - index.QueryIterate(q, func(_ int, w *index.MinerWebHook) (stop bool) { - if err := cmdutils.SendSideBlock(w, ts, b.MinerAddress, b); err != nil { - utils.Logf("WebHook", "Error sending %s webhook to %s: type %s, url %s: %s", cmdutils.JSONEventSideBlock, b.MinerAddress.ToBase58(), w.Type, w.Url, err) - } - return false - }) - }(b) - } - }() - } - }() - - go func() { - var blocksToReport []*index.FoundBlock - var unfoundBlocksToReport []*index.SideBlock - for range time.Tick(time.Second * 1) { - //reuse - blocksToReport = blocksToReport[:0] - unfoundBlocksToReport = unfoundBlocksToReport[:0] - - func() { - sideBlocksLock.RLock() - defer sideBlocksLock.RUnlock() - mainTip := indexDb.GetMainBlockTip() - for _, m := range foundBlockBuffer.buf { - if m != nil && indexDb.GetMainBlockById(m.MainBlock.Id) == nil { - //unfound - if b := indexDb.GetSideBlockByMainId(m.MainBlock.Id); b != nil { - unfoundBlocksToReport = append(unfoundBlocksToReport, fillSideBlockResult(mainTip, indexDb.GetSideBlockByMainId(m.MainBlock.Id))) - foundBlockBuffer.Remove(m) - } - } - } - - func() { - foundBlocks, _ := indexDb.GetFoundBlocks("", 5) - index.QueryIterate(foundBlocks, func(_ int, b *index.FoundBlock) (stop bool) { - if foundBlockBuffer.Insert(b) { - //first time seen - blocksToReport = append(blocksToReport, fillFoundBlockResult(b)) - } - return false - }) - }() - }() - - //sort for proper order - slices.SortFunc(blocksToReport, func(a, b *index.FoundBlock) int { - if a.MainBlock.Height < b.MainBlock.Height { - return -1 - } else if a.MainBlock.Height > b.MainBlock.Height { - return 1 - } - if a.EffectiveHeight < b.EffectiveHeight { - return -1 - } else if a.EffectiveHeight > b.EffectiveHeight { - return 1 - } - if a.SideHeight < b.SideHeight { - return -1 - } else if a.SideHeight > b.SideHeight { - return 1 - } - //same height, sort by main id - return a.MainBlock.Id.Compare(b.MainBlock.Id) - }) - - //sort for proper order - slices.SortFunc(unfoundBlocksToReport, func(a, b *index.SideBlock) int { - if a.MainHeight < b.MainHeight { - return -1 - } else if a.MainHeight > b.MainHeight { - return 1 - } - - return index.SortSideBlock(a, b) - }) - - func() { - listenerLock.RLock() - defer listenerLock.RUnlock() - for _, b := range unfoundBlocksToReport { - buf, err := utils.MarshalJSON(&cmdutils.JSONEvent{ - Type: cmdutils.JSONEventOrphanedBlock, - SideBlock: b, - FoundBlock: nil, - MainCoinbaseOutputs: nil, - }) - if err != nil { - continue - } - for _, l := range listeners { - select { - case <-l.Context.Done(): - default: - l.Write(buf) - } - } - - ts := time.Now().Unix() - - // Send webhooks - go func(b *index.SideBlock) { - q, _ := indexDb.GetMinerWebHooks(b.Miner) - index.QueryIterate(q, func(_ int, w *index.MinerWebHook) (stop bool) { - if err := cmdutils.SendOrphanedBlock(w, ts, b.MinerAddress, b); err != nil { - utils.Logf("WebHook", "Error sending %s webhook to %s: type %s, url %s: %s", cmdutils.JSONEventOrphanedBlock, b.MinerAddress.ToBase58(), w.Type, w.Url, err) - } - return false - }) - }(b) - } - for _, b := range blocksToReport { - coinbaseOutputs := func() index.MainCoinbaseOutputs { - mainOutputs, err := indexDb.GetMainCoinbaseOutputs(b.MainBlock.CoinbaseId) - if err != nil { - panic(err) - } - defer mainOutputs.Close() - return fillMainCoinbaseOutputs(index.IterateToSliceWithoutPointer[index.MainCoinbaseOutput](mainOutputs)) - }() - - if len(coinbaseOutputs) == 0 { - //report next time - foundBlockBuffer.Remove(b) - continue - } - buf, err := utils.MarshalJSON(&cmdutils.JSONEvent{ - Type: cmdutils.JSONEventFoundBlock, - SideBlock: nil, - FoundBlock: b, - MainCoinbaseOutputs: coinbaseOutputs, - }) - if err != nil { - continue - } - for _, l := range listeners { - select { - case <-l.Context.Done(): - default: - l.Write(buf) - } - } - - includingHeight := max(b.EffectiveHeight, b.SideHeight) - if uint64(b.WindowDepth) > includingHeight { - includingHeight = 0 - } else { - includingHeight -= uint64(b.WindowDepth) - } - - // Send webhooks on outputs - for _, o := range coinbaseOutputs { - payout := &index.Payout{ - Miner: o.Miner, - TemplateId: b.MainBlock.SideTemplateId, - SideHeight: b.SideHeight, - UncleOf: b.UncleOf, - MainId: b.MainBlock.Id, - MainHeight: b.MainBlock.Height, - Timestamp: b.MainBlock.Timestamp, - CoinbaseId: b.MainBlock.CoinbaseId, - Reward: o.Value, - PrivateKey: b.MainBlock.CoinbasePrivateKey, - Index: uint64(o.Index), - GlobalOutputIndex: o.GlobalOutputIndex, - IncludingHeight: includingHeight, - } - - addr := o.MinerAddress - - ts := time.Now().Unix() - - // One goroutine per entry - go func() { - q, _ := indexDb.GetMinerWebHooks(payout.Miner) - index.QueryIterate(q, func(_ int, w *index.MinerWebHook) (stop bool) { - if err := cmdutils.SendFoundBlock(w, ts, addr, b, coinbaseOutputs); err != nil { - utils.Logf("WebHook", "Error sending %s webhook to %s: type %s, url %s: %s", cmdutils.JSONEventFoundBlock, addr.ToBase58(), w.Type, w.Url, err) - } - - if err := cmdutils.SendPayout(w, ts, addr, payout); err != nil { - utils.Logf("WebHook", "Error sending %s webhook to %s: type %s, url %s: %s", cmdutils.JSONEventPayout, addr.ToBase58(), w.Type, w.Url, err) - } - return false - }) - }() - } - } - }() - } - }() -} diff --git a/cmd/httputils/go.mod b/cmd/httputils/go.mod deleted file mode 100644 index 03d1ab8..0000000 --- a/cmd/httputils/go.mod +++ /dev/null @@ -1,18 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -require git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - -require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/floatdrop/lru v1.3.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/jxskiss/base62 v1.1.0 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/sys v0.17.0 // indirect -) - -replace github.com/goccy/go-json => github.com/WeebDataHoarder/go-json v0.0.0-20230730135821-d8f6463bb887 diff --git a/cmd/httputils/go.sum b/cmd/httputils/go.sum deleted file mode 100644 index 75ba25f..0000000 --- a/cmd/httputils/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -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/floatdrop/lru v1.3.0 h1:83abtaKjXcWrPmtzTAk2Ggq8DUKqI29YzrTrB8+vu0c= -github.com/floatdrop/lru v1.3.0/go.mod h1:83zlXKA06Bm32JImNINCiTr0ldadvdAjUe5jSwIaw0s= -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/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= -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= diff --git a/cmd/httputils/http.go b/cmd/httputils/http.go deleted file mode 100644 index 48034d7..0000000 --- a/cmd/httputils/http.go +++ /dev/null @@ -1,80 +0,0 @@ -package httputils - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "strings" -) - -func EncodeJson(r *http.Request, writer io.Writer, d any) error { - encoder := utils.NewJSONEncoder(writer) - if strings.Index(strings.ToLower(r.Header.Get("user-agent")), "mozilla") != -1 { - encoder.SetIndent("", " ") - } - return encoder.EncodeWithOption(d, utils.JsonEncodeOptions...) -} - -// StreamJsonChan Streams a channel of values into a JSON list via a writer. -func StreamJsonChan[T any](r *http.Request, writer io.Writer, stream <-chan T) error { - encoder := utils.NewJSONEncoder(writer) - if strings.Index(strings.ToLower(r.Header.Get("user-agent")), "mozilla") != -1 { - encoder.SetIndent("", " ") - } - // Write start of JSON list - _, _ = writer.Write([]byte{'[', 0xa}) - var count uint64 - defer func() { - // Write end of JSON list - _, _ = writer.Write([]byte{0xa, ']'}) - - // Empty channel - for range stream { - - } - }() - - for v := range stream { - if count > 0 { - // Write separator between list fields - _, _ = writer.Write([]byte{',', 0xa}) - } - if err := encoder.EncodeWithOption(v, utils.JsonEncodeOptions...); err != nil { - return err - } - count++ - } - return nil -} - -// StreamJsonIterator Streams an iterator of values into a JSON list via a writer. -func StreamJsonIterator[T any](r *http.Request, writer io.Writer, next func() (int, *T)) error { - encoder := utils.NewJSONEncoder(writer) - if strings.Index(strings.ToLower(r.Header.Get("user-agent")), "mozilla") != -1 { - encoder.SetIndent("", " ") - } - // Write start of JSON list - _, _ = writer.Write([]byte{'[', 0xa}) - var count uint64 - defer func() { - // Write end of JSON list - _, _ = writer.Write([]byte{0xa, ']'}) - }() - - for { - _, v := next() - if v == nil { - break - } - if count > 0 { - // Write separator between list fields - _, _ = writer.Write([]byte{',', 0xa}) - } - if err := encoder.EncodeWithOption(v, utils.JsonEncodeOptions...); err != nil { - return err - } - count++ - } - - return nil -} diff --git a/cmd/index/found_block.go b/cmd/index/found_block.go deleted file mode 100644 index e77ecb4..0000000 --- a/cmd/index/found_block.go +++ /dev/null @@ -1,36 +0,0 @@ -package index - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" -) - -type FoundBlock struct { - MainBlock MainBlock `json:"main_block"` - - SideHeight uint64 `json:"side_height"` - Miner uint64 `json:"miner"` - UncleOf types.Hash `json:"uncle_of,omitempty"` - EffectiveHeight uint64 `json:"effective_height"` - WindowDepth uint32 `json:"window_depth"` - WindowOutputs uint32 `json:"window_outputs"` - TransactionCount uint32 `json:"transaction_count"` - Difficulty uint64 `json:"difficulty"` - CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"` - Inclusion BlockInclusion `json:"inclusion"` - - // Extra information filled just for JSON purposes - MinerAddress *address.Address `json:"miner_address,omitempty"` - MinerAlias string `json:"miner_alias,omitempty"` -} - -func (b *FoundBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error { - if err := row.Scan( - &b.MainBlock.Id, &b.MainBlock.Height, &b.MainBlock.Timestamp, &b.MainBlock.Reward, &b.MainBlock.CoinbaseId, &b.MainBlock.CoinbasePrivateKey, &b.MainBlock.Difficulty, &b.MainBlock.SideTemplateId, - &b.SideHeight, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.Inclusion, - ); err != nil { - return err - } - return nil -} diff --git a/cmd/index/functions/count_estimate.sql b/cmd/index/functions/count_estimate.sql deleted file mode 100644 index 26f415d..0000000 --- a/cmd/index/functions/count_estimate.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE OR REPLACE FUNCTION count_estimate(query text) RETURNS integer AS $$ -DECLARE - rec record; - rows integer; -BEGIN - FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP - rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)'); - EXIT WHEN rows IS NOT NULL; - END LOOP; - RETURN rows; -END; -$$ LANGUAGE plpgsql VOLATILE STRICT; \ No newline at end of file diff --git a/cmd/index/go.mod b/cmd/index/go.mod deleted file mode 100644 index 1004cfe..0000000 --- a/cmd/index/go.mod +++ /dev/null @@ -1,32 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/sha3 v0.0.0-20230604092430-04fe7dc6439a - github.com/floatdrop/lru v1.3.0 - github.com/lib/pq v1.10.9 -) - -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 - 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/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 - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/index/go.sum b/cmd/index/go.sum deleted file mode 100644 index 66d7043..0000000 --- a/cmd/index/go.sum +++ /dev/null @@ -1,46 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/index/index.go b/cmd/index/index.go deleted file mode 100644 index 3a71e94..0000000 --- a/cmd/index/index.go +++ /dev/null @@ -1,1448 +0,0 @@ -package index - -import ( - "context" - "database/sql" - "embed" - "errors" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "github.com/lib/pq" - "io" - "path" - "reflect" - "regexp" - "slices" - "strings" - "sync" -) - -type Index struct { - consensus *sidechain.Consensus - - getDifficultyByHeight block.GetDifficultyByHeightFunc - getSeedByHeight block.GetSeedByHeightFunc - getByTemplateId sidechain.GetByTemplateIdFunc - derivationCache sidechain.DerivationCacheInterface - blockCache *lru.LRU[types.Hash, *sidechain.PoolBlock] - - handle *sql.DB - statements struct { - GetMinerById *sql.Stmt - GetMinerByAddress *sql.Stmt - GetMinerByAlias *sql.Stmt - InsertMiner *sql.Stmt - TipSideBlock *sql.Stmt - TipSideBlocksTemplateId *sql.Stmt - InsertOrUpdateSideBlock *sql.Stmt - TipMainBlock *sql.Stmt - GetMainBlockByHeight *sql.Stmt - GetMainBlockById *sql.Stmt - GetSideBlockByMainId *sql.Stmt - GetSideBlockByUncleId *sql.Stmt - } - caches struct { - minerLock sync.RWMutex - miner map[uint64]*Miner - } - - views map[string]string -} - -//go:embed schema.sql -var dbSchema string - -//go:embed functions/*.sql -var dbFunctions embed.FS - -func OpenIndex(connStr string, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getSeedByHeight block.GetSeedByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc) (index *Index, err error) { - index = &Index{ - consensus: consensus, - getDifficultyByHeight: difficultyByHeight, - getSeedByHeight: getSeedByHeight, - getByTemplateId: getByTemplateId, - derivationCache: sidechain.NewDerivationLRUCache(), - blockCache: lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)), - views: make(map[string]string), - } - if index.handle, err = sql.Open("postgres", connStr); err != nil { - return nil, err - } - - index.handle.SetMaxIdleConns(8) - - tx, err := index.handle.BeginTx(context.Background(), nil) - if err != nil { - return nil, err - } - defer tx.Rollback() - viewMatch := regexp.MustCompile("CREATE (MATERIALIZED |)VIEW ([^ \n\t]+?)(_v[0-9]+)? AS\n") - immv := regexp.MustCompile("SELECT (create_immv)\\('([^ \n\t']+?)(_v[0-9]+)?',") - for _, statement := range strings.Split(dbSchema, ";") { - var matches []string - if matches = viewMatch.FindStringSubmatch(statement); matches == nil { - matches = immv.FindStringSubmatch(statement) - } - if matches != nil { - isMaterialized := matches[1] == "MATERIALIZED " - isImmv := matches[1] == "create_immv" - viewName := matches[2] - fullViewName := viewName - //base view for materialized one - - if len(matches[3]) != 0 { - fullViewName = viewName + matches[3] - } - index.views[viewName] = fullViewName - if row, err := tx.Query(fmt.Sprintf("SELECT relname, relkind FROM pg_class WHERE relname LIKE '%s%%';", viewName)); err != nil { - return nil, err - } else { - var entries []struct{ n, kind string } - var exists bool - if err = func() error { - defer row.Close() - for row.Next() { - var n, kind string - if err = row.Scan(&n, &kind); err != nil { - return err - } - entries = append(entries, struct{ n, kind string }{n: n, kind: kind}) - } - return row.Err() - }(); err != nil { - return nil, err - } - for _, e := range entries { - if e.kind == "m" && fullViewName != e.n { - if _, err := tx.Exec(fmt.Sprintf("DROP MATERIALIZED VIEW %s CASCADE;", e.n)); err != nil { - return nil, err - } - } else if e.kind == "v" && fullViewName != e.n { - if _, err := tx.Exec(fmt.Sprintf("DROP VIEW %s CASCADE;", e.n)); err != nil { - return nil, err - } - } else if e.kind == "r" && fullViewName != e.n { - if _, err := tx.Exec(fmt.Sprintf("DROP TABLE %s CASCADE;", e.n)); err != nil { - return nil, err - } - } else if e.kind != "i" { - exists = true - } - } - - if !exists { - if _, err := tx.Exec(statement); err != nil { - return nil, err - } - - //Do first refresh - if isMaterialized { - if _, err := tx.Exec(fmt.Sprintf("REFRESH MATERIALIZED VIEW %s;", fullViewName)); err != nil { - return nil, err - } - } else if isImmv { - if _, err := tx.Exec(fmt.Sprintf("SELECT refresh_immv('%s', true);", fullViewName)); err != nil { - return nil, err - } - } - continue - } else { - continue - } - } - - } - if _, err := tx.Exec(statement); err != nil { - return nil, err - } - } - - if dir, err := dbFunctions.ReadDir("functions"); err != nil { - return nil, err - } else { - for _, e := range dir { - f, err := dbFunctions.Open(path.Join("functions", e.Name())) - if err != nil { - return nil, err - } - - var data []byte - func() { - defer f.Close() - data, err = io.ReadAll(f) - }() - if err != nil { - return nil, err - } - - if _, err := tx.Exec(string(data)); err != nil { - return nil, err - } - } - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - if index.statements.GetMinerById, err = index.handle.Prepare("SELECT " + MinerSelectFields + " FROM miners WHERE id = $1;"); err != nil { - return nil, err - } - if index.statements.GetMinerByAddress, err = index.handle.Prepare("SELECT " + MinerSelectFields + " FROM miners WHERE spend_public_key = $1 AND view_public_key = $2;"); err != nil { - return nil, err - } - if index.statements.GetMinerByAlias, err = index.handle.Prepare("SELECT " + MinerSelectFields + " FROM miners WHERE alias = $1;"); err != nil { - return nil, err - } - if index.statements.InsertMiner, err = index.handle.Prepare("INSERT INTO miners (spend_public_key, view_public_key) VALUES ($1, $2) RETURNING " + MinerSelectFields + ";"); err != nil { - return nil, err - } - if index.statements.TipSideBlocksTemplateId, err = index.PrepareSideBlocksByQueryStatement("WHERE template_id = $1 AND effective_height = side_height AND inclusion = $2;"); err != nil { - return nil, err - } - - if index.statements.InsertOrUpdateSideBlock, err = index.handle.Prepare("INSERT INTO side_blocks (" + SideBlockSelectFields + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) ON CONFLICT (main_id) DO UPDATE SET uncle_of = $7, effective_height = $8, inclusion = $21;"); err != nil { - return nil, err - } - - if index.statements.GetMainBlockByHeight, err = index.PrepareMainBlocksByQueryStatement("WHERE height = $1;"); err != nil { - return nil, err - } - - if index.statements.GetMainBlockById, err = index.PrepareMainBlocksByQueryStatement("WHERE id = $1;"); err != nil { - return nil, err - } - - if index.statements.GetSideBlockByMainId, err = index.PrepareSideBlocksByQueryStatement("WHERE main_id = $1;"); err != nil { - return nil, err - } - - if index.statements.GetSideBlockByUncleId, err = index.PrepareSideBlocksByQueryStatement("WHERE uncle_of = $1;"); err != nil { - return nil, err - } - - if index.statements.TipSideBlock, err = index.PrepareSideBlocksByQueryStatement("WHERE inclusion = $1 ORDER BY side_height DESC LIMIT 1;"); err != nil { - return nil, err - } - - if index.statements.TipMainBlock, err = index.PrepareMainBlocksByQueryStatement("ORDER BY height DESC LIMIT 1;"); err != nil { - return nil, err - } - - index.caches.miner = make(map[uint64]*Miner) - - return index, nil -} - -func (i *Index) GetDifficultyByHeight(height uint64) types.Difficulty { - if mb := i.GetMainBlockByHeight(height); mb != nil { - return types.DifficultyFrom64(mb.Difficulty) - } - return i.getDifficultyByHeight(height) -} - -func (i *Index) GetSeedByHeight(height uint64) types.Hash { - seedHeight := randomx.SeedHeight(height) - if mb := i.GetMainBlockByHeight(seedHeight); mb != nil { - return mb.Id - } - return i.getSeedByHeight(height) -} - -func (i *Index) GetByTemplateId(id types.Hash) *sidechain.PoolBlock { - if v := i.blockCache.Get(id); v != nil { - return *v - } else { - if b := i.getByTemplateId(id); b != nil { - i.blockCache.Set(id, b) - return b - } - } - - return nil -} - -func (i *Index) CachePoolBlock(b *sidechain.PoolBlock) { - i.blockCache.Set(b.SideTemplateId(i.consensus), b) -} - -func (i *Index) Consensus() *sidechain.Consensus { - return i.consensus -} - -func (i *Index) GetMiner(miner uint64) *Miner { - if m := func() *Miner { - i.caches.minerLock.RLock() - defer i.caches.minerLock.RUnlock() - return i.caches.miner[miner] - }(); m != nil { - return m - } else if m = i.getMiner(miner); m != nil { - i.caches.minerLock.Lock() - defer i.caches.minerLock.Unlock() - i.caches.miner[miner] = m - return m - } else { - return nil - } -} - -func (i *Index) getMiner(miner uint64) *Miner { - if rows, err := i.statements.GetMinerById.Query(miner); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } -} - -func (i *Index) GetMinerByAlias(alias string) *Miner { - if rows, err := i.statements.GetMinerByAlias.Query(alias); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } -} - -func (i *Index) GetMinerByStringAddress(addr string) *Miner { - minerAddr := address.FromBase58(addr) - if minerAddr != nil { - return i.GetMinerByAddress(minerAddr) - } - return nil -} - -func (i *Index) GetMinerByAddress(addr *address.Address) *Miner { - if network, _ := i.consensus.NetworkType.AddressNetwork(); network != addr.Network { - return nil - } - - spendPub, viewPub := addr.SpendPublicKey().AsBytes(), addr.ViewPublicKey().AsBytes() - if rows, err := i.statements.GetMinerByAddress.Query(&spendPub, &viewPub); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } -} - -func (i *Index) GetMinerByPackedAddress(addr address.PackedAddress) *Miner { - if rows, err := i.statements.GetMinerByAddress.Query(&addr[0], &addr[1]); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } -} - -func (i *Index) GetOrCreateMinerByAddress(addr *address.Address) *Miner { - if m := i.GetMinerByAddress(addr); m != nil { - return m - } else { - spendPub, viewPub := addr.SpendPublicKey().AsSlice(), addr.ViewPublicKey().AsSlice() - if rows, err := i.statements.InsertMiner.Query(&spendPub, &viewPub); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } - } -} - -func (i *Index) GetOrCreateMinerPackedAddress(addr address.PackedAddress) *Miner { - if m := i.GetMinerByPackedAddress(addr); m != nil { - return m - } else { - if rows, err := i.statements.InsertMiner.Query(&addr[0], &addr[1]); err != nil { - return nil - } else { - defer rows.Close() - return i.scanMiner(rows) - } - } -} - -func (i *Index) SetMinerAlias(minerId uint64, alias string) error { - miner := i.GetMiner(minerId) - if miner == nil { - return nil - } - if alias == "" { - if err := i.Query("UPDATE miners SET alias = NULL WHERE id = $1;", nil, miner.Id()); err != nil { - return err - } - miner.alias.String = "" - miner.alias.Valid = false - } else { - if err := i.Query("UPDATE miners SET alias = $2 WHERE id = $1;", nil, miner.Id(), alias); err != nil { - return err - } - miner.alias.String = alias - miner.alias.Valid = true - } - - return nil -} - -func (i *Index) GetView(k string) string { - return i.views[k] -} - -func (i *Index) Query(query string, callback func(row RowScanInterface) error, params ...any) error { - var parentError error - if stmt, err := i.handle.Prepare(query); err != nil { - parentError = err - } else { - defer stmt.Close() - parentError = i.QueryStatement(stmt, callback, params...) - } - return parentError -} - -func (i *Index) QueryStatement(stmt *sql.Stmt, callback func(row RowScanInterface) error, params ...any) error { - if rows, err := stmt.Query(params...); err != nil { - return err - } else { - defer rows.Close() - for callback != nil && rows.Next() { - //callback will call sql.Rows.Scan - if err = callback(rows); err != nil { - return err - } - } - - return nil - } -} - -func (i *Index) PrepareSideBlocksByQueryStatement(where string) (stmt *sql.Stmt, err error) { - return i.handle.Prepare(fmt.Sprintf("SELECT "+SideBlockSelectFields+" FROM side_blocks %s;", where)) -} - -func (i *Index) GetSideBlocksByQuery(where string, params ...any) (QueryIterator[SideBlock], error) { - if stmt, err := i.PrepareSideBlocksByQueryStatement(where); err != nil { - return nil, err - } else { - return i.getSideBlocksByQueryStatement(stmt, params...) - } -} - -func (i *Index) getSideBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[SideBlock], error) { - if r, err := queryStatement[SideBlock](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } -} - -func (i *Index) GetSideBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[SideBlock], error) { - if r, err := queryStatement[SideBlock](i, stmt, params...); err != nil { - return nil, err - } else { - return r, nil - } -} - -func (i *Index) PrepareMainBlocksByQueryStatement(where string) (stmt *sql.Stmt, err error) { - return i.handle.Prepare(fmt.Sprintf("SELECT "+MainBlockSelectFields+" FROM main_blocks %s;", where)) -} - -func (i *Index) GetShares(limit, minerId uint64, onlyBlocks bool, inclusion BlockInclusion) (QueryIterator[SideBlock], error) { - if limit == 0 { - if minerId != 0 { - if onlyBlocks { - return i.GetSideBlocksByQuery("WHERE miner = $1 AND uncle_of IS NULL AND inclusion = $2 ORDER BY side_height DESC;", minerId, inclusion) - } else { - return i.GetSideBlocksByQuery("WHERE miner = $1 AND inclusion = $2 ORDER BY side_height DESC, timestamp DESC;", minerId, inclusion) - } - } else { - if onlyBlocks { - return i.GetSideBlocksByQuery("WHERE uncle_of IS NULL AND inclusion = $1 ORDER BY side_height DESC;", inclusion) - } else { - return i.GetSideBlocksByQuery("ORDER BY side_height AND inclusion = $1 DESC, timestamp DESC;", inclusion) - } - } - } else { - if minerId != 0 { - if onlyBlocks { - return i.GetSideBlocksByQuery("WHERE miner = $1 AND uncle_of IS NULL AND inclusion = $3 ORDER BY side_height DESC LIMIT $2;", minerId, limit, inclusion) - } else { - return i.GetSideBlocksByQuery("WHERE miner = $1 AND inclusion = $3 ORDER BY side_height DESC, timestamp DESC LIMIT $2;", minerId, limit, inclusion) - } - } else { - if onlyBlocks { - return i.GetSideBlocksByQuery("WHERE uncle_of IS NULL AND inclusion = $2 ORDER BY side_height DESC LIMIT $1;", limit, inclusion) - } else { - return i.GetSideBlocksByQuery("WHERE inclusion = $2 ORDER BY side_height DESC, timestamp DESC LIMIT $1;", limit, inclusion) - } - } - } -} - -func (i *Index) GetFoundBlocks(where string, limit uint64, params ...any) (QueryIterator[FoundBlock], error) { - if stmt, err := i.handle.Prepare(fmt.Sprintf("SELECT * FROM "+i.views["found_main_blocks"]+" %s ORDER BY main_height DESC LIMIT %d;", where, limit)); err != nil { - return nil, err - } else { - if r, err := queryStatement[FoundBlock](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) GetMainBlocksByQuery(where string, params ...any) (QueryIterator[MainBlock], error) { - if stmt, err := i.PrepareMainBlocksByQueryStatement(where); err != nil { - return nil, err - } else { - return i.getMainBlocksByQueryStatement(stmt, params...) - } -} - -func (i *Index) getMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[MainBlock], error) { - if r, err := queryStatement[MainBlock](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } -} - -func (i *Index) GetMainBlocksByQueryStatement(stmt *sql.Stmt, params ...any) (QueryIterator[MainBlock], error) { - if r, err := queryStatement[MainBlock](i, stmt, params...); err != nil { - return nil, err - } else { - return r, nil - } -} - -func (i *Index) GetMainBlockById(id types.Hash) (b *MainBlock) { - if r, err := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockById, id[:]); err != nil { - utils.Print(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Print(r.Err()) - } - } - return b -} - -func (i *Index) GetMainBlockTip() (b *MainBlock) { - if r, err := i.GetMainBlocksByQueryStatement(i.statements.TipMainBlock); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetMainBlockByCoinbaseId(id types.Hash) (b *MainBlock) { - if r, err := i.GetMainBlocksByQuery("WHERE coinbase_id = $1;", id[:]); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetMainBlockByGlobalOutputIndex(globalOutputIndex uint64) (b *MainBlock) { - if r, err := i.GetMainBlocksByQuery("WHERE coinbase_id = (SELECT id FROM main_coinbase_outputs WHERE global_output_index = $1 LIMIT 1);", globalOutputIndex); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetMainBlockByHeight(height uint64) (b *MainBlock) { - if r, err := i.GetMainBlocksByQueryStatement(i.statements.GetMainBlockByHeight, height); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetSideBlockByMainId(id types.Hash) (b *SideBlock) { - if r, err := i.GetSideBlocksByQueryStatement(i.statements.GetSideBlockByMainId, id[:]); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetSideBlocksByTemplateId(id types.Hash) QueryIterator[SideBlock] { - if r, err := i.GetSideBlocksByQuery("WHERE template_id = $1;", id[:]); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) GetSideBlocksByUncleOfId(id types.Hash) QueryIterator[SideBlock] { - if r, err := i.GetSideBlocksByQueryStatement(i.statements.GetSideBlockByUncleId, id[:]); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) GetTipSideBlockByTemplateId(id types.Hash) (b *SideBlock) { - if r, err := i.GetSideBlocksByQueryStatement(i.statements.TipSideBlocksTemplateId, id[:], InclusionInVerifiedChain); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetSideBlocksByMainHeight(height uint64) QueryIterator[SideBlock] { - if r, err := i.GetSideBlocksByQuery("WHERE main_height = $1;", height); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) GetSideBlocksByHeight(height uint64) QueryIterator[SideBlock] { - if r, err := i.GetSideBlocksByQuery("WHERE side_height = $1;", height); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) GetTipSideBlockByHeight(height uint64) (b *SideBlock) { - if r, err := i.GetSideBlocksByQuery("WHERE side_height = $1 AND effective_height = $2 AND inclusion = $3;", height, height, InclusionInVerifiedChain); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetSideBlockTip() (b *SideBlock) { - if r, err := i.GetSideBlocksByQueryStatement(i.statements.TipSideBlock, InclusionInVerifiedChain); err != nil { - utils.Error(err) - } else { - defer r.Close() - if _, b = r.Next(); b == nil && r.Err() != nil { - utils.Error(r.Err()) - } - } - return b -} - -func (i *Index) GetSideBlocksInPPLNSWindow(tip *SideBlock) QueryIterator[SideBlock] { - return i.GetSideBlocksInWindow(tip.SideHeight, uint64(tip.WindowDepth)) -} - -func (i *Index) GetSideBlocksInWindow(startHeight, windowSize uint64) QueryIterator[SideBlock] { - if startHeight < windowSize { - windowSize = startHeight - } - - if r, err := i.GetSideBlocksByQuery("WHERE effective_height <= $1 AND effective_height > $2 AND inclusion = $3 ORDER BY effective_height DESC, side_height DESC;", startHeight, startHeight-windowSize, InclusionInVerifiedChain); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) GetSideBlocksByMinerIdInWindow(minerId, startHeight, windowSize uint64) QueryIterator[SideBlock] { - if startHeight < windowSize { - windowSize = startHeight - } - - if r, err := i.GetSideBlocksByQuery("WHERE miner = $1 AND effective_height <= $2 AND effective_height > $3 AND inclusion = $4 ORDER BY effective_height DESC, side_height DESC;", minerId, startHeight, startHeight-windowSize, InclusionInVerifiedChain); err != nil { - utils.Error(err) - } else { - return r - } - return nil -} - -func (i *Index) InsertOrUpdateSideBlock(b *SideBlock) error { - if b.IsTipOfHeight() { - if oldBlock := i.GetTipSideBlockByHeight(b.SideHeight); oldBlock != nil { - if oldBlock.MainId != b.MainId { - //conflict resolution, change other block status - if oldBlock.TemplateId == oldBlock.TemplateId { - oldBlock.Inclusion = InclusionAlternateInVerifiedChain - } else { - //mark as orphan if templates don't match. if uncle it will be adjusted later - oldBlock.Inclusion = InclusionOrphan - } - if err := i.InsertOrUpdateSideBlock(oldBlock); err != nil { - return err - } - } - } - } - - var parentId any = &b.ParentTemplateId - if b.SideHeight == 0 { - parentId = types.ZeroHash[:] - } - - return i.QueryStatement( - i.statements.InsertOrUpdateSideBlock, - nil, - &b.MainId, - b.MainHeight, - &b.TemplateId, - b.SideHeight, - parentId, - b.Miner, - &b.UncleOf, - b.EffectiveHeight, - b.Nonce, - b.ExtraNonce, - b.Timestamp, - b.SoftwareId, - b.SoftwareVersion, - b.WindowDepth, - b.WindowOutputs, - b.TransactionCount, - b.Difficulty, - &b.CumulativeDifficulty, - b.PowDifficulty, - &b.PowHash, - b.Inclusion, - ) -} - -func (i *Index) InsertOrUpdateMainBlock(b *MainBlock) error { - if oldBlock := i.GetMainBlockByHeight(b.Height); oldBlock != nil { - if oldBlock.Id != b.Id { - //conflict resolution - if tx, err := i.handle.BeginTx(context.Background(), nil); err != nil { - return err - } else { - defer tx.Rollback() - if _, err := tx.Exec("DELETE FROM main_coinbase_outputs WHERE id = $1;", oldBlock.CoinbaseId[:]); err != nil { - return err - } - - if _, err = tx.Exec("DELETE FROM main_blocks WHERE id = $1;", oldBlock.Id[:]); err != nil { - return err - } - - if err = tx.Commit(); err != nil { - return err - } - } - } - } - - metadataJson, _ := utils.MarshalJSON(b.Metadata) - - if tx, err := i.handle.BeginTx(context.Background(), nil); err != nil { - return err - } else { - defer tx.Rollback() - if _, err := tx.Exec( - "INSERT INTO main_blocks (id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, coinbase_private_key) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9) ON CONFLICT (id) DO UPDATE SET metadata = $7, side_template_id = $8, coinbase_private_key = $9;", - b.Id[:], - b.Height, - b.Timestamp, - b.Reward, - b.CoinbaseId[:], - b.Difficulty, - metadataJson, - &b.SideTemplateId, - &b.CoinbasePrivateKey, - ); err != nil { - return err - } - - return tx.Commit() - } -} - -func (i *Index) GetPayoutsByMinerId(minerId uint64, limit uint64) (r QueryIterator[Payout], err error) { - var stmt *sql.Stmt - var params []any - - if limit == 0 { - if stmt, err = i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 ORDER BY main_height DESC;"); err != nil { - return nil, err - } - params = []any{minerId} - } else { - if stmt, err = i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 ORDER BY main_height DESC LIMIT $2;"); err != nil { - return nil, err - } - params = []any{minerId, limit} - } - - if r, err := queryStatement[Payout](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } -} - -func (i *Index) GetPayoutsByMinerIdFromHeight(minerId uint64, height uint64) (QueryIterator[Payout], error) { - if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND main_height >= $2 ORDER BY main_height DESC;"); err != nil { - return nil, err - } else { - if r, err := queryStatement[Payout](i, stmt, minerId, height); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) GetPayoutsByMinerIdFromTimestamp(minerId uint64, timestamp uint64) (QueryIterator[Payout], error) { - if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND timestamp >= $2 ORDER BY main_height DESC;"); err != nil { - return nil, err - } else { - if r, err := queryStatement[Payout](i, stmt, minerId, timestamp); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) GetPayoutsBySideBlock(b *SideBlock) (QueryIterator[Payout], error) { - if stmt, err := i.handle.Prepare("SELECT * FROM " + i.views["payouts"] + " WHERE miner = $1 AND ((side_height >= $2 AND including_height <= $2) OR main_id = $3) ORDER BY main_height DESC;"); err != nil { - return nil, err - } else { - if r, err := queryStatement[Payout](i, stmt, b.Miner, b.EffectiveHeight, &b.MainId); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) GetMainCoinbaseOutputs(coinbaseId types.Hash) (QueryIterator[MainCoinbaseOutput], error) { - if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE id = $1 ORDER BY index ASC;"); err != nil { - return nil, err - } else { - if r, err := queryStatement[MainCoinbaseOutput](i, stmt, coinbaseId[:]); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) GetMainCoinbaseOutputByIndex(coinbaseId types.Hash, index uint64) (o *MainCoinbaseOutput) { - if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE id = $1 AND index = $2 ORDER BY index ASC;"); err != nil { - utils.Error(err) - return nil - } else { - if r, err := queryStatement[MainCoinbaseOutput](i, stmt, coinbaseId[:], index); err != nil { - utils.Error(err) - return nil - } else { - defer r.Close() - r.closer = func() { - stmt.Close() - } - defer r.Close() - if _, o = r.Next(); o == nil && r.Err() != nil { - utils.Error(r.Err()) - } - return o - } - } -} - -func (i *Index) GetMainCoinbaseOutputByGlobalOutputIndex(globalOutputIndex uint64) (o *MainCoinbaseOutput) { - if stmt, err := i.handle.Prepare("SELECT " + MainCoinbaseOutputSelectFields + " FROM main_coinbase_outputs WHERE global_output_index = $1 ORDER BY index ASC;"); err != nil { - utils.Error(err) - return nil - } else { - if r, err := queryStatement[MainCoinbaseOutput](i, stmt, globalOutputIndex); err != nil { - utils.Error(err) - return nil - } else { - defer r.Close() - r.closer = func() { - stmt.Close() - } - defer r.Close() - if _, o = r.Next(); o == nil && r.Err() != nil { - utils.Error(r.Err()) - } - return o - } - } -} - -func (i *Index) GetMainLikelySweepTransactions(limit uint64) (r QueryIterator[MainLikelySweepTransaction], err error) { - var stmt *sql.Stmt - var params []any - - if limit > 0 { - if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions ORDER BY timestamp DESC LIMIT $1;"); err != nil { - return nil, err - } - params = []any{limit} - } else { - if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions ORDER BY timestamp DESC;"); err != nil { - return nil, err - } - params = []any{} - } - - if r, err := queryStatement[MainLikelySweepTransaction](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } -} - -func (i *Index) GetMainLikelySweepTransactionsByAddress(addr *address.Address, limit uint64) (r QueryIterator[MainLikelySweepTransaction], err error) { - var stmt *sql.Stmt - var params []any - - spendPub, viewPub := addr.SpendPublicKey().AsSlice(), addr.ViewPublicKey().AsSlice() - - if limit > 0 { - if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC LIMIT $3;"); err != nil { - return nil, err - } - params = []any{&spendPub, &viewPub, limit} - } else { - if stmt, err = i.handle.Prepare("SELECT " + MainLikelySweepTransactionSelectFields + " FROM main_likely_sweep_transactions WHERE miner_spend_public_key = $1 AND miner_view_public_key = $2 ORDER BY timestamp DESC;"); err != nil { - return nil, err - } - params = []any{&spendPub, &viewPub} - } - - if r, err := queryStatement[MainLikelySweepTransaction](i, stmt, params...); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } -} - -type TransactionInputQueryResult struct { - Input client.TransactionInput `json:"input"` - MatchedOutputs []*MatchedOutput `json:"matched_outputs"` -} - -type MatchedOutput struct { - Coinbase *MainCoinbaseOutput `json:"coinbase,omitempty"` - Sweep *MainLikelySweepTransaction `json:"sweep,omitempty"` - GlobalOutputIndex uint64 `json:"global_output_index"` - Timestamp uint64 `json:"timestamp"` - Address *address.Address `json:"address"` -} - -type MinimalTransactionInputQueryResult struct { - Input client.TransactionInput `json:"input"` - MatchedOutputs []*MinimalMatchedOutput `json:"matched_outputs"` -} - -type MinimalMatchedOutput struct { - Coinbase types.Hash `json:"coinbase,omitempty"` - Sweep types.Hash `json:"sweep,omitempty"` - GlobalOutputIndex uint64 `json:"global_output_index"` - Address *address.Address `json:"address"` -} - -type MinimalTransactionInputQueryResults []MinimalTransactionInputQueryResult -type TransactionInputQueryResults []TransactionInputQueryResult - -type TransactionInputQueryResultsMatch struct { - Address *address.Address `json:"address"` - Count uint64 `json:"count"` - SweepCount uint64 `json:"sweep_count"` - CoinbaseCount uint64 `json:"coinbase_count"` - CoinbaseAmount uint64 `json:"coinbase_amount"` -} -type TransactionInputQueryResultsMatches []TransactionInputQueryResultsMatch - -func (r TransactionInputQueryResults) Match() (result TransactionInputQueryResultsMatches) { - //cannot have more than one of same miner outputs valid per input - //no miner outputs in whole input doesn't count - //cannot take vsame exact miner outputs on different inputs - //TODO - - var zeroAddress address.PackedAddress - miners := make(map[address.PackedAddress]*TransactionInputQueryResultsMatch) - miners[zeroAddress] = &TransactionInputQueryResultsMatch{ - Address: nil, - Count: 0, - SweepCount: 0, - CoinbaseCount: 0, - CoinbaseAmount: 0, - } - - //TODO: handle same miner multiple times in same decoy - - for _, matchResult := range r { - for _, o := range matchResult.MatchedOutputs { - if o != nil { - pA := o.Address.ToPackedAddress() - if _, ok := miners[pA]; !ok { - miners[pA] = &TransactionInputQueryResultsMatch{ - Address: o.Address, - Count: 0, - SweepCount: 0, - CoinbaseCount: 0, - CoinbaseAmount: 0, - } - } - - if o.Coinbase != nil { - miners[pA].CoinbaseCount++ - miners[pA].CoinbaseAmount += o.Coinbase.Value - } else if o.Sweep != nil { - miners[pA].SweepCount++ - } - miners[pA].Count++ - } else { - miners[zeroAddress].Count++ - } - } - } - - result = make([]TransactionInputQueryResultsMatch, 0, len(miners)) - - for _, v := range miners { - result = append(result, *v) - } - - slices.SortFunc(result, func(a, b TransactionInputQueryResultsMatch) int { - return int(b.Count) - int(a.Count) - }) - - return result -} - -func (i *Index) QueryTransactionInputs(inputs []client.TransactionInput) TransactionInputQueryResults { - result := make(TransactionInputQueryResults, len(inputs)) - for index, input := range inputs { - result[index].Input = input - result[index].MatchedOutputs = make([]*MatchedOutput, len(input.KeyOffsets)) - if input.Amount != 0 { - continue - } - result[index].MatchedOutputs = i.QueryGlobalOutputIndices(input.KeyOffsets) - } - return result -} - -func (i *Index) QueryGlobalOutputIndices(indices []uint64) []*MatchedOutput { - result := make([]*MatchedOutput, len(indices)) - - if err := i.Query("SELECT "+MainCoinbaseOutputSelectFields+" FROM main_coinbase_outputs WHERE global_output_index = ANY($1) ORDER BY index ASC;", func(row RowScanInterface) error { - var o MainCoinbaseOutput - if err := o.ScanFromRow(i.Consensus(), row); err != nil { - return err - } - if index := slices.Index(indices, o.GlobalOutputIndex); index != -1 { - result[index] = &MatchedOutput{ - Coinbase: &o, - Sweep: nil, - GlobalOutputIndex: o.GlobalOutputIndex, - Address: i.GetMiner(o.Miner).Address(), - } - if mb := i.GetMainBlockByCoinbaseId(o.Id); mb != nil { - result[index].Timestamp = mb.Timestamp - } - } - return nil - }, pq.Array(indices)); err != nil { - return nil - } - - if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions WHERE $1::bigint[] && global_output_indices ORDER BY timestamp ASC;", func(row RowScanInterface) error { - var tx MainLikelySweepTransaction - if err := tx.ScanFromRow(i.Consensus(), row); err != nil { - return err - } - for _, globalOutputIndex := range tx.GlobalOutputIndices { - // fill all possible indices - if index := slices.Index(indices, globalOutputIndex); index != -1 { - if result[index] == nil { - result[index] = &MatchedOutput{ - Coinbase: nil, - Sweep: &tx, - GlobalOutputIndex: globalOutputIndex, - Timestamp: tx.Timestamp, - Address: tx.Address, - } - } - } - } - return nil - }, pq.Array(indices)); err != nil { - return nil - } - return result -} - -func (i *Index) GetMainLikelySweepTransactionBySpendingGlobalOutputIndices(globalOutputIndices ...uint64) [][]*MainLikelySweepTransaction { - entries := make([][]*MainLikelySweepTransaction, len(globalOutputIndices)) - if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions WHERE $1::bigint[] && spending_output_indices ORDER BY timestamp ASC;", func(row RowScanInterface) error { - var tx MainLikelySweepTransaction - if err := tx.ScanFromRow(i.Consensus(), row); err != nil { - return err - } - for _, globalOutputIndex := range tx.SpendingOutputIndices { - // fill all possible indices - if index := slices.Index(globalOutputIndices, globalOutputIndex); index != -1 { - entries[index] = append(entries[index], &tx) - } - } - return nil - }, pq.Array(globalOutputIndices)); err != nil { - return nil - } - return entries -} - -func (i *Index) GetMainLikelySweepTransactionByGlobalOutputIndices(globalOutputIndices ...uint64) []*MainLikelySweepTransaction { - entries := make([]*MainLikelySweepTransaction, len(globalOutputIndices)) - if err := i.Query("SELECT "+MainLikelySweepTransactionSelectFields+" FROM main_likely_sweep_transactions WHERE $1::bigint[] && global_output_indices ORDER BY timestamp ASC;", func(row RowScanInterface) error { - var tx MainLikelySweepTransaction - if err := tx.ScanFromRow(i.Consensus(), row); err != nil { - return err - } - for _, globalOutputIndex := range tx.GlobalOutputIndices { - // fill all possible indices - if index := slices.Index(globalOutputIndices, globalOutputIndex); index != -1 { - if entries[index] == nil { - entries[index] = &tx - } - } - } - return nil - }, pq.Array(globalOutputIndices)); err != nil { - return nil - } - return entries -} - -func (i *Index) InsertOrUpdateMainLikelySweepTransaction(t *MainLikelySweepTransaction) error { - - resultJson, _ := utils.MarshalJSON(t.Result) - matchJson, _ := utils.MarshalJSON(t.Match) - spendPub, viewPub := t.Address.SpendPublicKey().AsSlice(), t.Address.ViewPublicKey().AsSlice() - - if _, err := i.handle.Exec( - "INSERT INTO main_likely_sweep_transactions (id, timestamp, result, match, value, spending_output_indices, global_output_indices, input_count, input_decoy_count, miner_count, other_miners_count, no_miner_count, miner_ratio, other_miners_ratio, no_miner_ratio, miner_spend_public_key, miner_view_public_key) VALUES ($1, $2, $3, $4, $5, $6::bigint[], $7::bigint[], $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (id) DO UPDATE SET result = $3, match = $4, value = $5, miner_count = $10, other_miners_count = $11, no_miner_count = $12, miner_ratio = $13, other_miners_ratio = $14, no_miner_ratio = $15, miner_spend_public_key = $16, miner_view_public_key = $17;", - t.Id[:], - t.Timestamp, - resultJson, - matchJson, - t.Value, - pq.Array(t.SpendingOutputIndices), - pq.Array(t.GlobalOutputIndices), - t.InputCount, - t.InputDecoyCount, - t.MinerCount, - t.OtherMinersCount, - t.NoMinerCount, - t.MinerRatio, - t.OtherMinersRatio, - t.NoMinerRatio, - &spendPub, - &viewPub, - ); err != nil { - return err - } - - return nil -} - -func (i *Index) GetMainCoinbaseOutputByMinerId(coinbaseId types.Hash, minerId uint64) *MainCoinbaseOutput { - var output MainCoinbaseOutput - if err := i.Query("SELECT "+MainCoinbaseOutputSelectFields+" FROM main_coinbase_outputs WHERE id = $1 AND miner = $2 ORDER BY index DESC;", func(row RowScanInterface) error { - if err := output.ScanFromRow(i.Consensus(), row); err != nil { - return err - } - return nil - }, coinbaseId[:], minerId); err != nil { - return nil - } - if output.Id == types.ZeroHash { - return nil - } - return &output -} - -func (i *Index) InsertOrUpdateMainCoinbaseOutputs(outputs MainCoinbaseOutputs) error { - if len(outputs) == 0 { - return nil - } - for ix, o := range outputs[1:] { - if outputs[ix].Id != o.Id { - return errors.New("differing coinbase ids") - } - } - - if tx, err := i.handle.BeginTx(context.Background(), nil); err != nil { - return err - } else { - defer tx.Rollback() - for _, o := range outputs { - if _, err := tx.Exec( - "INSERT INTO main_coinbase_outputs (id, index, global_output_index, miner, value) VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING;", - o.Id[:], - o.Index, - o.GlobalOutputIndex, - o.Miner, - o.Value, - ); err != nil { - return err - } - } - return tx.Commit() - } -} - -func (i *Index) Close() error { - //cleanup statements - v := reflect.ValueOf(i.statements) - for ix := 0; ix < v.NumField(); ix++ { - if stmt, ok := v.Field(ix).Interface().(*sql.Stmt); ok && stmt != nil { - //v.Field(i).Elem().Set(reflect.ValueOf((*sql.Stmt)(nil))) - stmt.Close() - } - } - - return i.handle.Close() -} - -func (i *Index) scanMiner(rows *sql.Rows) *Miner { - if rows.Next() { - m := &Miner{} - if m.ScanFromRow(i.Consensus(), rows) == nil { - return m - } - } - return nil -} - -func (i *Index) GetSideBlockFromPoolBlock(b *sidechain.PoolBlock, inclusion BlockInclusion) (tip *SideBlock, uncles []*SideBlock, err error) { - if err = i.preProcessPoolBlock(b); err != nil { - return nil, nil, err - } - tip = &SideBlock{} - if err = tip.FromPoolBlock(i, b, i.GetSeedByHeight); err != nil { - return nil, nil, err - } - tip.EffectiveHeight = tip.SideHeight - tip.Inclusion = inclusion - - if bottomHeight, err := sidechain.BlocksInPPLNSWindow(b, i.consensus, i.GetDifficultyByHeight, i.GetByTemplateId, func(b *sidechain.PoolBlock, weight types.Difficulty) { - - }); err != nil { - // unknown - tip.WindowDepth = 0 - } else { - tip.WindowDepth = uint32(tip.SideHeight - bottomHeight + 1) - } - - for _, u := range b.Side.Uncles { - uncleBlock := i.GetTipSideBlockByTemplateId(u) - uncle := i.GetByTemplateId(u) - if err = i.preProcessPoolBlock(uncle); err != nil { - return nil, nil, err - } - if uncleBlock == nil && uncle != nil { - uncleBlock = &SideBlock{} - if err = uncleBlock.FromPoolBlock(i, uncle, i.GetSeedByHeight); err != nil { - return nil, nil, err - } - if tip.Inclusion == InclusionOrphan { - uncleBlock.Inclusion = InclusionOrphan - } else if tip.Inclusion == InclusionInVerifiedChain || tip.Inclusion == InclusionAlternateInVerifiedChain { - uncleBlock.Inclusion = InclusionInVerifiedChain - } - } - if uncleBlock == nil || uncle == nil { - return nil, nil, errors.New("nil uncle") - } - if tip.Inclusion == InclusionInVerifiedChain || tip.Inclusion == InclusionAlternateInVerifiedChain { - uncleBlock.UncleOf = tip.TemplateId - uncleBlock.EffectiveHeight = tip.EffectiveHeight - uncleBlock.Inclusion = InclusionInVerifiedChain - } - - if uncleBottomHeight, err := sidechain.BlocksInPPLNSWindow(uncle, i.consensus, i.GetDifficultyByHeight, i.GetByTemplateId, func(b *sidechain.PoolBlock, weight types.Difficulty) { - - }); err != nil { - // unknown - uncleBlock.WindowDepth = 0 - } else { - uncleBlock.WindowDepth = uint32(uncleBlock.SideHeight - uncleBottomHeight + 1) - } - uncles = append(uncles, uncleBlock) - } - - return tip, uncles, nil -} - -func (i *Index) preProcessPoolBlock(b *sidechain.PoolBlock) error { - if b == nil { - return errors.New("nil block") - } - var preAllocatedShares sidechain.Shares - if len(b.Main.Coinbase.Outputs) == 0 { - //cannot use SideTemplateId() as it might not be proper to calculate yet. fetch from coinbase only here - if b2 := i.GetByTemplateId(types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId))); b2 != nil && len(b2.Main.Coinbase.Outputs) != 0 { - b.Main.Coinbase.Outputs = b2.Main.Coinbase.Outputs - } else { - preAllocatedShares = sidechain.PreAllocateShares(i.consensus.ChainWindowSize * 2) - } - } - - _, err := b.PreProcessBlock(i.consensus, i.derivationCache, preAllocatedShares, i.GetDifficultyByHeight, i.GetByTemplateId) - return err -} - -func (i *Index) DerivationCache() sidechain.DerivationCacheInterface { - return i.derivationCache -} - -func (i *Index) InsertOrUpdatePoolBlock(b *sidechain.PoolBlock, inclusion BlockInclusion) error { - sideBlock, sideUncles, err := i.GetSideBlockFromPoolBlock(b, inclusion) - if err != nil { - return err - } - - if err := i.InsertOrUpdateSideBlock(sideBlock); err != nil { - return err - } - - for _, sideUncle := range sideUncles { - if err := i.InsertOrUpdateSideBlock(sideUncle); err != nil { - return err - } - } - - return nil -} - -func (i *Index) GetMinerWebHooks(minerId uint64) (QueryIterator[MinerWebHook], error) { - if stmt, err := i.handle.Prepare("SELECT miner, type, url, settings FROM miner_webhooks WHERE miner = $1;"); err != nil { - return nil, err - } else { - if r, err := queryStatement[MinerWebHook](i, stmt, minerId); err != nil { - defer stmt.Close() - return nil, err - } else { - r.closer = func() { - stmt.Close() - } - return r, nil - } - } -} - -func (i *Index) DeleteMinerWebHook(minerId uint64, hookType WebHookType) error { - return i.Query("DELETE FROM miner_webhooks WHERE miner = $1 AND type = $2", func(row RowScanInterface) error { - return nil - }, minerId, hookType) -} - -func (i *Index) InsertOrUpdateMinerWebHook(w *MinerWebHook) error { - metadataJson, _ := utils.MarshalJSON(w.Settings) - - if w.Settings == nil { - metadataJson = []byte{'{', '}'} - } - - if tx, err := i.handle.BeginTx(context.Background(), nil); err != nil { - return err - } else { - defer tx.Rollback() - if _, err := tx.Exec( - "INSERT INTO miner_webhooks (miner, type, url, settings) VALUES ($1, $2, $3, $4::jsonb) ON CONFLICT (miner, type) DO UPDATE SET url = $3, settings = $4;", - w.Miner, - w.Type, - w.Url, - metadataJson, - ); err != nil { - return err - } - - return tx.Commit() - } -} diff --git a/cmd/index/iterator.go b/cmd/index/iterator.go deleted file mode 100644 index 0580bc8..0000000 --- a/cmd/index/iterator.go +++ /dev/null @@ -1,66 +0,0 @@ -package index - -type IterateFunction[K, V any] func(key K, value V) (stop bool) - -type IteratorFunction[K, V any] func(f IterateFunction[K, V]) (complete bool) - -type Iterator[K, V any] interface { - All(f IterateFunction[K, V]) (complete bool) -} - -func IteratorToIterator[K, V any](i Iterator[K, V], _ ...error) Iterator[K, V] { - return i -} - -func IterateToSlice[T any](i Iterator[int, T], _ ...error) (s []T) { - i.All(func(key int, value T) (stop bool) { - s = append(s, value) - return false - }) - return s -} - -func IterateToSliceWithoutPointer[T any](i Iterator[int, *T], _ ...error) (s []T) { - i.All(func(key int, value *T) (stop bool) { - s = append(s, *value) - return false - }) - return s -} - -func SliceIterate[S ~[]T, T any](s S, f IterateFunction[int, T]) (complete bool) { - for i, v := range s { - if f(i, v) { - return (len(s) - 1) == i - } - } - return true -} - -func ChanIterate[T any](c <-chan T, f IterateFunction[int, T]) (complete bool) { - var i int - for { - v, more := <-c - if !more { - return true - } - if f(i, v) { - return false - } - i++ - } -} - -func ChanConsume[T any](c <-chan T) { - for range c { - - } -} - -func ChanToSlice[S ~[]T, T any](c <-chan T) (s S) { - s = make(S, 0, len(c)) - for v := range c { - s = append(s, v) - } - return s -} diff --git a/cmd/index/main_block.go b/cmd/index/main_block.go deleted file mode 100644 index 6fe8578..0000000 --- a/cmd/index/main_block.go +++ /dev/null @@ -1,47 +0,0 @@ -package index - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" -) - -const MainBlockSelectFields = "id, height, timestamp, reward, coinbase_id, difficulty, metadata, side_template_id, coinbase_private_key" - -type MainBlock struct { - Id types.Hash `json:"id"` - Height uint64 `json:"height"` - Timestamp uint64 `json:"timestamp"` - Reward uint64 `json:"reward"` - CoinbaseId types.Hash `json:"coinbase_id"` - Difficulty uint64 `json:"difficulty"` - - // Metadata should be jsonb blob, can be NULL. metadata such as pool ownership, links to other p2pool networks, and other interesting data - Metadata map[string]any `json:"metadata,omitempty"` - - // sidechain data for blocks we own - // SideTemplateId can be NULL - SideTemplateId types.Hash `json:"side_template_id,omitempty"` - // CoinbasePrivateKey private key for coinbase outputs we own (all 0x00 = not known, but should have one) - CoinbasePrivateKey crypto.PrivateKeyBytes `json:"coinbase_private_key,omitempty"` -} - -func (b *MainBlock) GetMetadata(key string) any { - return b.Metadata[key] -} - -func (b *MainBlock) SetMetadata(key string, v any) { - b.Metadata[key] = v -} - -func (b *MainBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error { - var metadataBuf []byte - b.Metadata = make(map[string]any) - if err := row.Scan(&b.Id, &b.Height, &b.Timestamp, &b.Reward, &b.CoinbaseId, &b.Difficulty, &metadataBuf, &b.SideTemplateId, &b.CoinbasePrivateKey); err != nil { - return err - } else if err = utils.UnmarshalJSON(metadataBuf, &b.Metadata); err != nil { - return err - } - return nil -} diff --git a/cmd/index/main_coinbase_output.go b/cmd/index/main_coinbase_output.go deleted file mode 100644 index 5d4a6ae..0000000 --- a/cmd/index/main_coinbase_output.go +++ /dev/null @@ -1,37 +0,0 @@ -package index - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" -) - -type MainCoinbaseOutputs []MainCoinbaseOutput - -const MainCoinbaseOutputSelectFields = "id, index, global_output_index, miner, value" - -type MainCoinbaseOutput struct { - // Id coinbase id - Id types.Hash `json:"id"` - // Index transaction output index - Index uint32 `json:"index"` - // Monero global output idx - GlobalOutputIndex uint64 `json:"global_output_index"` - - // Miner owner of the output - Miner uint64 `json:"miner"` - - Value uint64 `json:"value"` - - // Extra information filled just for JSON purposes - - MinerAddress *address.Address `json:"miner_address,omitempty"` - MinerAlias string `json:"miner_alias,omitempty"` -} - -func (o *MainCoinbaseOutput) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error { - if err := row.Scan(&o.Id, &o.Index, &o.GlobalOutputIndex, &o.Miner, &o.Value); err != nil { - return err - } - return nil -} diff --git a/cmd/index/main_likely_sweep_transaction.go b/cmd/index/main_likely_sweep_transaction.go deleted file mode 100644 index df58c9b..0000000 --- a/cmd/index/main_likely_sweep_transaction.go +++ /dev/null @@ -1,66 +0,0 @@ -package index - -import ( - "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/lib/pq" -) - -const MainLikelySweepTransactionSelectFields = "id, timestamp, result, match, value, spending_output_indices, global_output_indices, input_count, input_decoy_count, miner_count, other_miners_count, no_miner_count, miner_ratio, other_miners_ratio, no_miner_ratio, miner_spend_public_key, miner_view_public_key" - -type MainLikelySweepTransaction struct { - // Id coinbase id - Id types.Hash `json:"id"` - Timestamp uint64 `json:"timestamp"` - Result MinimalTransactionInputQueryResults `json:"result"` - Match []TransactionInputQueryResultsMatch `json:"match"` - Value uint64 `json:"value"` - SpendingOutputIndices []uint64 `json:"spending_output_indices"` - GlobalOutputIndices []uint64 `json:"global_output_indices"` - - InputCount int `json:"input_count"` - InputDecoyCount int `json:"input_decoy_count"` - - MinerCount int `json:"miner_count"` - OtherMinersCount int `json:"other_miners_count"` - NoMinerCount int `json:"no_miner_count"` - - MinerRatio float32 `json:"miner_ratio"` - OtherMinersRatio float32 `json:"other_miners_ratio"` - NoMinerRatio float32 `json:"no_miner_ratio"` - - Address *address.Address `json:"address"` -} - -func (t *MainLikelySweepTransaction) ScanFromRow(consensus *sidechain.Consensus, row RowScanInterface) error { - var spendPub, viewPub crypto.PublicKeyBytes - var resultBuf, matchBuf []byte - var spendingOutputIndices, globalOutputIndices pq.Int64Array - if err := row.Scan(&t.Id, &t.Timestamp, &resultBuf, &matchBuf, &t.Value, &spendingOutputIndices, &globalOutputIndices, &t.InputCount, &t.InputDecoyCount, &t.MinerCount, &t.OtherMinersCount, &t.NoMinerCount, &t.MinerRatio, &t.OtherMinersRatio, &t.NoMinerRatio, &spendPub, &viewPub); err != nil { - return err - } else if err = utils.UnmarshalJSON(resultBuf, &t.Result); err != nil { - return err - } else if err = utils.UnmarshalJSON(resultBuf, &t.Match); err != nil { - return err - } - t.SpendingOutputIndices = make([]uint64, len(spendingOutputIndices)) - for j, ix := range spendingOutputIndices { - t.SpendingOutputIndices[j] = uint64(ix) - } - t.GlobalOutputIndices = make([]uint64, len(globalOutputIndices)) - for j, ix := range globalOutputIndices { - t.GlobalOutputIndices[j] = uint64(ix) - } - - network, err := consensus.NetworkType.AddressNetwork() - if err != nil { - return errors.New("unknown network type") - } - - t.Address = address.FromRawAddress(network, &spendPub, &viewPub) - return nil -} diff --git a/cmd/index/miner.go b/cmd/index/miner.go deleted file mode 100644 index 8ce895d..0000000 --- a/cmd/index/miner.go +++ /dev/null @@ -1,45 +0,0 @@ -package index - -import ( - "database/sql" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" -) - -const MinerSelectFields = "id, alias, spend_public_key, view_public_key" - -type Miner struct { - id uint64 - addr address.Address - alias sql.NullString -} - -func (m *Miner) Id() uint64 { - return m.id -} - -func (m *Miner) Alias() string { - if m.alias.Valid { - return m.alias.String - } - return "" -} - -func (m *Miner) Address() *address.Address { - return &m.addr -} - -func (m *Miner) ScanFromRow(consensus *sidechain.Consensus, row RowScanInterface) error { - var spendPub, viewPub crypto.PublicKeyBytes - if err := row.Scan(&m.id, &m.alias, &spendPub, &viewPub); err != nil { - return err - } - - network, err := consensus.NetworkType.AddressNetwork() - if err != nil { - return err - } - m.addr = *address.FromRawAddress(network, &spendPub, &viewPub) - return nil -} diff --git a/cmd/index/miner_webhook.go b/cmd/index/miner_webhook.go deleted file mode 100644 index c0a8f4d..0000000 --- a/cmd/index/miner_webhook.go +++ /dev/null @@ -1,124 +0,0 @@ -package index - -import ( - "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "net/url" - "slices" - "strings" -) - -type MinerWebHook struct { - Miner uint64 `json:"miner"` - Type WebHookType `json:"type"` - Url string `json:"url"` - Settings map[string]string `json:"settings"` - Consensus *sidechain.Consensus `json:"-"` -} - -type WebHookType string - -const ( - WebHookSlack WebHookType = "slack" - WebHookDiscord WebHookType = "discord" - WebHookTelegram WebHookType = "telegram" - WebHookMatrixHookshot WebHookType = "matrix-hookshot" - WebHookCustom WebHookType = "custom" -) - -var disallowedCustomHookHosts = []string{ - "hooks.slack.com", - "discord.com", - "api.telegram.org", -} - -var disallowedCustomHookPorts = []string{} - -func (w *MinerWebHook) ScanFromRow(consensus *sidechain.Consensus, row RowScanInterface) error { - var settingsBuf []byte - - w.Consensus = consensus - w.Settings = make(map[string]string) - - if err := row.Scan(&w.Miner, &w.Type, &w.Url, &settingsBuf); err != nil { - return err - } else if err = utils.UnmarshalJSON(settingsBuf, &w.Settings); err != nil { - return err - } - return nil -} - -func (w *MinerWebHook) Verify() error { - uri, err := url.Parse(w.Url) - if err != nil { - return err - } - switch w.Type { - case WebHookSlack: - if uri.Scheme != "https" { - return errors.New("invalid URL scheme, expected https") - } - if uri.Host != "hooks.slack.com" { - return errors.New("invalid hook host, expected hooks.slack.com") - } - if uri.Port() != "" { - return errors.New("unexpected port") - } - if !strings.HasPrefix(uri.Path, "/services/") { - return errors.New("invalid hook path start") - } - case WebHookDiscord: - if uri.Scheme != "https" { - return errors.New("invalid URL scheme, expected https") - } - if uri.Host != "discord.com" { - return errors.New("invalid hook host, expected discord.com") - } - if uri.Port() != "" { - return errors.New("unexpected port") - } - if !strings.HasPrefix(uri.Path, "/api/webhooks/") { - return errors.New("invalid hook path start") - } - case WebHookTelegram: - if uri.Scheme != "https" { - return errors.New("invalid URL scheme, expected https") - } - if uri.Host != "api.telegram.org" { - return errors.New("invalid hook host, expected api.telegram.org") - } - if uri.Port() != "" { - return errors.New("unexpected port") - } - if !strings.HasPrefix(uri.Path, "/bot") { - return errors.New("invalid hook path start") - } - if !strings.HasSuffix(uri.Path, "/sendMessage") { - return errors.New("invalid hook path end") - } - case WebHookMatrixHookshot: - if uri.Scheme != "https" { - return errors.New("invalid URL scheme, expected https") - } - if slices.Contains(disallowedCustomHookHosts, strings.ToLower(uri.Hostname())) { - return errors.New("disallowed hook host") - } - if uri.Port() != "" { - return errors.New("unexpected port") - } - case WebHookCustom: - if uri.Scheme != "https" && uri.Scheme != "http" { - return errors.New("invalid URL scheme, expected http or https") - } - if slices.Contains(disallowedCustomHookHosts, strings.ToLower(uri.Hostname())) { - return errors.New("disallowed hook host") - } - if slices.Contains(disallowedCustomHookPorts, strings.ToLower(uri.Port())) { - return errors.New("disallowed hook port") - } - default: - return errors.New("unsupported hook type") - } - return nil -} diff --git a/cmd/index/payout.go b/cmd/index/payout.go deleted file mode 100644 index 5b90921..0000000 --- a/cmd/index/payout.go +++ /dev/null @@ -1,31 +0,0 @@ -package index - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" -) - -type Payout struct { - Miner uint64 `json:"miner"` - TemplateId types.Hash `json:"template_id"` - SideHeight uint64 `json:"side_height"` - UncleOf types.Hash `json:"uncle_of,omitempty"` - MainId types.Hash `json:"main_id"` - MainHeight uint64 `json:"main_height"` - Timestamp uint64 `json:"timestamp"` - CoinbaseId types.Hash `json:"coinbase_id"` - Reward uint64 `json:"coinbase_reward"` - PrivateKey crypto.PrivateKeyBytes `json:"coinbase_private_key"` - Index uint64 `json:"coinbase_output_index"` - GlobalOutputIndex uint64 `json:"global_output_index"` - IncludingHeight uint64 `json:"including_height"` -} - -func (p *Payout) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error { - if err := row.Scan(&p.Miner, &p.MainId, &p.MainHeight, &p.Timestamp, &p.CoinbaseId, &p.PrivateKey, &p.TemplateId, &p.SideHeight, &p.UncleOf, &p.Reward, &p.Index, &p.GlobalOutputIndex, &p.IncludingHeight); err != nil { - return err - } - - return nil -} diff --git a/cmd/index/query.go b/cmd/index/query.go deleted file mode 100644 index f183eca..0000000 --- a/cmd/index/query.go +++ /dev/null @@ -1,174 +0,0 @@ -package index - -import ( - "database/sql" - "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" -) - -type RowScanInterface interface { - Scan(dest ...any) error -} - -type Scannable interface { - ScanFromRow(consensus *sidechain.Consensus, row RowScanInterface) error -} - -type QueryIterator[V any] interface { - All(f IterateFunction[int, *V]) (complete bool) - Next() (int, *V) - Close() - Err() error -} - -func QueryIterate[V any](i QueryIterator[V], f IterateFunction[int, *V]) { - if i == nil { - return - } - defer i.Close() - i.All(f) - - if i.Err() != nil { - panic(i.Err()) - } -} - -func QueryIterateToSlice[T any](i QueryIterator[T], err ...error) (s []*T) { - if len(err) > 0 { - if err[0] != nil { - panic(err) - } - } - - QueryIterate(i, func(key int, value *T) (stop bool) { - s = append(s, value) - return false - }) - return s -} - -func QueryFirstResult[T any](i QueryIterator[T], err ...error) (v *T) { - if len(err) > 0 { - if err[0] != nil { - panic(err) - } - } - - QueryIterate(i, func(_ int, value *T) (stop bool) { - v = value - return true - }) - return -} - -func QueryHasResults[T any](i QueryIterator[T], err ...error) bool { - if len(err) > 0 { - if err[0] != nil { - panic(err) - } - } - - var hasValue bool - QueryIterate(i, func(key int, value *T) (stop bool) { - if value != nil { - hasValue = true - } - return true - }) - return hasValue -} - -// queryStatement Queries a provided sql.Stmt and returns a QueryIterator -// After results are read QueryIterator must be closed/freed -func queryStatement[V any](index *Index, stmt *sql.Stmt, params ...any) (*QueryResult[V], error) { - var testV *V - if _, ok := any(testV).(Scannable); !ok { - return nil, errors.New("unsupported type") - } - - if rows, err := stmt.Query(params...); err != nil { - return nil, err - } else { - return &QueryResult[V]{ - consensus: index.consensus, - rows: rows, - }, err - } -} - -type FakeQueryResult[V any] struct { - NextFunction func() (int, *V) -} - -func (r *FakeQueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) { - for { - if i, v := r.Next(); v == nil { - return true - } else { - if f(i, v) { - // do not allow resuming - return true - } - } - } -} - -func (r *FakeQueryResult[V]) Next() (int, *V) { - return r.NextFunction() -} - -func (r *FakeQueryResult[V]) Err() error { - return nil -} - -func (r *FakeQueryResult[V]) Close() { - -} - -type QueryResult[V any] struct { - consensus *sidechain.Consensus - rows *sql.Rows - closer func() - i int - err error -} - -func (r *QueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) { - for { - if i, v := r.Next(); v == nil { - return true - } else { - if f(i, v) { - // do not allow resuming - return true - } - } - } -} - -func (r *QueryResult[V]) Next() (int, *V) { - if r.rows.Next() { - var v V - if r.err = any(&v).(Scannable).ScanFromRow(r.consensus, r.rows); r.err != nil { - return 0, nil - } - r.i++ - return r.i - 1, &v - } - return 0, nil -} - -func (r *QueryResult[V]) Err() error { - return r.err -} - -func (r *QueryResult[V]) Close() { - if r.closer != nil { - r.closer() - r.closer = nil - } - if r.rows != nil { - r.rows.Close() - r.rows = nil - } -} diff --git a/cmd/index/schema.sql b/cmd/index/schema.sql deleted file mode 100644 index 3b6c3d3..0000000 --- a/cmd/index/schema.sql +++ /dev/null @@ -1,202 +0,0 @@ -CREATE TABLE IF NOT EXISTS miners ( - id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - alias varchar UNIQUE DEFAULT NULL, - spend_public_key bytea NOT NULL, - view_public_key bytea NOT NULL, - UNIQUE (spend_public_key, view_public_key) -); - -CREATE TABLE IF NOT EXISTS miner_webhooks ( - miner bigint NOT NULL, - type varchar NOT NULL, - url varchar NOT NULL, - settings jsonb NOT NULL DEFAULT '{}', -- settings to know when to trigger or other required settings for the method - UNIQUE (miner, type), - FOREIGN KEY (miner) REFERENCES miners (id) -); - -CREATE INDEX IF NOT EXISTS miner_webhooks_miner_idx ON miner_webhooks (miner); - -CREATE TABLE IF NOT EXISTS side_blocks ( - main_id bytea PRIMARY KEY, -- mainchain id, on Monero network - main_height bigint NOT NULL, -- mainchain height - - template_id bytea NOT NULL, -- sidechain template id. Note multiple blocks can exist per template id, see inclusion - side_height bigint NOT NULL, -- sidechain height - parent_template_id bytea NOT NULL, -- previous sidechain template id - - miner bigint NOT NULL, -- miner who contributed the block - - -- uncle inclusion information - uncle_of bytea DEFAULT NULL, -- has been included under this parent block template id as an uncle. Can change after insert - effective_height bigint NOT NULL, -- has been included under this parent block height as an uncle, or is this height. Can change after insert - - -- nonce data - nonce bigint NOT NULL, -- nonce on block header. requires bigint for unsigned int32 - extra_nonce bigint NOT NULL, -- nonce on coinbase transaction extra data. requires bigint for unsigned int32 - - -- other not indexed data - timestamp bigint NOT NULL, -- mainchain timestamp - software_id bigint NOT NULL, -- Software used to generate this template. requires bigint for unsigned int32 - software_version bigint NOT NULL, -- Software version used to generate this template. requires bigint for unsigned int32 - window_depth int NOT NULL, -- PPLNS window depth, in blocks including this one - window_outputs int NOT NULL, -- number of outputs on coinbase transaction - transaction_count int NOT NULL, -- number of transactions included in the template - - difficulty bigint NOT NULL, -- sidechain difficulty at height - cumulative_difficulty bytea NOT NULL, -- sidechain cumulative difficulty at height, binary - pow_difficulty bigint NOT NULL, -- difficulty of pow_hash - pow_hash bytea NOT NULL, -- result of PoW function as a hash (all 0x00 = not known) - - inclusion int NOT NULL DEFAULT 1, -- how the block is included. Can change after insert: - -- 0 = orphan (was not included in-verified-chain) - -- 1 = in-verified-chain (uncle or main) - -- 2 = alternate in-verified-chain (uncle or main), for example when duplicate nonce happens - -- Higher values might specify forks or other custom additions - - UNIQUE (template_id, nonce, extra_nonce), -- main id can only change when nonce / extra nonce is adjusted, as template_id hash does not include them - FOREIGN KEY (miner) REFERENCES miners (id) -); - -CREATE INDEX IF NOT EXISTS side_blocks_miner_idx ON side_blocks (miner); -CREATE INDEX IF NOT EXISTS side_blocks_template_id_idx ON side_blocks (template_id); -CREATE INDEX IF NOT EXISTS side_blocks_main_height_idx ON side_blocks (main_height); -CREATE INDEX IF NOT EXISTS side_blocks_side_height_idx ON side_blocks (side_height); -CREATE INDEX IF NOT EXISTS side_blocks_parent_template_id_idx ON side_blocks (parent_template_id); -CREATE INDEX IF NOT EXISTS side_blocks_uncle_of_idx ON side_blocks (uncle_of); -CREATE INDEX IF NOT EXISTS side_blocks_effective_height_idx ON side_blocks (effective_height); - --- CLUSTER VERBOSE side_blocks USING side_blocks_effective_height_idx; - --- Cannot have non-unique constraints --- ALTER TABLE side_blocks ADD CONSTRAINT fk_side_blocks_uncle_of FOREIGN KEY (uncle_of) REFERENCES side_blocks (template_id); --- ALTER TABLE side_blocks ADD CONSTRAINT fk_side_blocks_effective_height FOREIGN KEY (effective_height) REFERENCES side_blocks (side_height); - - -CREATE TABLE IF NOT EXISTS main_blocks ( - id bytea PRIMARY KEY, - height bigint UNIQUE NOT NULL, - timestamp bigint NOT NULL, -- timestamp as set in block - reward bigint NOT NULL, - coinbase_id bytea UNIQUE NOT NULL, - difficulty bigint NOT NULL, -- mainchain difficulty at height - metadata jsonb NOT NULL DEFAULT '{}', -- metadata such as pool ownership, links to other p2pool networks, and other interesting data - -- sidechain data for blocks we own - side_template_id bytea UNIQUE DEFAULT NULL, - coinbase_private_key bytea DEFAULT NULL -- private key for coinbase outputs (all 0x00 = not known, but should have one) - - -- Cannot have non-unique constraints - -- FOREIGN KEY (side_template_id) REFERENCES side_blocks (template_id) -); - --- CLUSTER VERBOSE main_blocks USING main_blocks_height_key; - - -CREATE TABLE IF NOT EXISTS main_coinbase_outputs ( - id bytea NOT NULL, -- coinbase id - index int NOT NULL, -- transaction output index - global_output_index bigint UNIQUE NOT NULL, -- Monero global output idx - miner bigint NOT NULL, -- owner of the output - value bigint NOT NULL, - PRIMARY KEY (id, index), - FOREIGN KEY (id) REFERENCES main_blocks (coinbase_id), - FOREIGN KEY (miner) REFERENCES miners (id) -); - -CREATE INDEX IF NOT EXISTS main_coinbase_outputs_id_idx ON main_coinbase_outputs (id); -CREATE INDEX IF NOT EXISTS main_coinbase_outputs_miner_idx ON main_coinbase_outputs (miner); - -CREATE TABLE IF NOT EXISTS main_likely_sweep_transactions ( - id bytea PRIMARY KEY NOT NULL, -- transaction id - timestamp bigint NOT NULL, -- when the transaction was made / included in block - result jsonb NOT NULL, -- MinimalTransactionInputQueryResults - match jsonb NOT NULL, -- TransactionInputQueryResultsMatch - value bigint NOT NULL, - spending_output_indices bigint[] NOT NULL, -- global output indices consumed by this transaction (including decoys) - global_output_indices bigint[] NOT NULL, -- global output indices produced by this transaction - - input_count integer NOT NULL, -- count of inputs - input_decoy_count integer NOT NULL, -- count of decoys per input - - miner_count integer NOT NULL, - other_miners_count integer NOT NULL, - no_miner_count integer NOT NULL, - - miner_ratio real NOT NULL, - other_miners_ratio real NOT NULL, - no_miner_ratio real NOT NULL, - - miner_spend_public_key bytea NOT NULL, -- plausible owner of the transaction - miner_view_public_key bytea NOT NULL -); -CREATE INDEX IF NOT EXISTS main_likely_sweep_transactions_miner_idx ON main_likely_sweep_transactions (miner_spend_public_key, miner_view_public_key); - -CREATE INDEX IF NOT EXISTS main_likely_sweep_transactions_spending_output_indexes_idx ON main_likely_sweep_transactions USING GIN (spending_output_indices); -CREATE INDEX IF NOT EXISTS main_likely_sweep_transactions_global_output_indexes_idx ON main_likely_sweep_transactions USING GIN (global_output_indices); -CREATE INDEX IF NOT EXISTS main_likely_sweep_transactions_timestamp_idx ON main_likely_sweep_transactions (timestamp); - -CREATE EXTENSION IF NOT EXISTS pg_stat_statements; - - --- Views -CREATE EXTENSION IF NOT EXISTS pg_ivm; - -SELECT create_immv('found_main_blocks_v4', $$ -SELECT - m.id AS main_id, - m.height AS main_height, - m.timestamp AS timestamp, - m.reward AS reward, - m.coinbase_id AS coinbase_id, - m.coinbase_private_key AS coinbase_private_key, - m.difficulty AS main_difficulty, - m.side_template_id AS template_id, - s.side_height AS side_height, - s.miner AS miner, - s.uncle_of AS uncle_of, - s.effective_height AS effective_height, - s.window_depth AS window_depth, - s.window_outputs AS window_outputs, - s.transaction_count AS transaction_count, - s.difficulty AS side_difficulty, - s.cumulative_difficulty AS side_cumulative_difficulty, - s.inclusion AS side_inclusion -FROM - (SELECT * FROM main_blocks WHERE side_template_id IS NOT NULL) AS m - JOIN - (SELECT * FROM side_blocks) AS s ON s.main_id = m.id -$$); - -CREATE UNIQUE INDEX IF NOT EXISTS found_main_blocks_main_id_idx ON found_main_blocks_v4 (main_id); -CREATE INDEX IF NOT EXISTS found_main_blocks_miner_idx ON found_main_blocks_v4 (miner); -CREATE INDEX IF NOT EXISTS found_main_blocks_side_height_idx ON found_main_blocks_v4 (side_height); - -SELECT create_immv('payouts_v4', $$ -SELECT - o.miner AS miner, - m.id AS main_id, - m.height AS main_height, - m.timestamp AS timestamp, - m.coinbase_id AS coinbase_id, - m.coinbase_private_key AS coinbase_private_key, - m.side_template_id AS template_id, - s.side_height AS side_height, - s.uncle_of AS uncle_of, - o.value AS value, - o.index AS index, - o.global_output_index AS global_output_index, - s.including_height AS including_height -FROM - (SELECT id, value, index, global_output_index, miner FROM main_coinbase_outputs) AS o - JOIN - (SELECT id, height, timestamp, side_template_id, coinbase_id, coinbase_private_key FROM main_blocks) AS m ON m.coinbase_id = o.id - JOIN - (SELECT template_id, main_id, side_height, uncle_of, GREATEST(0, GREATEST(effective_height, side_height) - window_depth) AS including_height FROM side_blocks) AS s ON s.main_id = m.id -$$); - - -CREATE UNIQUE INDEX IF NOT EXISTS found_main_blocks_global_output_index_idx ON payouts_v4 (global_output_index); -CREATE INDEX IF NOT EXISTS payouts_miner_idx ON payouts_v4 (miner); -CREATE INDEX IF NOT EXISTS payouts_main_id_idx ON payouts_v4 (main_id); -CREATE INDEX IF NOT EXISTS payouts_side_height_idx ON payouts_v4 (side_height); -CREATE INDEX IF NOT EXISTS payouts_main_height_idx ON payouts_v4 (main_height); \ No newline at end of file diff --git a/cmd/index/side_block.go b/cmd/index/side_block.go deleted file mode 100644 index e375f5e..0000000 --- a/cmd/index/side_block.go +++ /dev/null @@ -1,241 +0,0 @@ -package index - -import ( - "bytes" - "encoding/binary" - "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "slices" - "unsafe" -) - -type BlockInclusion int - -const ( - // InclusionOrphan orphan (was not included in-verified-chain) - InclusionOrphan = BlockInclusion(iota) - // InclusionInVerifiedChain in-verified-chain (uncle or main) - InclusionInVerifiedChain - // InclusionAlternateInVerifiedChain alternate in-verified-chain (uncle or main), for example when duplicate nonce happens - InclusionAlternateInVerifiedChain - - InclusionCount -) - -const SideBlockSelectFields = "main_id, main_height, template_id, side_height, parent_template_id, miner, uncle_of, effective_height, nonce, extra_nonce, timestamp, software_id, software_version, window_depth, window_outputs, transaction_count, difficulty, cumulative_difficulty, pow_difficulty, pow_hash, inclusion" - -type SideBlock struct { - // MainId mainchain id, on Monero network - MainId types.Hash `json:"main_id"` - MainHeight uint64 `json:"main_height"` - - // TemplateId -- sidechain template id. Note multiple blocks can exist per template id, see Inclusion - TemplateId types.Hash `json:"template_id"` - SideHeight uint64 `json:"side_height"` - // ParentTemplateId previous sidechain template id - ParentTemplateId types.Hash `json:"parent_template_id"` - - // Miner internal id of the miner who contributed the block - Miner uint64 `json:"miner"` - - // Uncle inclusion information - - // UncleOf has been included under this parent block TemplateId as an uncle - UncleOf types.Hash `json:"uncle_of,omitempty"` - // EffectiveHeight has been included under this parent block height as an uncle, or is this height - EffectiveHeight uint64 `json:"effective_height"` - - // Nonce data - Nonce uint32 `json:"nonce"` - ExtraNonce uint32 `json:"extra_nonce"` - - Timestamp uint64 `json:"timestamp"` - SoftwareId p2pooltypes.SoftwareId `json:"software_id"` - SoftwareVersion p2pooltypes.SoftwareVersion `json:"software_version"` - // WindowDepth PPLNS window depth, in blocks including this one - WindowDepth uint32 `json:"window_depth"` - WindowOutputs uint32 `json:"window_outputs"` - - // Difficulty sidechain difficulty at height - Difficulty uint64 `json:"difficulty"` - CumulativeDifficulty types.Difficulty `json:"cumulative_difficulty"` - PowDifficulty uint64 `json:"pow_difficulty"` - // PowHash result of PoW function as a hash (all 0x00 = not known) - PowHash types.Hash `json:"pow_hash"` - - Inclusion BlockInclusion `json:"inclusion"` - - TransactionCount uint32 `json:"transaction_count"` - - // Extra information filled just for JSON purposes - - 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,omitempty"` -} - -type SideBlockUncleEntry struct { - TemplateId types.Hash `json:"template_id"` - Miner uint64 `json:"miner"` - SideHeight uint64 `json:"side_height"` - Difficulty uint64 `json:"difficulty"` -} - -// FromPoolBlock block needs to be pre-processed for ids to be correct -// These fields need to be filled by caller to match needs: -// SideBlock.UncleOf -// SideBlock.EffectiveHeight -// SideBlock.WindowDepth -// SideBlock.Inclusion -func (b *SideBlock) FromPoolBlock(i *Index, block *sidechain.PoolBlock, getSeedByHeight mainblock.GetSeedByHeightFunc) error { - b.MainId = block.MainId() - b.TemplateId = block.SideTemplateId(i.consensus) - - if b.MainId == types.ZeroHash { - return errors.New("invalid main id") - } - if b.TemplateId == types.ZeroHash || bytes.Compare(b.TemplateId[:], block.CoinbaseExtra(sidechain.SideTemplateId)) != 0 { - return errors.New("invalid template id") - } - b.MainHeight = block.Main.Coinbase.GenHeight - b.SideHeight = block.Side.Height - b.ParentTemplateId = block.Side.Parent - b.Miner = i.GetOrCreateMinerPackedAddress(block.GetAddress()).id - b.Nonce = block.Main.Nonce - b.ExtraNonce = block.ExtraNonce() - b.Timestamp = block.Main.Timestamp - b.SoftwareId = block.Side.ExtraBuffer.SoftwareId - b.SoftwareVersion = block.Side.ExtraBuffer.SoftwareVersion - b.WindowOutputs = uint32(len(block.Main.Coinbase.Outputs)) - b.TransactionCount = uint32(len(block.Main.Transactions)) - b.Difficulty = block.Side.Difficulty.Lo - b.CumulativeDifficulty = block.Side.CumulativeDifficulty - b.PowHash = block.PowHash(i.Consensus().GetHasher(), getSeedByHeight) - if b.PowHash == types.ZeroHash { - return errors.New("invalid pow hash") - } - b.PowDifficulty = types.DifficultyFromPoW(b.PowHash).Lo - - return nil -} - -func (b *SideBlock) SetUncleOf(block *SideBlock) error { - if block == nil { - b.UncleOf = types.ZeroHash - b.EffectiveHeight = b.SideHeight - return nil - } - if block.IsUncle() { - return errors.New("parent cannot be uncle") - } - if block.SideHeight <= b.SideHeight { - return errors.New("parent side height cannot be lower or equal") - } - b.UncleOf = block.TemplateId - b.EffectiveHeight = block.EffectiveHeight - return nil -} - -// IsTipOfHeight whether this block is considered to be the "main" block within this height, not an uncle, or alternate -func (b *SideBlock) IsTipOfHeight() bool { - return b.SideHeight == b.EffectiveHeight && b.Inclusion == InclusionInVerifiedChain -} - -func (b *SideBlock) FullId() sidechain.FullId { - var buf sidechain.FullId - copy(buf[:], b.TemplateId[:]) - binary.LittleEndian.PutUint32(buf[types.HashSize:], b.Nonce) - binary.LittleEndian.PutUint32(buf[types.HashSize+unsafe.Sizeof(b.Nonce):], b.ExtraNonce) - return buf -} - -func (b *SideBlock) IsUncle() bool { - return b.SideHeight != b.EffectiveHeight && b.UncleOf != types.ZeroHash -} - -func (b *SideBlock) IsOrphan() bool { - return b.Inclusion == InclusionOrphan -} - -func (b *SideBlock) ScanFromRow(_ *sidechain.Consensus, row RowScanInterface) error { - if err := row.Scan(&b.MainId, &b.MainHeight, &b.TemplateId, &b.SideHeight, &b.ParentTemplateId, &b.Miner, &b.UncleOf, &b.EffectiveHeight, &b.Nonce, &b.ExtraNonce, &b.Timestamp, &b.SoftwareId, &b.SoftwareVersion, &b.WindowDepth, &b.WindowOutputs, &b.TransactionCount, &b.Difficulty, &b.CumulativeDifficulty, &b.PowDifficulty, &b.PowHash, &b.Inclusion); err != nil { - return err - } - return nil -} - -//Utilities to be used by JSON decoded blocks - -func (b *SideBlock) Weight(tipHeight, windowSize, consensusUnclePenalty uint64) (weight, parentWeight uint64) { - if (tipHeight - b.SideHeight) >= windowSize { - return 0, 0 - } - if b.IsUncle() { - unclePenalty := types.DifficultyFrom64(b.Difficulty).Mul64(consensusUnclePenalty).Div64(100) - uncleWeight := b.Difficulty - unclePenalty.Lo - - return uncleWeight, unclePenalty.Lo - } else { - weight = b.Difficulty - for _, u := range b.Uncles { - if (tipHeight - u.SideHeight) >= windowSize { - continue - } - weight += types.DifficultyFrom64(u.Difficulty).Mul64(consensusUnclePenalty).Div64(100).Lo - } - return weight, 0 - } -} - -func HashRatioSideBlocks(shares []*SideBlock, latest *SideBlock, consensusUnclePenalty uint64) (hashrate uint64, ratio float64, weight, total types.Difficulty) { - if len(shares) == 0 || latest == nil { - return 0, 0, types.ZeroDifficulty, types.ZeroDifficulty - } - - shares = slices.Clone(shares) - slices.SortFunc(shares, SortSideBlock) - - var totalWeight types.Difficulty - for _, s := range shares { - w, _ := s.Weight(s.SideHeight, uint64(s.WindowDepth), consensusUnclePenalty) - totalWeight = totalWeight.Add64(w) - } - - cumWeight := latest.CumulativeDifficulty.Sub(shares[0].CumulativeDifficulty) - - return totalWeight.Div64(latest.Timestamp - shares[0].Timestamp).Lo, - totalWeight.Float64() / cumWeight.Float64(), - totalWeight, - cumWeight -} - -func SortSideBlock(a, b *SideBlock) int { - if a == b { - return 0 - } - - if a == nil { - return -1 - } else if b == nil { - return 1 - } - - if a.EffectiveHeight < b.EffectiveHeight { - return -1 - } else if a.EffectiveHeight > b.EffectiveHeight { - return 1 - } - if a.SideHeight < b.SideHeight { - return -1 - } else if a.SideHeight > b.SideHeight { - return 1 - } - //same height, sort by main id - return a.MainId.Compare(b.MainId) -} diff --git a/cmd/index/utils.go b/cmd/index/utils.go deleted file mode 100644 index 1f23759..0000000 --- a/cmd/index/utils.go +++ /dev/null @@ -1,447 +0,0 @@ -package index - -import ( - "errors" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "git.gammaspectra.live/P2Pool/sha3" - "slices" -) - -type GetByTemplateIdFunc func(h types.Hash) *SideBlock -type GetUnclesByTemplateIdFunc func(h types.Hash) QueryIterator[SideBlock] -type SideBlockWindowAddWeightFunc func(b *SideBlock, weight types.Difficulty) - -type SideBlockWindowSlot struct { - Block *SideBlock - // Uncles that count for the window weight - Uncles []*SideBlock -} - -// IterateSideBlocksInPPLNSWindow -// Copy of sidechain.IterateBlocksInPPLNSWindow -func IterateSideBlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, addWeightFunc SideBlockWindowAddWeightFunc, slotFunc func(slot SideBlockWindowSlot)) error { - - cur := tip - - var blockDepth uint64 - - var mainchainDiff types.Difficulty - - if tip.ParentTemplateId != types.ZeroHash { - seedHeight := randomx.SeedHeight(tip.MainHeight) - mainchainDiff = difficultyByHeight(seedHeight) - if mainchainDiff == types.ZeroDifficulty { - return fmt.Errorf("couldn't get mainchain difficulty for height = %d", seedHeight) - } - } - - // Dynamic PPLNS window starting from v2 - // Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average) - sidechainVersion := sidechain.P2PoolShareVersion(consensus, tip.Timestamp) - - maxPplnsWeight := types.MaxDifficulty - - if sidechainVersion > sidechain.ShareVersion_V1 { - maxPplnsWeight = mainchainDiff.Mul64(2) - } - - var pplnsWeight types.Difficulty - - for { - curEntry := SideBlockWindowSlot{ - Block: cur, - } - curWeight := types.DifficultyFrom64(cur.Difficulty) - - QueryIterate(getUnclesByTemplateId(cur.TemplateId), func(_ int, uncle *SideBlock) (stop bool) { - //Needs to be added regardless - for other consumers - if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool { - return sideBlock.TemplateId == uncle.TemplateId - }) { - curEntry.Uncles = append(curEntry.Uncles, uncle) - } - - // Skip uncles which are already out of PPLNS window - if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize { - return false - } - - // Take some % of uncle's weight into this share - uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(uncle.Difficulty)) - newPplnsWeight := pplnsWeight.Add(uncleWeight) - - // Skip uncles that push PPLNS weight above the limit - if newPplnsWeight.Cmp(maxPplnsWeight) > 0 { - return false - } - curWeight = curWeight.Add(unclePenalty) - - if addWeightFunc != nil { - addWeightFunc(uncle, uncleWeight) - } - - pplnsWeight = newPplnsWeight - - return false - }) - - // Always add non-uncle shares even if PPLNS weight goes above the limit - slotFunc(curEntry) - - if addWeightFunc != nil { - addWeightFunc(cur, curWeight) - } - - pplnsWeight = pplnsWeight.Add(curWeight) - - // One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty - if pplnsWeight.Cmp(maxPplnsWeight) > 0 { - break - } - - blockDepth++ - - if blockDepth >= consensus.ChainWindowSize { - break - } - - // Reached the genesis block so we're done - if cur.SideHeight == 0 { - break - } - - parentId := cur.ParentTemplateId - cur = getByTemplateId(parentId) - - if cur == nil { - return fmt.Errorf("could not find parent %s", parentId.String()) - } - } - - return nil -} - -// BlocksInPPLNSWindow -// Copy of sidechain.BlocksInPPLNSWindow -func BlocksInPPLNSWindow(tip *SideBlock, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, addWeightFunc SideBlockWindowAddWeightFunc) (bottomHeight uint64, err error) { - - cur := tip - - var blockDepth uint64 - - var mainchainDiff types.Difficulty - - if tip.ParentTemplateId != types.ZeroHash { - seedHeight := randomx.SeedHeight(tip.MainHeight) - mainchainDiff = difficultyByHeight(seedHeight) - if mainchainDiff == types.ZeroDifficulty { - return 0, fmt.Errorf("couldn't get mainchain difficulty for height = %d", seedHeight) - } - } - - // Dynamic PPLNS window starting from v2 - // Limit PPLNS weight to 2x of the Monero difficulty (max 2 blocks per PPLNS window on average) - sidechainVersion := sidechain.P2PoolShareVersion(consensus, tip.Timestamp) - - maxPplnsWeight := types.MaxDifficulty - - if sidechainVersion > sidechain.ShareVersion_V1 { - maxPplnsWeight = mainchainDiff.Mul64(2) - } - - var pplnsWeight types.Difficulty - - for { - curEntry := SideBlockWindowSlot{ - Block: cur, - } - curWeight := types.DifficultyFrom64(cur.Difficulty) - - QueryIterate(getUnclesByTemplateId(cur.TemplateId), func(_ int, uncle *SideBlock) (stop bool) { - //Needs to be added regardless - for other consumers - if !slices.ContainsFunc(curEntry.Uncles, func(sideBlock *SideBlock) bool { - return sideBlock.TemplateId == uncle.TemplateId - }) { - curEntry.Uncles = append(curEntry.Uncles, uncle) - } - - // Skip uncles which are already out of PPLNS window - if (tip.SideHeight - uncle.SideHeight) >= consensus.ChainWindowSize { - return false - } - - // Take some % of uncle's weight into this share - uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(uncle.Difficulty)) - newPplnsWeight := pplnsWeight.Add(uncleWeight) - - // Skip uncles that push PPLNS weight above the limit - if newPplnsWeight.Cmp(maxPplnsWeight) > 0 { - return false - } - curWeight = curWeight.Add(unclePenalty) - - if addWeightFunc != nil { - addWeightFunc(uncle, uncleWeight) - } - - pplnsWeight = newPplnsWeight - - return false - }) - - // Always add non-uncle shares even if PPLNS weight goes above the limit - bottomHeight = cur.SideHeight - - if addWeightFunc != nil { - addWeightFunc(cur, curWeight) - } - - pplnsWeight = pplnsWeight.Add(curWeight) - - // One non-uncle share can go above the limit, but it will also guarantee that "shares" is never empty - if pplnsWeight.Cmp(maxPplnsWeight) > 0 { - break - } - - blockDepth++ - - if blockDepth >= consensus.ChainWindowSize { - break - } - - // Reached the genesis block so we're done - if cur.SideHeight == 0 { - break - } - - parentId := cur.ParentTemplateId - cur = getByTemplateId(parentId) - - if cur == nil { - return 0, fmt.Errorf("could not find parent %s", parentId.String()) - } - } - - return bottomHeight, nil -} - -func IterateSideBlocksInPPLNSWindowFast(indexDb *Index, tip *SideBlock, difficultyByHeight block.GetDifficultyByHeightFunc, addWeightFunc SideBlockWindowAddWeightFunc, slotFunc func(slot SideBlockWindowSlot)) error { - if tip == nil { - return errors.New("nil tip") - } - window := QueryIterateToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip)) - if len(window) == 0 { - return errors.New("nil window") - } - - var hintIndex int - - getByTemplateIdFull := func(h types.Hash) *SideBlock { - if i := slices.IndexFunc(window, func(e *SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex = i - return window[i] - } - return nil - } - getByTemplateId := func(h types.Hash) *SideBlock { - //fast lookup first - if i := slices.IndexFunc(window[hintIndex:], func(e *SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex += i - return window[hintIndex] - } - return getByTemplateIdFull(h) - } - getUnclesOf := func(h types.Hash) QueryIterator[SideBlock] { - parentEffectiveHeight := window[hintIndex].EffectiveHeight - if window[hintIndex].TemplateId != h { - parentEffectiveHeight = 0 - } - - startIndex := 0 - - return &FakeQueryResult[SideBlock]{ - NextFunction: func() (int, *SideBlock) { - for _, b := range window[startIndex+hintIndex:] { - if b.UncleOf == h { - startIndex++ - return startIndex, b - } - startIndex++ - - if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight { - //early exit - break - } - } - return 0, nil - }, - } - } - - return IterateSideBlocksInPPLNSWindow(tip, indexDb.Consensus(), difficultyByHeight, getByTemplateId, getUnclesOf, addWeightFunc, slotFunc) -} - -func BlocksInPPLNSWindowFast(indexDb *Index, tip *SideBlock, difficultyByHeight block.GetDifficultyByHeightFunc, addWeightFunc SideBlockWindowAddWeightFunc) (bottomHeight uint64, err error) { - if tip == nil { - return 0, errors.New("nil tip") - } - window := QueryIterateToSlice(indexDb.GetSideBlocksInPPLNSWindow(tip)) - if len(window) == 0 { - return 0, errors.New("nil window") - } - - var hintIndex int - - getByTemplateIdFull := func(h types.Hash) *SideBlock { - if i := slices.IndexFunc(window, func(e *SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex = i - return window[i] - } - return nil - } - getByTemplateId := func(h types.Hash) *SideBlock { - //fast lookup first - if i := slices.IndexFunc(window[hintIndex:], func(e *SideBlock) bool { - return e.TemplateId == h - }); i != -1 { - hintIndex += i - return window[hintIndex] - } - return getByTemplateIdFull(h) - } - getUnclesOf := func(h types.Hash) QueryIterator[SideBlock] { - parentEffectiveHeight := window[hintIndex].EffectiveHeight - if window[hintIndex].TemplateId != h { - parentEffectiveHeight = 0 - } - - startIndex := 0 - - return &FakeQueryResult[SideBlock]{ - NextFunction: func() (int, *SideBlock) { - for _, b := range window[startIndex+hintIndex:] { - if b.UncleOf == h { - startIndex++ - return startIndex, b - } - startIndex++ - - if parentEffectiveHeight != 0 && b.EffectiveHeight < parentEffectiveHeight { - //early exit - break - } - } - return 0, nil - }, - } - } - - return BlocksInPPLNSWindow(tip, indexDb.Consensus(), difficultyByHeight, getByTemplateId, getUnclesOf, addWeightFunc) -} - -// GetSharesOrdered -// Copy of sidechain.GetSharesOrdered -func GetSharesOrdered(indexDb *Index, tip *SideBlock, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, preAllocatedShares sidechain.Shares) (shares sidechain.Shares, bottomHeight uint64) { - index := 0 - l := len(preAllocatedShares) - - if bottomHeight, err := BlocksInPPLNSWindow(tip, indexDb.Consensus(), difficultyByHeight, getByTemplateId, getUnclesByTemplateId, func(b *SideBlock, weight types.Difficulty) { - addr := indexDb.GetMiner(b.Miner).Address().ToPackedAddress() - if index < l { - preAllocatedShares[index].Address = addr - - preAllocatedShares[index].Weight = weight - } else { - preAllocatedShares = append(preAllocatedShares, &sidechain.Share{ - Address: addr, - Weight: weight, - }) - } - index++ - }); err != nil { - return nil, 0 - } else { - shares = preAllocatedShares[:index] - - //remove dupes - shares = shares.Compact() - - return shares, bottomHeight - } -} - -// GetShares -// Copy of sidechain.GetShares -func GetShares(indexDb *Index, tip *SideBlock, coinbasePrivateKeySeed types.Hash, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, preAllocatedShares sidechain.Shares) (shares sidechain.Shares, bottomHeight uint64) { - shares, bottomHeight = GetSharesOrdered(indexDb, tip, difficultyByHeight, getByTemplateId, getUnclesByTemplateId, preAllocatedShares) - if shares == nil { - return - } - - //Shuffle shares - sidechain.ShuffleShares(shares, sidechain.P2PoolShareVersion(indexDb.Consensus(), tip.Timestamp), coinbasePrivateKeySeed) - - return shares, bottomHeight -} - -// CalculateOutputs -// Copy of sidechain.CalculateOutputs -func CalculateOutputs(indexDb *Index, block *SideBlock, transactionOutputType uint8, totalReward uint64, coinbasePrivateKey crypto.PrivateKey, coinbasePrivateKeySeed types.Hash, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId GetByTemplateIdFunc, getUnclesByTemplateId GetUnclesByTemplateIdFunc, derivationCache sidechain.DerivationCacheInterface, preAllocatedShares sidechain.Shares, preAllocatedRewards []uint64) (outputs transaction.Outputs, bottomHeight uint64) { - tmpShares, bottomHeight := GetShares(indexDb, block, coinbasePrivateKeySeed, difficultyByHeight, getByTemplateId, getUnclesByTemplateId, preAllocatedShares) - if preAllocatedRewards == nil { - preAllocatedRewards = make([]uint64, 0, len(tmpShares)) - } - tmpRewards := sidechain.SplitReward(preAllocatedRewards, totalReward, tmpShares) - - if tmpShares == nil || tmpRewards == nil || len(tmpRewards) != len(tmpShares) { - return nil, 0 - } - - n := uint64(len(tmpShares)) - - outputs = make(transaction.Outputs, n) - - txType := transactionOutputType - - txPrivateKeySlice := coinbasePrivateKey.AsSlice() - txPrivateKeyScalar := coinbasePrivateKey.AsScalar() - - var hashers []*sha3.HasherState - - defer func() { - for _, h := range hashers { - crypto.PutKeccak256Hasher(h) - } - }() - - utils.SplitWork(-2, n, func(workIndex uint64, workerIndex int) error { - output := transaction.Output{ - Index: workIndex, - Type: txType, - } - output.Reward = tmpRewards[output.Index] - output.EphemeralPublicKey, output.ViewTag = derivationCache.GetEphemeralPublicKey(&tmpShares[output.Index].Address, txPrivateKeySlice, txPrivateKeyScalar, output.Index, hashers[workerIndex]) - - outputs[output.Index] = output - - return nil - }, func(routines, routineIndex int) error { - hashers = append(hashers, crypto.GetKeccak256Hasher()) - return nil - }, nil) - - return outputs, bottomHeight -} diff --git a/cmd/legacytoarchive/go.mod b/cmd/legacytoarchive/go.mod deleted file mode 100644 index b63b6da..0000000 --- a/cmd/legacytoarchive/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/legacytoarchive - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - github.com/floatdrop/lru v1.3.0 -) - -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/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.9 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/legacytoarchive/go.sum b/cmd/legacytoarchive/go.sum deleted file mode 100644 index 188f34f..0000000 --- a/cmd/legacytoarchive/go.sum +++ /dev/null @@ -1,47 +0,0 @@ -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/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/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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/legacytoarchive/legacytoarchive.go b/cmd/legacytoarchive/legacytoarchive.go deleted file mode 100644 index 3d47d6a..0000000 --- a/cmd/legacytoarchive/legacytoarchive.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "encoding/hex" - "flag" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "math" - "os" - "path" -) - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputFolder := flag.String("input", "", "Input legacy api folder with raw / failed blocks") - outputArchive := flag.String("output", "", "Output path for archive database") - - flag.Parse() - - cf, err := os.ReadFile(*inputConsensus) - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - - archiveCache, err := archive.NewCache(*outputArchive, consensus, func(height uint64) types.Difficulty { - return types.ZeroDifficulty - }) - if err != nil { - utils.Panic(err) - } - defer archiveCache.Close() - - processed := make(map[types.Hash]bool) - - totalStored := 0 - - derivationCache := sidechain.NewDerivationLRUCache() - - blockCache := lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4 * 60)) - - loadBlock := func(id types.Hash) *sidechain.PoolBlock { - n := id.String() - fPath := path.Join(*inputFolder, "blocks", n[:1], n) - if buf, err := os.ReadFile(fPath); err != nil { - return nil - } else { - if hexBuf, err := hex.DecodeString(string(buf)); err != nil { - utils.Panic(err) - } else { - if block, err := sidechain.NewShareFromExportedBytes(hexBuf, consensus, derivationCache); err != nil { - utils.Error("", "error decoding block %s, %s", id.String(), err) - } else { - block.Depth.Store(math.MaxUint64) - return block - } - } - } - return nil - } - - getByTemplateId := func(h types.Hash) *sidechain.PoolBlock { - if v := blockCache.Get(h); v == nil { - if b := loadBlock(h); b != nil { - b.Depth.Store(math.MaxUint64) - blockCache.Set(h, b) - return b - } - return nil - } else { - return *v - } - } - - var storeBlock func(k types.Hash, b *sidechain.PoolBlock, depth uint64) - storeBlock = func(k types.Hash, b *sidechain.PoolBlock, depth uint64) { - if b == nil || processed[k] { - return - } - if depth >= consensus.ChainWindowSize*4*30 { //avoid infinite memory growth - return - } - if parent := getByTemplateId(b.Side.Parent); parent != nil { - storeBlock(b.Side.Parent, parent, depth+1) - topDepth := parent.Depth.Load() - b.FillTransactionParentIndices(parent) - if topDepth == math.MaxUint64 { - b.Depth.Store(consensus.ChainWindowSize * 2) - } else if topDepth == 0 { - b.Depth.Store(0) - } else { - b.Depth.Store(topDepth - 1) - } - } else { - b.Depth.Store(math.MaxUint64) - } - archiveCache.Store(b) - totalStored++ - processed[k] = true - } - - for i := 0; i <= 0xf; i++ { - n := hex.EncodeToString([]byte{byte(i)}) - dPath := path.Join(*inputFolder, "blocks", n[1:]) - utils.Logf("", "Reading directory %s", dPath) - if dir, err := os.ReadDir(dPath); err != nil { - utils.Panic(err) - } else { - for _, e := range dir { - h, _ := hex.DecodeString(e.Name()) - id := types.HashFromBytes(h) - bb := getByTemplateId(id) - if bb != nil && !archiveCache.ExistsByMainId(bb.MainId()) { - storeBlock(id, bb, 0) - } - } - } - } - - for i := 0; i <= 0xf; i++ { - n := hex.EncodeToString([]byte{byte(i)}) - dPath := path.Join(*inputFolder, "failed_blocks", n[1:]) - if dir, err := os.ReadDir(dPath); err != nil { - utils.Panic(err) - } else { - for _, e := range dir { - fPath := path.Join(dPath, e.Name()) - utils.Logf("", "Processing %s", fPath) - if buf, err := os.ReadFile(path.Join(dPath, e.Name())); err != nil { - utils.Panic(err) - } else { - if hexBuf, err := hex.DecodeString(string(buf)); err != nil { - utils.Panic(err) - } else { - if block, err := sidechain.NewShareFromExportedBytes(hexBuf, consensus, derivationCache); err != nil { - utils.Panic(err) - } else { - block.Depth.Store(math.MaxUint64) - archiveCache.Store(block) - totalStored++ - } - } - } - } - } - } - - utils.Logf("", "total stored %d", totalStored) -} diff --git a/cmd/p2pool/api.go b/cmd/p2pool/api.go deleted file mode 100644 index a94db86..0000000 --- a/cmd/p2pool/api.go +++ /dev/null @@ -1,892 +0,0 @@ -package main - -import ( - "bytes" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/p2p" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "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 -} diff --git a/cmd/p2pool/default.pgo b/cmd/p2pool/default.pgo deleted file mode 100644 index 60522bd..0000000 Binary files a/cmd/p2pool/default.pgo and /dev/null differ diff --git a/cmd/p2pool/go.mod b/cmd/p2pool/go.mod deleted file mode 100644 index 5bbb1ea..0000000 --- a/cmd/p2pool/go.mod +++ /dev/null @@ -1,43 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/p2pool - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils v0.0.0 => ../httputils - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/httputils v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - 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.9 // 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 diff --git a/cmd/p2pool/go.sum b/cmd/p2pool/go.sum deleted file mode 100644 index 9dad8f7..0000000 --- a/cmd/p2pool/go.sum +++ /dev/null @@ -1,56 +0,0 @@ -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/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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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= diff --git a/cmd/p2pool/main.go b/cmd/p2pool/main.go deleted file mode 100644 index f18c2c9..0000000 --- a/cmd/p2pool/main.go +++ /dev/null @@ -1,318 +0,0 @@ -package main - -import ( - "bufio" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/p2p" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/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) - } - } - } -} diff --git a/cmd/p2pool/p2pool.go b/cmd/p2pool/p2pool.go deleted file mode 100644 index 6b0b2a4..0000000 --- a/cmd/p2pool/p2pool.go +++ /dev/null @@ -1,627 +0,0 @@ -package main - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client/zmq" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/legacy" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mainchain" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mempool" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/p2p" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "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) - } - } -} diff --git a/cmd/readcache/go.mod b/cmd/readcache/go.mod deleted file mode 100644 index 96d1fde..0000000 --- a/cmd/readcache/go.mod +++ /dev/null @@ -1,34 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/readcache - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 -) - -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/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 - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/readcache/go.sum b/cmd/readcache/go.sum deleted file mode 100644 index faca067..0000000 --- a/cmd/readcache/go.sum +++ /dev/null @@ -1,44 +0,0 @@ -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/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/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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/readcache/readcache.go b/cmd/readcache/readcache.go deleted file mode 100644 index 1bfd85e..0000000 --- a/cmd/readcache/readcache.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "flag" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/legacy" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "os" - "path" -) - -type loadee struct { - c *sidechain.Consensus - cb func(block *sidechain.PoolBlock) -} - -func (l *loadee) Consensus() *sidechain.Consensus { - return l.c -} - -func (l *loadee) AddCachedBlock(block *sidechain.PoolBlock) { - l.cb(block) -} - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputFile := flag.String("input", "p2pool.cache", "Input p2pool.cache path") - outputFolder := flag.String("output", "shares", "Output path for extracted shares") - - flag.Parse() - - cf, err := os.ReadFile(*inputConsensus) - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - - cache, err := legacy.NewCache(consensus, *inputFile) - if err != nil { - utils.Panic(err) - } - defer cache.Close() - - l := &loadee{ - c: consensus, - cb: func(block *sidechain.PoolBlock) { - expectedBlockId := types.HashFromBytes(block.CoinbaseExtra(sidechain.SideTemplateId)) - calculatedBlockId := block.SideTemplateId(consensus) - - if expectedBlockId != calculatedBlockId { - utils.Errorf("", "block height %d, template id %s, expected %s", block.Side.Height, calculatedBlockId, expectedBlockId) - } else { - blob, err := block.MarshalBinary() - if err != nil { - utils.Panic(err) - } - utils.Logf("", "block height %d, template id %s, version %d", block.Side.Height, calculatedBlockId, block.ShareVersion()) - - _ = os.WriteFile(path.Join(*outputFolder, expectedBlockId.String()+".raw"), blob, 0664) - } - }, - } - - cache.LoadAll(l) -} diff --git a/cmd/recoverpoolblock/go.mod b/cmd/recoverpoolblock/go.mod deleted file mode 100644 index 039f9ac..0000000 --- a/cmd/recoverpoolblock/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/recoverpoolblock - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 => ../../p2pool/cache - -require ( - git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523 - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache v0.0.0 - git.gammaspectra.live/P2Pool/sha3 v0.0.0-20230604092430-04fe7dc6439a - github.com/floatdrop/lru v1.3.0 -) - -require ( - git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33 // 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 - 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/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.9 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/recoverpoolblock/go.sum b/cmd/recoverpoolblock/go.sum deleted file mode 100644 index 188f34f..0000000 --- a/cmd/recoverpoolblock/go.sum +++ /dev/null @@ -1,47 +0,0 @@ -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/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/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.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/recoverpoolblock/recoverpoolblock.go b/cmd/recoverpoolblock/recoverpoolblock.go deleted file mode 100644 index 395fe40..0000000 --- a/cmd/recoverpoolblock/recoverpoolblock.go +++ /dev/null @@ -1,517 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/binary" - "encoding/hex" - "errors" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache/archive" - crypto2 "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mempool" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "git.gammaspectra.live/P2Pool/sha3" - "github.com/floatdrop/lru" - "math" - "os" - "runtime" - "slices" - "sync" - "sync/atomic" -) - -func main() { - inputConsensus := flag.String("consensus", "config.json", "Input config.json consensus file") - inputArchive := flag.String("input", "", "Input path for archive database") - 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") - inputTemplate := flag.String("template", "", "Template data for share recovery") - - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - cf, err := os.ReadFile(*inputConsensus) - if err != nil { - utils.Panic(err) - } - inputTemplateData, err := os.ReadFile(*inputTemplate) - if err != nil { - utils.Panic(err) - } - - jsonTemplate, err := JSONFromTemplate(inputTemplateData) - if err != nil { - utils.Panic(err) - } - - consensus, err := sidechain.NewConsensusFromJSON(cf) - if err != nil { - utils.Panic(err) - } - - var headerCacheLock sync.RWMutex - headerByHeightCache := make(map[uint64]*daemon.BlockHeader) - - getHeaderByHeight := func(height uint64) *daemon.BlockHeader { - if v := func() *daemon.BlockHeader { - headerCacheLock.RLock() - defer headerCacheLock.RUnlock() - return headerByHeightCache[height] - }(); v == nil { - if r, err := client.GetDefaultClient().GetBlockHeaderByHeight(height, context.Background()); err == nil { - headerCacheLock.Lock() - defer headerCacheLock.Unlock() - headerByHeightCache[r.BlockHeader.Height] = &r.BlockHeader - return &r.BlockHeader - } - return nil - } else { - return v - } - } - - getDifficultyByHeight := func(height uint64) types.Difficulty { - if v := getHeaderByHeight(height); v != nil { - return types.DifficultyFrom64(v.Difficulty) - } - return types.ZeroDifficulty - } - - getSeedByHeight := func(height uint64) (hash types.Hash) { - seedHeight := randomx.SeedHeight(height) - if v := getHeaderByHeight(seedHeight); v != nil { - h, _ := types.HashFromString(v.Hash) - return h - } - return types.ZeroHash - } - - _ = getSeedByHeight - - archiveCache, err := archive.NewCache(*inputArchive, consensus, getDifficultyByHeight) - if err != nil { - utils.Panic(err) - } - defer archiveCache.Close() - - blockCache := lru.New[types.Hash, *sidechain.PoolBlock](int(consensus.ChainWindowSize * 4)) - - derivationCache := sidechain.NewDerivationLRUCache() - getByTemplateIdDirect := func(h types.Hash) *sidechain.PoolBlock { - if v := blockCache.Get(h); v == nil { - if bs := archiveCache.LoadByTemplateId(h); len(bs) != 0 { - blockCache.Set(h, bs[0]) - return bs[0] - } else { - return nil - } - } else { - return *v - } - } - - processBlock := func(b *sidechain.PoolBlock) error { - var preAllocatedShares sidechain.Shares - if len(b.Main.Coinbase.Outputs) == 0 { - //cannot use SideTemplateId() as it might not be proper to calculate yet. fetch from coinbase only here - if b2 := getByTemplateIdDirect(types.HashFromBytes(b.CoinbaseExtra(sidechain.SideTemplateId))); b2 != nil && len(b2.Main.Coinbase.Outputs) != 0 { - b.Main.Coinbase.Outputs = b2.Main.Coinbase.Outputs - } else { - preAllocatedShares = sidechain.PreAllocateShares(consensus.ChainWindowSize * 2) - } - } - - _, err := b.PreProcessBlock(consensus, derivationCache, preAllocatedShares, getDifficultyByHeight, getByTemplateIdDirect) - return err - } - - getByTemplateId := func(h types.Hash) *sidechain.PoolBlock { - if v := getByTemplateIdDirect(h); v != nil { - if processBlock(v) != nil { - return nil - } - return v - } else { - return nil - } - } - - var poolBlock *sidechain.PoolBlock - var expectedMainId types.Hash - var expectedTemplateId types.Hash - var expectedCoinbaseId types.Hash - var baseMainReward, deltaReward uint64 - - getTransactionsEntries := func(h ...types.Hash) (r mempool.Mempool) { - if data, jsonTx, err := client.GetDefaultClient().GetTransactions(h...); err != nil { - utils.Errorf("", "could not get txids: %s", err) - return nil - } else { - r = make(mempool.Mempool, len(jsonTx)) - for i, tx := range jsonTx { - r[i] = mempool.NewEntryFromRPCData(h[i], data[i], tx) - } - - return r - } - } - - if v2, ok := jsonTemplate.(JsonBlock2); ok { - parentBlock := getByTemplateId(v2.PrevId) - - keyPair := crypto.NewKeyPairFromPrivate(&v2.CoinbasePriv) - expectedMainId = v2.MainId - expectedCoinbaseId = v2.CoinbaseId - expectedTemplateId = v2.Id - - header := getHeaderByHeight(v2.MainHeight) - - var nextBlock *sidechain.PoolBlock - for _, t2 := range archiveCache.LoadBySideChainHeight(v2.Height + 1) { - if t2.Side.Parent == expectedTemplateId { - if err := processBlock(t2); err == nil { - nextBlock = t2 - break - } - } - } - - if parentBlock == nil { - parentBlock = getByTemplateIdDirect(v2.PrevId) - } - if parentBlock == nil { - //use forward method - } else { - - if parentBlock.Main.Coinbase.GenHeight == v2.MainHeight { - baseMainReward = parentBlock.Main.Coinbase.TotalReward - for _, tx := range getTransactionsEntries(parentBlock.Main.Transactions...) { - baseMainReward -= tx.Fee - } - - deltaReward = v2.CoinbaseReward - baseMainReward - } else if nextBlock != nil && nextBlock.Main.Coinbase.GenHeight == v2.MainHeight { - for _, tx := range getTransactionsEntries(nextBlock.Main.Transactions...) { - baseMainReward -= tx.Fee - } - - deltaReward = v2.CoinbaseReward - baseMainReward - } else { - //fallback method can fail on very full blocks - headerId, _ := types.HashFromString(header.Hash) - - if b, err := client.GetDefaultClient().GetBlock(headerId, context.Background()); err != nil { - utils.Panic(err) - } else { - //generalization, actual reward could be different in some cases - ij, _ := b.InnerJSON() - baseMainReward = b.BlockHeader.Reward - var txs []types.Hash - for _, txid := range ij.TxHashes { - h, _ := types.HashFromString(txid) - txs = append(txs, h) - } - for _, tx := range getTransactionsEntries(txs...) { - baseMainReward -= tx.Fee - } - } - - deltaReward = v2.CoinbaseReward - baseMainReward - } - - utils.Logf("", "pool delta reward: %d (%s), base %d (%s), expected %d (%s)", deltaReward, utils.XMRUnits(deltaReward), baseMainReward, utils.XMRUnits(baseMainReward), v2.CoinbaseReward, utils.XMRUnits(v2.CoinbaseReward)) - - poolBlock = &sidechain.PoolBlock{ - Main: block.Block{ - MajorVersion: uint8(header.MajorVersion), - MinorVersion: uint8(header.MinorVersion), - Timestamp: v2.Ts, - PreviousId: v2.MinerMainId, - Coinbase: transaction.CoinbaseTransaction{ - Version: 2, - UnlockTime: v2.MainHeight + monero.MinerRewardUnlockTime, - InputCount: 1, - InputType: transaction.TxInGen, - GenHeight: v2.MainHeight, - TotalReward: v2.CoinbaseReward, - Extra: transaction.ExtraTags{ - transaction.ExtraTag{ - Tag: transaction.TxExtraTagPubKey, - VarInt: 0, - Data: types.Bytes(keyPair.PublicKey.AsSlice()), - }, - transaction.ExtraTag{ - Tag: transaction.TxExtraTagNonce, - VarInt: 4, - HasVarInt: true, - Data: make(types.Bytes, 4), //TODO: expand nonce size as needed - }, - transaction.ExtraTag{ - Tag: transaction.TxExtraTagMergeMining, - VarInt: 32, - HasVarInt: true, - Data: v2.Id[:], - }, - }, - ExtraBaseRCT: 0, - }, - Transactions: nil, - }, - Side: sidechain.SideData{ - PublicKey: v2.Wallet.ToPackedAddress(), - CoinbasePrivateKey: keyPair.PrivateKey.AsBytes(), - Parent: v2.PrevId, - Uncles: func() (result []types.Hash) { - for _, u := range v2.Uncles { - result = append(result, u.Id) - } - return result - }(), - Height: v2.Height, - Difficulty: v2.Diff, - CumulativeDifficulty: parentBlock.Side.CumulativeDifficulty.Add(v2.Diff), - // no extrabuffer - }, - } - poolBlock.CachedShareVersion = poolBlock.CalculateShareVersion(consensus) - } - - poolBlock.Depth.Store(math.MaxUint64) - - if poolBlock.ShareVersion() > sidechain.ShareVersion_V1 { - poolBlock.Side.CoinbasePrivateKeySeed = parentBlock.Side.CoinbasePrivateKeySeed - if parentBlock.Main.PreviousId != poolBlock.Main.PreviousId { - poolBlock.Side.CoinbasePrivateKeySeed = parentBlock.CalculateTransactionPrivateKeySeed() - } - } else { - expectedSeed := poolBlock.CalculateTransactionPrivateKeySeed() - kP := crypto.NewKeyPairFromPrivate(crypto2.GetDeterministicTransactionPrivateKey(expectedSeed, poolBlock.Main.PreviousId)) - - if bytes.Compare(poolBlock.CoinbaseExtra(sidechain.SideCoinbasePublicKey), kP.PublicKey.AsSlice()) == 0 && bytes.Compare(kP.PrivateKey.AsSlice(), poolBlock.Side.CoinbasePrivateKey[:]) == 0 { - poolBlock.Side.CoinbasePrivateKeySeed = expectedSeed - } else { - utils.Logf("", "not deterministic private key") - } - } - - currentOutputs, _ := sidechain.CalculateOutputs(poolBlock, consensus, getDifficultyByHeight, getByTemplateIdDirect, derivationCache, sidechain.PreAllocateShares(consensus.ChainWindowSize*2), make([]uint64, consensus.ChainWindowSize*2)) - - if currentOutputs == nil { - utils.Panicf("could not calculate outputs blob") - } - poolBlock.Main.Coinbase.Outputs = currentOutputs - - if blob, err := currentOutputs.MarshalBinary(); err != nil { - utils.Panic(err) - } else { - poolBlock.Main.Coinbase.OutputsBlobSize = uint64(len(blob)) - } - utils.Logf("", "expected main id %s, template id %s, coinbase id %s", expectedMainId, expectedTemplateId, expectedCoinbaseId) - - rctHash := crypto.Keccak256([]byte{0}) - type partialBlobWork struct { - Hashers [2]*sha3.HasherState - Tx *transaction.CoinbaseTransaction - EncodedBuffer []byte - EncodedOffset int - TempHash types.Hash - } - var stop atomic.Bool - - var foundExtraNonce atomic.Uint32 - var foundExtraNonceSize atomic.Uint32 - - minerTxBlob, _ := poolBlock.Main.Coinbase.MarshalBinary() - - searchForNonces := func(nonceSize int, max uint64) { - coinbases := make([]*partialBlobWork, runtime.NumCPU()) - utils.SplitWork(0, max, func(workIndex uint64, routineIndex int) error { - if stop.Load() { - return errors.New("found nonce") - } - w := coinbases[routineIndex] - if workIndex%(1024*256) == 0 { - utils.Logf("", "try %d/%d @ %d ~%.2f%%", workIndex, max, nonceSize, (float64(workIndex)/math.MaxUint32)*100) - } - binary.LittleEndian.PutUint32(w.EncodedBuffer[w.EncodedOffset:], uint32(workIndex)) - idHasher := w.Hashers[0] - txHasher := w.Hashers[1] - - txHasher.Write(w.EncodedBuffer) - crypto.HashFastSum(txHasher, w.TempHash[:]) - - idHasher.Write(w.TempHash[:]) - // Base RCT, single 0 byte in miner tx - idHasher.Write(rctHash[:]) - // Prunable RCT, empty in miner tx - idHasher.Write(types.ZeroHash[:]) - crypto.HashFastSum(idHasher, w.TempHash[:]) - - if w.TempHash == expectedCoinbaseId { - foundExtraNonce.Store(uint32(workIndex)) - foundExtraNonceSize.Store(uint32(nonceSize)) - //FOUND! - stop.Store(true) - return errors.New("found nonce") - } - - idHasher.Reset() - txHasher.Reset() - - return nil - }, func(routines, routineIndex int) error { - if len(coinbases) < routines { - coinbases = slices.Grow(coinbases, routines) - } - - tx := &transaction.CoinbaseTransaction{} - if err := tx.UnmarshalBinary(minerTxBlob); err != nil { - return err - } - tx.Extra[1].VarInt = uint64(nonceSize) - tx.Extra[1].Data = make([]byte, nonceSize) - - buf, _ := tx.MarshalBinary() - - coinbases[routineIndex] = &partialBlobWork{ - Hashers: [2]*sha3.HasherState{crypto.GetKeccak256Hasher(), crypto.GetKeccak256Hasher()}, - Tx: tx, - EncodedBuffer: buf[:len(buf)-1], /* remove RCT */ - EncodedOffset: len(buf) - 1 - (types.HashSize + 1 + 1 /*Merge mining tag*/) - nonceSize, - } - - return nil - }, nil) - } - nonceSize := sidechain.SideExtraNonceSize - - //do quick search first - for ; nonceSize <= sidechain.SideExtraNonceMaxSize; nonceSize++ { - if stop.Load() { - break - } - searchForNonces(nonceSize, math.MaxUint16) - } - //do deeper search next - for nonceSize = sidechain.SideExtraNonceSize; nonceSize <= sidechain.SideExtraNonceMaxSize; nonceSize++ { - if stop.Load() { - break - } - searchForNonces(nonceSize, math.MaxUint32) - } - - utils.Logf("", "found extra nonce %d, size %d", foundExtraNonce.Load(), foundExtraNonceSize.Load()) - poolBlock.Main.Coinbase.Extra[1].VarInt = uint64(foundExtraNonceSize.Load()) - poolBlock.Main.Coinbase.Extra[1].Data = make([]byte, foundExtraNonceSize.Load()) - binary.LittleEndian.PutUint32(poolBlock.Main.Coinbase.Extra[1].Data, foundExtraNonce.Load()) - - if poolBlock.Main.Coinbase.CalculateId() != expectedCoinbaseId { - utils.Panic() - } - - utils.Logf("", "got coinbase id %s", poolBlock.Main.Coinbase.CalculateId()) - minerTxBlob, _ = poolBlock.Main.Coinbase.MarshalBinary() - utils.Logf("", "raw coinbase: %s", hex.EncodeToString(minerTxBlob)) - - var collectedTransactions mempool.Mempool - - collectTxs := func(hashes ...types.Hash) { - var txs []types.Hash - for _, h := range hashes { - if slices.ContainsFunc(collectedTransactions, func(entry *mempool.MempoolEntry) bool { - return entry.Id == h - }) { - continue - } - txs = append(txs, h) - } - - collectedTransactions = append(collectedTransactions, getTransactionsEntries(txs...)...) - } - - collectTxsHex := func(hashes ...string) { - var txs []types.Hash - for _, txid := range hashes { - h, _ := types.HashFromString(txid) - if slices.ContainsFunc(collectedTransactions, func(entry *mempool.MempoolEntry) bool { - return entry.Id == h - }) { - continue - } - txs = append(txs, h) - } - - collectedTransactions = append(collectedTransactions, getTransactionsEntries(txs...)...) - } - - collectTxs(parentBlock.Main.Transactions...) - if nextBlock != nil { - collectTxs(nextBlock.Main.Transactions...) - } - - if bh, err := client.GetDefaultClient().GetBlockByHeight(poolBlock.Main.Coinbase.GenHeight, context.Background()); err == nil { - ij, _ := bh.InnerJSON() - collectTxsHex(ij.TxHashes...) - } - - /*if bh, err := client.GetDefaultClient().GetBlockByHeight(poolBlock.Main.Coinbase.GenHeight+1, context.Background()); err == nil { - ij, _ := bh.InnerJSON() - collectTxsHex(ij.TxHashes...) - }*/ - - for _, uncleId := range poolBlock.Side.Uncles { - if u := getByTemplateId(uncleId); u != nil { - if u.Main.Coinbase.GenHeight == poolBlock.Main.Coinbase.GenHeight { - collectTxs(u.Main.Transactions...) - } - } - } - - if bh, err := client.GetDefaultClient().GetBlockByHeight(poolBlock.Main.Coinbase.GenHeight-1, context.Background()); err == nil { - ij, _ := bh.InnerJSON() - for _, txH := range ij.TxHashes { - //remove mined tx - txid, _ := types.HashFromString(txH) - if i := slices.IndexFunc(collectedTransactions, func(entry *mempool.MempoolEntry) bool { - return entry.Id == txid - }); i != -1 { - collectedTransactions = slices.Delete(collectedTransactions, i, i+1) - } - } - } - - minerTxWeight := uint64(len(minerTxBlob)) - totalTxWeight := collectedTransactions.Weight() - - medianWeight := getHeaderByHeight(poolBlock.Main.Coinbase.GenHeight).LongTermWeight - - utils.Logf("", "collected transaction candidates: %d", len(collectedTransactions)) - - if totalTxWeight+minerTxWeight <= medianWeight { - //special case - for solution := range collectedTransactions.PerfectSum(deltaReward) { - utils.Logf("", "got %d, %d (%s)", solution.Weight(), solution.Fees(), utils.XMRUnits(solution.Fees())) - } - } else { - //sort in preference order - pickedTxs := collectedTransactions.Pick(baseMainReward, minerTxWeight, medianWeight) - - utils.Logf("", "got %d, %d (%s)", pickedTxs.Weight(), pickedTxs.Fees(), utils.XMRUnits(pickedTxs.Fees())) - } - - //TODO: nonce - } - -} diff --git a/cmd/recoverpoolblock/template.go b/cmd/recoverpoolblock/template.go deleted file mode 100644 index f1d3735..0000000 --- a/cmd/recoverpoolblock/template.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" -) - -type JsonBlock2 struct { - MinerMainId types.Hash `json:"miner_main_id"` - CoinbaseReward uint64 `json:"coinbase_reward,string"` - CoinbaseId types.Hash `json:"coinbase_id"` - Version uint64 `json:"version,string"` - Diff types.Difficulty `json:"diff"` - Wallet *address.Address `json:"wallet"` - MinerMainDiff types.Difficulty `json:"miner_main_diff"` - Id types.Hash `json:"id"` - Height uint64 `json:"height,string"` - PowHash types.Hash `json:"pow_hash"` - MainId types.Hash `json:"main_id"` - MainHeight uint64 `json:"main_height,string"` - Ts uint64 `json:"ts,string"` - PrevId types.Hash `json:"prev_id"` - CoinbasePriv crypto.PrivateKeyBytes `json:"coinbase_priv"` - Lts uint64 `json:"lts,string"` - MainFound string `json:"main_found,omitempty"` - - Uncles []struct { - MinerMainId types.Hash `json:"miner_main_id"` - CoinbaseReward uint64 `json:"coinbase_reward,string"` - CoinbaseId types.Hash `json:"coinbase_id"` - Version uint64 `json:"version,string"` - Diff types.Difficulty `json:"diff"` - Wallet *address.Address `json:"wallet"` - MinerMainDiff types.Difficulty `json:"miner_main_diff"` - Id types.Hash `json:"id"` - Height uint64 `json:"height,string"` - PowHash types.Hash `json:"pow_hash"` - MainId types.Hash `json:"main_id"` - MainHeight uint64 `json:"main_height,string"` - Ts uint64 `json:"ts,string"` - PrevId types.Hash `json:"prev_id"` - CoinbasePriv crypto.PrivateKeyBytes `json:"coinbase_priv"` - Lts uint64 `json:"lts,string"` - MainFound string `json:"main_found,omitempty"` - } `json:"uncles,omitempty"` -} - -type JsonBlock1 struct { - Wallet *address.Address `json:"wallet"` - Height uint64 `json:"height,string"` - MHeight uint64 `json:"mheight,string"` - PrevId types.Hash `json:"prev_id"` - Ts uint64 `json:"ts,string"` - PowHash types.Hash `json:"pow_hash"` - Id types.Hash `json:"id"` - PrevHash types.Hash `json:"prev_hash"` - Diff uint64 `json:"diff,string"` - TxCoinbase types.Hash `json:"tx_coinbase"` - Lts uint64 `json:"lts,string"` - MHash types.Hash `json:"mhash"` - TxPriv crypto.PrivateKeyBytes `json:"tx_priv"` - TxPub crypto.PublicKeyBytes `json:"tx_pub"` - BlockFound string `json:"main_found,omitempty"` - - Uncles []struct { - Diff uint64 `json:"diff,string"` - PrevId types.Hash `json:"prev_id"` - Ts uint64 `json:"ts,string"` - MHeight uint64 `json:"mheight,string"` - PrevHash types.Hash `json:"prev_hash"` - Height uint64 `json:"height,string"` - Wallet *address.Address `json:"wallet"` - Id types.Hash `json:"id"` - } `json:"uncles,omitempty"` -} - -type versionBlock struct { - Version uint64 `json:"version,string"` -} - -func JSONFromTemplate(data []byte) (any, error) { - var version versionBlock - - if err := utils.UnmarshalJSON(data, &version); err != nil { - return nil, err - } else { - if version.Version == 2 { - var b JsonBlock2 - if err = utils.UnmarshalJSON(data, &b); err != nil { - return nil, err - } - return b, nil - } else if version.Version == 0 || version.Version == 1 { - - var b JsonBlock1 - if err = utils.UnmarshalJSON(data, &b); err != nil { - return nil, err - } - return b, nil - } else { - return nil, fmt.Errorf("unknown version %d", version.Version) - } - } -} diff --git a/cmd/scansweeps/go.mod b/cmd/scansweeps/go.mod deleted file mode 100644 index 8608fff..0000000 --- a/cmd/scansweeps/go.mod +++ /dev/null @@ -1,38 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/scansweeps - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 -) - -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/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/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/scansweeps/go.sum b/cmd/scansweeps/go.sum deleted file mode 100644 index 66d7043..0000000 --- a/cmd/scansweeps/go.sum +++ /dev/null @@ -1,46 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/scansweeps/scansweeps.go b/cmd/scansweeps/scansweeps.go deleted file mode 100644 index 440761c..0000000 --- a/cmd/scansweeps/scansweeps.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - p2poolapi "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "time" -) - -func main() { - 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") - startFromHeight := flag.Uint64("from", 0, "Start sweep from this main height") - dbString := flag.String("db", "", "") - p2poolApiHost := flag.String("api-host", "", "Host URL for p2pool go observer consensus") - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - client.GetDefaultClient().SetThrottle(1000) - - p2api := p2poolapi.NewP2PoolApi(*p2poolApiHost) - - if err := p2api.WaitSync(); err != nil { - utils.Panic(err) - } - - indexDb, err := index.OpenIndex(*dbString, p2api.Consensus(), p2api.DifficultyByHeight, p2api.SeedByHeight, p2api.ByTemplateId) - if err != nil { - utils.Panic(err) - } - defer indexDb.Close() - - startHeight := *startFromHeight - - var tipHeight uint64 - - if err := indexDb.Query("SELECT MIN(height), MAX(height) FROM main_blocks WHERE height > 0;", func(row index.RowScanInterface) error { - var minHeight uint64 - if err := row.Scan(&minHeight, &tipHeight); err != nil { - return err - } - - if minHeight > startHeight { - startHeight = minHeight - } - return nil - }); err != nil { - utils.Panic(err) - } - - stopHeight := tipHeight - monero.MinerRewardUnlockTime - if tipHeight < monero.MinerRewardUnlockTime { - stopHeight = tipHeight - } - - utils.Logf("", "Starting at height %d, stopping at %d", startHeight, stopHeight) - - isProcessedPrevious := true - for height := startHeight; height <= stopHeight; height++ { - b := indexDb.GetMainBlockByHeight(height) - if b == nil { - utils.Logf("", "Block at %d is nil", height) - continue - } - - if isProcessed, ok := b.GetMetadata("processed").(bool); ok && isProcessed && isProcessedPrevious { - utils.Logf("", "Block %s at %d (%s) has already been processed", b.Id, b.Height, time.Unix(int64(b.Timestamp), 0).UTC().Format("02-01-2006 15:04:05 MST")) - continue - } - - isProcessedPrevious = false - - if err := cmdutils.ProcessFullBlock(b, indexDb); err != nil { - utils.Logf("", "error processing block %s at %d: %s", b.Id, b.Height, err) - } - } - -} diff --git a/cmd/utils/api_types.go b/cmd/utils/api_types.go deleted file mode 100644 index 36d7388..0000000 --- a/cmd/utils/api_types.go +++ /dev/null @@ -1,213 +0,0 @@ -package utils - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - types2 "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "strings" - "time" -) - -const ( - JSONEventSideBlock = "side_block" - JSONEventFoundBlock = "found_block" - JSONEventOrphanedBlock = "orphaned_block" - JSONEventPayout = "payout" -) - -type JSONEvent struct { - Type string `json:"type"` - SideBlock *index.SideBlock `json:"side_block,omitempty"` - FoundBlock *index.FoundBlock `json:"found_block,omitempty"` - MainCoinbaseOutputs index.MainCoinbaseOutputs `json:"main_coinbase_outputs,omitempty"` - Payout *index.Payout `json:"payout,omitempty"` -} - -type VersionInfo struct { - Version string `json:"version"` - Timestamp int64 `json:"timestamp"` - Link string `json:"link"` - CheckedTimestamp int64 `json:"-"` -} - -func (v VersionInfo) ShortVersion() types2.SemanticVersion { - parts := strings.Split(v.Version, ".") - for len(parts) < 2 { - parts = append(parts, "0") - } - for len(parts) > 2 { - parts = parts[:len(parts)-1] - } - return types2.SemanticVersionFromString(strings.Join(parts, ".")) -} - -type ReleaseDataJson struct { - TagName string `json:"tag_name"` - TargetCommitish string `json:"target_commitish"` - Name string `json:"name"` - PublishedAt time.Time `json:"published_at"` -} - -type SideChainVersionEntry struct { - Weight types.Difficulty `json:"weight"` - Share float64 `json:"share"` - Count int `json:"count"` - SoftwareId types2.SoftwareId `json:"software_id"` - SoftwareVersion types2.SoftwareVersion `json:"software_version"` - SoftwareString string `json:"software_string"` -} - -type PoolInfoResult struct { - SideChain PoolInfoResultSideChain `json:"sidechain"` - MainChain PoolInfoResultMainChain `json:"mainchain"` - Versions struct { - P2Pool VersionInfo `json:"p2pool"` - Monero VersionInfo `json:"monero"` - } `json:"versions"` -} - -type PoolInfoResultSideChain struct { - // 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 - MaxWindowSize int `json:"max_window_size"` - - // BlockTime Available on Consensus - // Deprecated - BlockTime int `json:"block_time"` - - // UnclePenalty Available on Consensus - // Deprecated - UnclePenalty int `json:"uncle_penalty"` -} - -type PoolInfoResultSideChainEffortLastEntry struct { - Id types.Hash `json:"id"` - Effort float64 `json:"effort"` -} - -type PoolInfoResultSideChainEffort struct { - Current float64 `json:"current"` - Average10 float64 `json:"average10"` - Average50 float64 `json:"average"` - Average200 float64 `json:"average200"` - Last []PoolInfoResultSideChainEffortLastEntry `json:"last"` -} -type PoolInfoResultSideChainWindow struct { - // 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"` - TransactionUnlockTime uint64 `json:"transaction_unlock_time"` - MinerRewardUnlockTime uint64 `json:"miner_reward_unlock_time"` - - // HardForkSupportedVersion - HardForkSupportedVersion uint8 `json:"hard_fork_supported_version"` - // HardForks HardFork information for Monero known hardfork by backing p2pool - HardForks []sidechain.HardFork `json:"hard_forks,omitempty"` -} - -type PoolInfoResultMainChain struct { - 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 included in Consensus - // Deprecated - BlockTime int `json:"block_time"` -} - -type MinerInfoBlockData struct { - ShareCount uint64 `json:"shares"` - UncleCount uint64 `json:"uncles"` - LastShareHeight uint64 `json:"last_height"` -} - -type MinerInfoResult struct { - Id uint64 `json:"id"` - Address *address.Address `json:"address"` - Alias string `json:"alias,omitempty"` - Shares [index.InclusionCount]MinerInfoBlockData `json:"shares"` - LastShareHeight uint64 `json:"last_share_height"` - LastShareTimestamp uint64 `json:"last_share_timestamp"` -} - -type TransactionLookupResult struct { - Id types.Hash `json:"id"` - Inputs index.TransactionInputQueryResults `json:"inputs"` - Outs []client.Output `json:"outs"` - Match []index.TransactionInputQueryResultsMatch `json:"matches"` -} diff --git a/cmd/utils/coinbase.go b/cmd/utils/coinbase.go deleted file mode 100644 index 8180b01..0000000 --- a/cmd/utils/coinbase.go +++ /dev/null @@ -1,302 +0,0 @@ -package utils - -import ( - "context" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/floatdrop/lru" - "math" -) - -// keep last few main blocks -var mainBlockCache = lru.New[types.Hash, *block.Block](100) - -func GetMainBlock(id types.Hash, client *client.Client) (*block.Block, error) { - if bb := mainBlockCache.Get(id); bb == nil { - blockData, err := client.GetBlock(id, context.Background()) - if err != nil { - return nil, err - } - bufData, err := hex.DecodeString(blockData.Blob) - if err != nil { - return nil, err - } - mainBlock := &block.Block{} - err = mainBlock.UnmarshalBinary(bufData) - if err != nil { - return nil, err - } - if len(mainBlock.Coinbase.Outputs) == 0 { - //should never happen from monero itself - return nil, errors.New("outputs not filled") - } - mainBlockCache.Set(id, mainBlock) - return mainBlock, nil - } else { - return *bb, nil - } -} - -func FindAndInsertMainHeader(h daemon.BlockHeader, indexDb *index.Index, storeFunc func(b *sidechain.PoolBlock), client *client.Client, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc, getByMainId sidechain.GetByMainIdFunc, getByMainHeight sidechain.GetByMainHeightFunc, processBlock func(b *sidechain.PoolBlock) error) error { - mainId, _ := types.HashFromString(h.Hash) - coinbaseId, _ := types.HashFromString(h.MinerTxHash) - prevHash, _ := types.HashFromString(h.PrevHash) - - var indexMain *index.MainBlock - indexMain = indexDb.GetMainBlockByHeight(h.Height) - if indexMain == nil { - indexMain = &index.MainBlock{} - } - if indexMain.Id != mainId { - //Zero data if they don't match to insert new height - indexMain.Metadata = make(map[string]any) - indexMain.SideTemplateId = types.ZeroHash - indexMain.CoinbasePrivateKey = crypto.PrivateKeyBytes{} - } - indexMain.Id = mainId - indexMain.Height = h.Height - indexMain.Timestamp = uint64(h.Timestamp) - indexMain.Reward = h.Reward - indexMain.CoinbaseId = coinbaseId - indexMain.Difficulty = h.Difficulty - - // Find possible candidates that match on main height, main previous id, header nonce, timestamp, and count of transactions to reduce full block lookups - var candidates sidechain.UniquePoolBlockSlice - for _, b := range getByMainHeight(h.Height) { - if b.Main.PreviousId == prevHash && b.Main.Coinbase.TotalReward == h.Reward && b.Main.Timestamp == uint64(h.Timestamp) && len(b.Main.Transactions) == int(h.NumTxes) { - // candidate for checking - candidates = append(candidates, b) - } - } - - if len(candidates) == 0 { - // no candidates, insert header as-is - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - return nil - } - - utils.Logf("Monero", "checking block %s at height %d: %d candidate(s)", mainId, h.Height, len(candidates)) - - mainBlock, err := GetMainBlock(mainId, client) - if err != nil { - utils.Errorf("Monero", "could not get main block: %e", err) - // insert errored block as-is - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - return nil - } - var sideTemplateId types.Hash - var extraNonce []byte - - // try to fetch extra nonce if existing - { - if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideExtraNonce)); t != nil { - if len(t.Data) >= sidechain.SideExtraNonceSize || len(t.Data) < sidechain.SideExtraNonceMaxSize { - extraNonce = t.Data - } - } - } - - // get a merge tag if existing - { - if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideTemplateId)); t != nil { - if len(t.Data) == types.HashSize { - sideTemplateId = types.HashFromBytes(t.Data) - } - } - } - - if sideTemplateId == types.ZeroHash || len(extraNonce) == 0 { - utils.Logf("Monero", "checking block %s at height %d: not a p2pool block", mainId, h.Height) - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - return nil - } - - if t := getByTemplateId(sideTemplateId); t != nil { - //found template - if err := processBlock(t); err != nil { - return fmt.Errorf("could not process template: %w", err) - } - - if t.MainId() == mainId { //exact match, no need to re-arrange template inclusion - sideBlock := indexDb.GetSideBlockByMainId(t.MainId()) - if sideBlock == nil { - //found deep orphan which found block, insert parents - cur := t - for cur != nil && !index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(cur.SideTemplateId(indexDb.Consensus()))) { - orphanBlock, orphanUncles, err := indexDb.GetSideBlockFromPoolBlock(cur, index.InclusionOrphan) - if err != nil { - break - } - - if index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(orphanBlock.TemplateId)) { - continue - } - if err := indexDb.InsertOrUpdateSideBlock(orphanBlock); err != nil { - return fmt.Errorf("error inserting %s, %s at %d: %s", cur.SideTemplateId(indexDb.Consensus()), cur.MainId(), cur.Side.Height, err) - } - - for _, orphanUncle := range orphanUncles { - if index.QueryHasResults(indexDb.GetSideBlocksByTemplateId(orphanUncle.TemplateId)) { - continue - } - if err := indexDb.InsertOrUpdateSideBlock(orphanUncle); err != nil { - return fmt.Errorf("error inserting uncle of %s, %s; %s, %s at %d: %s", cur.SideTemplateId(indexDb.Consensus()), cur.MainId(), orphanUncle.TemplateId, orphanUncle.MainId, cur.Side.Height, err) - } - } - - cur = getByTemplateId(cur.Side.Parent) - if cur == nil { - break - } - if err := processBlock(cur); err != nil { - return fmt.Errorf("could not process cur template: %w", err) - } - } - sideBlock = indexDb.GetSideBlockByMainId(t.MainId()) - if sideBlock == nil { - return errors.New("no block in database for orphan") - } - sideBlock.Inclusion = index.InclusionOrphan - } else if sideBlock.Inclusion == index.InclusionAlternateInVerifiedChain { - sideBlock.Inclusion = index.InclusionInVerifiedChain - } - - if err := indexDb.InsertOrUpdateSideBlock(sideBlock); err != nil { - return fmt.Errorf("error inserting %s, %s at %d: %s", sideBlock.TemplateId, sideBlock.MainId, sideBlock.SideHeight, err) - } - t.Depth.Store(math.MaxUint64) - storeFunc(t) - - indexMain.SideTemplateId = sideTemplateId - indexMain.CoinbasePrivateKey = t.Side.CoinbasePrivateKey - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - utils.Logf("Monero", "INSERTED found block %s at height %d, template id %s", mainId, h.Height, sideTemplateId) - if err := FindAndInsertMainHeaderOutputs(indexMain, indexDb, client, difficultyByHeight, getByTemplateId, getByMainId, getByMainHeight, processBlock); err != nil { - utils.Errorf("", "error inserting coinbase outputs: %s", err) - } - return nil - } else { - data, _ := t.MarshalBinary() - newBlock := &sidechain.PoolBlock{} - if err := newBlock.UnmarshalBinary(indexDb.Consensus(), &sidechain.NilDerivationCache{}, data); err != nil { - utils.Panic(err) - } - copy(newBlock.CoinbaseExtra(sidechain.SideExtraNonce), extraNonce) - newBlock.Main.Nonce = uint32(h.Nonce) - newBlock.Depth.Store(math.MaxUint64) - - if newBlock.MainId() == mainId { - //store into archive - storeFunc(newBlock) - //insert into db as well - var sideBlock *index.SideBlock - if indexDb.GetSideBlockByMainId(t.MainId()).Inclusion == index.InclusionOrphan { - sideBlock, _, err = indexDb.GetSideBlockFromPoolBlock(newBlock, index.InclusionOrphan) - } else { - sideBlock, _, err = indexDb.GetSideBlockFromPoolBlock(newBlock, index.InclusionInVerifiedChain) - } - if err != nil { - return fmt.Errorf("could not process %s, at side height %d, template id %s: %w", types.HashFromBytes(t.CoinbaseExtra(sidechain.SideTemplateId)), t.Side.Height, sideTemplateId, err) - } - - if err := indexDb.InsertOrUpdateSideBlock(sideBlock); err != nil { - return fmt.Errorf("error inserting %s, %s at %d: %s", sideBlock.TemplateId, sideBlock.MainId, sideBlock.SideHeight, err) - } - - indexMain.SideTemplateId = sideTemplateId - indexMain.CoinbasePrivateKey = newBlock.Side.CoinbasePrivateKey - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - utils.Logf("", "INSERTED ALTERNATE found block %s at height %d, template id %s", mainId, h.Height, sideTemplateId) - if err := FindAndInsertMainHeaderOutputs(indexMain, indexDb, client, difficultyByHeight, getByTemplateId, getByMainId, getByMainHeight, processBlock); err != nil { - utils.Errorf("", "error inserting coinbase outputs: %s", err) - } - return nil - } - } - } - - utils.Logf("", "checking block %s at height %d, template id %s: could not find matching template id", mainId, h.Height, sideTemplateId) - - // could not find template, maybe it's other pool? - - // fill template id for future reference - // TODO: do this for all blocks - indexMain.SetMetadata("merge_mining_tag", sideTemplateId) - indexMain.SetMetadata("extra_nonce", binary.LittleEndian.Uint32(extraNonce)) - - if err := indexDb.InsertOrUpdateMainBlock(indexMain); err != nil { - return err - } - return nil -} - -func FindAndInsertMainHeaderOutputs(mb *index.MainBlock, indexDb *index.Index, client *client.Client, difficultyByHeight block.GetDifficultyByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc, getByMainId sidechain.GetByMainIdFunc, getByMainHeight sidechain.GetByMainHeightFunc, processBlock func(b *sidechain.PoolBlock) error) error { - if !index.QueryHasResults(indexDb.GetMainCoinbaseOutputs(mb.CoinbaseId)) { - //fill information - utils.Logf("", "inserting coinbase outputs for %s, template id %s, coinbase id %s", mb.Id, mb.SideTemplateId, mb.CoinbaseId) - var t *sidechain.PoolBlock - if t = getByTemplateId(mb.SideTemplateId); t == nil || t.MainId() != mb.Id { - t = nil - } - if t == nil { - t = getByMainId(mb.Id) - } - if t != nil { - if err := processBlock(t); err != nil { - return fmt.Errorf("could not process block: %s", err) - } - if mb.CoinbaseId != t.CoinbaseId() { - return fmt.Errorf("not matching coinbase id: %s vs %s", mb.CoinbaseId, t.CoinbaseId()) - } - indexes, err := client.GetOutputIndexes(mb.CoinbaseId) - if err != nil { - return fmt.Errorf("error getting output indexes: %w", err) - } - if len(indexes) != len(t.Main.Coinbase.Outputs) { - return fmt.Errorf("not matching indexes vs coinbase output len: %d vs %d", len(indexes), len(t.Main.Coinbase.Outputs)) - } - preAllocatedShares := sidechain.PreAllocateShares(indexDb.Consensus().ChainWindowSize * 2) - shares, _ := sidechain.GetShares(t, indexDb.Consensus(), difficultyByHeight, getByTemplateId, preAllocatedShares) - if len(shares) != len(t.Main.Coinbase.Outputs) { - return fmt.Errorf("not matching shares vs coinbase output len: %d vs %d", len(shares), len(t.Main.Coinbase.Outputs)) - } - outputs := make(index.MainCoinbaseOutputs, 0, len(indexes)) - for _, o := range t.Main.Coinbase.Outputs { - output := index.MainCoinbaseOutput{ - Id: mb.CoinbaseId, - Index: uint32(o.Index), - GlobalOutputIndex: indexes[o.Index], - Miner: indexDb.GetOrCreateMinerPackedAddress(shares[o.Index].Address).Id(), - Value: o.Reward, - } - outputs = append(outputs, output) - } - if err := indexDb.InsertOrUpdateMainCoinbaseOutputs(outputs); err != nil { - return err - } - } else { - return errors.New("nil template") - } - } - return nil -} diff --git a/cmd/utils/go.mod b/cmd/utils/go.mod deleted file mode 100644 index 8729123..0000000 --- a/cmd/utils/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -require ( - git.gammaspectra.live/P2Pool/go-monero v0.0.0-20230410011208-910450c4a523 - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - github.com/floatdrop/lru v1.3.0 - github.com/goccy/go-json v0.10.2 -) - -require ( - git.gammaspectra.live/P2Pool/edwards25519 v0.0.0-20230701100949-027561bd2a33 // 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/holiman/uint256 v1.2.4 // indirect - github.com/jxskiss/base62 v1.1.0 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/utils/go.sum b/cmd/utils/go.sum deleted file mode 100644 index 66d7043..0000000 --- a/cmd/utils/go.sum +++ /dev/null @@ -1,46 +0,0 @@ -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/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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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= -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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -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= diff --git a/cmd/utils/position_chart.go b/cmd/utils/position_chart.go deleted file mode 100644 index b7287c9..0000000 --- a/cmd/utils/position_chart.go +++ /dev/null @@ -1,140 +0,0 @@ -package utils - -type PositionChart struct { - totalItems uint64 - bucket []uint64 - perBucket int - idle byte -} - -func (p *PositionChart) Add(index int, value uint64) { - if index < 0 || index >= int(p.totalItems) { - return - } - if len(p.bucket) == 1 { - p.bucket[0] += value - return - } - - p.bucket[p.indexOf(index)] += value -} - -func (p *PositionChart) indexOf(index int) int { - if len(p.bucket) == 1 { - return 0 - } - i := (index*len(p.bucket) - 1) / int(p.totalItems-1) - - return i -} - -func (p *PositionChart) Total() (result uint64) { - for _, e := range p.bucket { - result += e - } - return -} - -func (p *PositionChart) Size() uint64 { - return uint64(len(p.bucket)) -} - -func (p *PositionChart) Resolution() uint64 { - return p.totalItems / uint64(len(p.bucket)) -} - -func (p *PositionChart) SetIdle(idleChar byte) { - p.idle = idleChar -} - -func (p *PositionChart) String() string { - position := make([]byte, 2*2+len(p.bucket)) - position[0], position[1] = '[', '<' - position[len(position)-2], position[len(position)-1] = '<', ']' - - //reverse - size := len(p.bucket) - 1 - for i := len(p.bucket) - 1; i >= 0; i-- { - e := p.bucket[i] - if e > 0 { - if e > 9 { - position[2+size-i] = '+' - } else { - position[2+size-i] = 0x30 + byte(e) - } - } else { - position[2+size-i] = p.idle - } - } - - return string(position) -} - -func (p *PositionChart) StringWithoutDelimiters() string { - position := make([]byte, len(p.bucket)) - - //reverse - size := len(p.bucket) - 1 - for i := len(p.bucket) - 1; i >= 0; i-- { - e := p.bucket[i] - if e > 0 { - if e > 9 { - position[size-i] = '+' - } else { - position[size-i] = 0x30 + byte(e) - } - } else { - position[size-i] = p.idle - } - } - - return string(position) -} - -func (p *PositionChart) StringWithSeparator(index int) string { - if index < 0 || index >= int(p.totalItems) { - return p.String() - } - separatorIndex := p.indexOf(index) - position := make([]byte, 1+2*2+len(p.bucket)) - position[0], position[1] = '[', '<' - position[2+separatorIndex] = '|' - position[len(position)-2], position[len(position)-1] = '<', ']' - - //reverse - size := len(p.bucket) - 1 - for i := len(p.bucket) - 1; i >= 0; i-- { - e := p.bucket[i] - j := size - i - if j >= separatorIndex { - j++ - } - if e > 0 { - if e > 9 { - position[2+j] = '+' - } else { - position[2+j] = 0x30 + byte(e) - } - } else { - position[2+j] = p.idle - } - } - - return string(position) -} - -func NewPositionChart(size uint64, totalItems uint64) *PositionChart { - if size < 1 { - size = 1 - } - perBucket := int(totalItems / size) - if totalItems%size > 0 { - perBucket += 1 - } - return &PositionChart{ - totalItems: totalItems, - bucket: make([]uint64, size), - perBucket: perBucket, - idle: '.', - } -} diff --git a/cmd/utils/position_chart_test.go b/cmd/utils/position_chart_test.go deleted file mode 100644 index 3a58626..0000000 --- a/cmd/utils/position_chart_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package utils - -import ( - "testing" -) - -func TestChart(t *testing.T) { - p := NewPositionChart(32, 4096) - for i := 0; i < 4096; i++ { - p.Add(i, 1) - } - t.Log(p.String()) - -} - -func TestChartIncrement(t *testing.T) { - p := NewPositionChart(32, 32) - for i := 0; i < 32; i++ { - p.Add(i, uint64(i)) - } - t.Log(p.String()) - -} diff --git a/cmd/utils/signed_action.go b/cmd/utils/signed_action.go deleted file mode 100644 index e2a9e0f..0000000 --- a/cmd/utils/signed_action.go +++ /dev/null @@ -1,119 +0,0 @@ -package utils - -import ( - "bytes" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/goccy/go-json" - "slices" -) - -type SignedActionEntry struct { - Key string `json:"key"` - Value string `json:"value"` -} - -type SignedAction struct { - Action string `json:"action"` - Data []SignedActionEntry `json:"data"` - Realm string `json:"realm"` -} - -func (a *SignedAction) Get(key string) (string, bool) { - if i := slices.IndexFunc(a.Data, func(entry SignedActionEntry) bool { - return entry.Key == key - }); i != -1 { - return a.Data[i].Value, true - } - - return "", false -} - -// String Creates a consistent string for signing -func (a *SignedAction) String() string { - if a.Data == nil { - a.Data = make([]SignedActionEntry, 0) - } - d, err := utils.MarshalJSON(a) - if err != nil { - panic(err) - } - buf := bytes.NewBuffer(make([]byte, 0, len(d))) - if err = json.Compact(buf, d); err != nil { - panic(err) - } - return buf.String() -} - -func (a *SignedAction) Verify(realm string, addr address.Interface, signature string) address.SignatureVerifyResult { - if a.Realm != realm { - // Realm does not match - return address.ResultFail - } - message := a.String() - return address.VerifyMessage(addr, []byte(message), signature) -} - -func (a *SignedAction) VerifyFallbackToZero(realm string, addr address.Interface, signature string) address.SignatureVerifyResult { - if a.Realm != realm { - // Realm does not match - return address.ResultFail - } - message := a.String() - return address.VerifyMessageFallbackToZero(addr, []byte(message), signature) -} - -func SignedActionSetMinerAlias(realm, alias string) *SignedAction { - return &SignedAction{ - Action: "set_miner_alias", - Data: []SignedActionEntry{ - { - Key: "alias", - Value: alias, - }, - }, - Realm: realm, - } -} - -func SignedActionUnsetMinerAlias(realm string) *SignedAction { - return &SignedAction{ - Action: "unset_miner_alias", - Data: make([]SignedActionEntry, 0), - Realm: realm, - } -} - -func SignedActionAddWebHook(realm, webhookType, webhookUrl string, other ...SignedActionEntry) *SignedAction { - return &SignedAction{ - Action: "add_webhook", - Data: append([]SignedActionEntry{ - { - Key: "type", - Value: webhookType, - }, - { - Key: "url", - Value: webhookUrl, - }, - }, other...), - Realm: realm, - } -} - -func SignedActionRemoveWebHook(realm, webhookType, webhookUrlHash string, other ...SignedActionEntry) *SignedAction { - return &SignedAction{ - Action: "remove_webhook", - Data: append([]SignedActionEntry{ - { - Key: "type", - Value: webhookType, - }, - { - Key: "url_hash", - Value: webhookUrlHash, - }, - }, other...), - Realm: realm, - } -} diff --git a/cmd/utils/transaction_lookup.go b/cmd/utils/transaction_lookup.go deleted file mode 100644 index 5d38e36..0000000 --- a/cmd/utils/transaction_lookup.go +++ /dev/null @@ -1,291 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "net/url" - "os" - "strings" - "time" -) - -func LookupTransactions(requestOther func(ctx context.Context, indices []uint64) []*index.MatchedOutput, indexDb *index.Index, ctx context.Context, inputThreshold int, ids ...types.Hash) (results []index.TransactionInputQueryResults) { - txs, err := client.GetDefaultClient().GetTransactionInputs(ctx, ids...) - if err != nil || len(txs) != len(ids) { - return nil - } - - decoys := make([]uint64, 0, len(txs)*16*16) - for _, tx := range txs { - if len(tx.Inputs) < inputThreshold { - continue - } - for _, i := range tx.Inputs { - decoys = append(decoys, i.KeyOffsets...) - } - } - - var otherResult []*index.MatchedOutput - if requestOther != nil { - otherResult = requestOther(ctx, decoys) - if len(otherResult) != len(decoys) { - otherResult = nil - } - } - - var otherIndex int - for _, tx := range txs { - if len(tx.Inputs) < inputThreshold { - queryResult := make(index.TransactionInputQueryResults, len(tx.Inputs)) - for i := range queryResult { - queryResult[i].Input = tx.Inputs[i] - queryResult[i].MatchedOutputs = make([]*index.MatchedOutput, len(tx.Inputs[i].KeyOffsets)) - } - results = append(results, queryResult) - continue - } - queryResult := indexDb.QueryTransactionInputs(tx.Inputs) - - if otherResult != nil { - for i, input := range tx.Inputs { - for j := range input.KeyOffsets { - output := otherResult[otherIndex] - otherIndex++ - if output == nil { - continue - } - if queryResult[i].MatchedOutputs[j] == nil { //todo: multiple matches?? - queryResult[i].MatchedOutputs[j] = output - } - } - } - } - - results = append(results, queryResult) - } - - return results -} - -var otherLookupHostFunc func(ctx context.Context, indices []uint64) []*index.MatchedOutput - -func init() { - if os.Getenv("TRANSACTION_LOOKUP_OTHER") != "" { - otherLookupHostFunc = func(ctx context.Context, indices []uint64) (result []*index.MatchedOutput) { - data, _ := utils.MarshalJSON(indices) - - result = make([]*index.MatchedOutput, len(indices)) - - for _, host := range strings.Split(os.Getenv("TRANSACTION_LOOKUP_OTHER"), ",") { - host = strings.TrimSpace(host) - if host == "" { - continue - } - uri, _ := url.Parse(host + "/api/global_indices_lookup") - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "POST", - URL: uri, - Body: io.NopCloser(bytes.NewReader(data)), - }); err == nil { - func() { - defer response.Body.Close() - if response.StatusCode == http.StatusOK { - if data, err := io.ReadAll(response.Body); err == nil { - r := make([]*index.MatchedOutput, 0, len(indices)) - if utils.UnmarshalJSON(data, &r) == nil && len(r) == len(indices) { - for i := range r { - if result[i] == nil { - result[i] = r[i] - } - } - } - } - } - }() - } - } - return result - } - } -} - -func ProcessFullBlock(b *index.MainBlock, indexDb *index.Index) error { - - var sideTemplateId types.Hash - var extraNonce []byte - mainBlock, err := GetMainBlock(b.Id, client.GetDefaultClient()) - if err != nil { - return fmt.Errorf("could not get main block for %s at %d: %w", b.Id, b.Height, err) - } - - utils.Logf("", "Block %s at %d (%s), processing %d transactions", b.Id, b.Height, time.Unix(int64(b.Timestamp), 0).UTC().Format("02-01-2006 15:04:05 MST"), len(mainBlock.Transactions)) - - // try to fetch extra nonce if existing - { - if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideExtraNonce)); t != nil { - if len(t.Data) >= sidechain.SideExtraNonceSize || len(t.Data) < sidechain.SideExtraNonceMaxSize { - extraNonce = t.Data - } - } - } - - // get a merge tag if existing - { - if t := mainBlock.Coinbase.Extra.GetTag(uint8(sidechain.SideTemplateId)); t != nil { - if len(t.Data) == types.HashSize { - sideTemplateId = types.HashFromBytes(t.Data) - } - } - } - - if sideTemplateId != types.ZeroHash && len(extraNonce) != 0 { - b.SetMetadata("merge_mining_tag", sideTemplateId) - b.SetMetadata("extra_nonce", binary.LittleEndian.Uint32(extraNonce)) - utils.Logf("", "p2pool tags found template id %s, extra nonce %d", sideTemplateId, binary.LittleEndian.Uint32(extraNonce)) - } - - const txInputThreshold = 4 - const txInputThresholdForRatio = 8 - - if len(mainBlock.Transactions) > 0 { - - results := LookupTransactions(otherLookupHostFunc, indexDb, context.Background(), txInputThreshold, mainBlock.Transactions...) - - for i, inputs := range results { - txId := mainBlock.Transactions[i] - - if len(inputs) < txInputThreshold { - //skip due to not enough matches later on - continue - } - - matches := inputs.Match() - - var topMiner *index.TransactionInputQueryResultsMatch - for j, m := range matches { - if m.Address == nil { - continue - } else if topMiner == nil { - topMiner = &matches[j] - } else { - if topMiner.Count <= 2 && topMiner.Count == m.Count { - //if count is not greater - topMiner = nil - } - break - } - } - - if topMiner != nil { - var noMinerCount, minerCount, otherMinerCount uint64 - for _, i := range inputs { - var isNoMiner, isMiner, isOtherMiner bool - for _, o := range i.MatchedOutputs { - if o == nil { - isNoMiner = true - } else if topMiner.Address.Compare(o.Address) == 0 { - isMiner = true - } else { - isOtherMiner = true - } - } - - if isMiner { - minerCount++ - } else if isOtherMiner { - otherMinerCount++ - } else if isNoMiner { - noMinerCount++ - } - } - - minerRatio := float64(minerCount) / float64(len(inputs)) - noMinerRatio := float64(noMinerCount) / float64(len(inputs)) - otherMinerRatio := float64(otherMinerCount) / float64(len(inputs)) - var likelyMiner bool - if (len(inputs) >= txInputThresholdForRatio && minerRatio >= noMinerRatio && minerRatio > otherMinerRatio) || (len(inputs) >= txInputThresholdForRatio && minerRatio > 0.35 && minerRatio > otherMinerRatio) || (len(inputs) >= txInputThreshold && minerRatio > 0.75) { - likelyMiner = true - } - - if likelyMiner { - utils.Logf("", "transaction %s is LIKELY for %s: miner ratio %.02f (%d/%d), none %.02f (%d/%d), other %.02f (%d/%d); coinbase %d, sweep %d", txId, topMiner.Address.ToBase58(), minerRatio, minerCount, len(inputs), noMinerRatio, noMinerCount, len(inputs), otherMinerRatio, otherMinerCount, len(inputs), topMiner.CoinbaseCount, topMiner.SweepCount) - - minimalInputs := make(index.MinimalTransactionInputQueryResults, len(inputs)) - - decoyCount := len(inputs[0].Input.KeyOffsets) - spendingOutputIndices := make([]uint64, 0, len(inputs)*decoyCount) - for j, input := range inputs { - spendingOutputIndices = append(spendingOutputIndices, input.Input.KeyOffsets...) - minimalInputs[j].Input = input.Input - minimalInputs[j].MatchedOutputs = make([]*index.MinimalMatchedOutput, len(input.MatchedOutputs)) - for oi, o := range input.MatchedOutputs { - if o == nil { - continue - } else if o.Coinbase != nil { - minimalInputs[j].MatchedOutputs[oi] = &index.MinimalMatchedOutput{ - Coinbase: o.Coinbase.Id, - GlobalOutputIndex: o.GlobalOutputIndex, - Address: o.Address, - } - } else if o.Sweep != nil { - minimalInputs[j].MatchedOutputs[oi] = &index.MinimalMatchedOutput{ - Sweep: o.Sweep.Id, - GlobalOutputIndex: o.GlobalOutputIndex, - Address: o.Address, - } - } - } - } - - outputIndexes, err := client.GetDefaultClient().GetOutputIndexes(txId) - if err != nil { - return err - } - - tx := &index.MainLikelySweepTransaction{ - Id: txId, - Timestamp: mainBlock.Timestamp, - Result: minimalInputs, - Match: matches, - Value: topMiner.CoinbaseAmount, - SpendingOutputIndices: spendingOutputIndices, - GlobalOutputIndices: outputIndexes, - InputCount: len(inputs), - InputDecoyCount: decoyCount, - MinerCount: int(minerCount), - OtherMinersCount: int(otherMinerCount), - NoMinerCount: int(noMinerCount), - MinerRatio: float32(minerRatio), - OtherMinersRatio: float32(otherMinerRatio), - NoMinerRatio: float32(noMinerRatio), - Address: topMiner.Address, - } - if err := indexDb.InsertOrUpdateMainLikelySweepTransaction(tx); err != nil { - return err - } - } else { - utils.Logf("", "transaction %s is NOT likely for %s: miner ratio %.02f (%d/%d), none %.02f (%d/%d), other %.02f (%d/%d); coinbase %d, sweep %d", txId, topMiner.Address.ToBase58(), minerRatio, topMiner.Count, len(inputs), noMinerRatio, noMinerCount, len(inputs), otherMinerRatio, otherMinerCount, len(inputs), topMiner.CoinbaseCount, topMiner.SweepCount) - } - } else { - //utils.Logf("", "transaction %s does not have enough matches, %d outputs", txId, len(inputs)) - } - } - } - - b.SetMetadata("processed", true) - - if err = indexDb.InsertOrUpdateMainBlock(b); err != nil { - return err - } - - return nil -} diff --git a/cmd/utils/urls.go b/cmd/utils/urls.go deleted file mode 100644 index 13fe999..0000000 --- a/cmd/utils/urls.go +++ /dev/null @@ -1,88 +0,0 @@ -package utils - -type urlEntry struct { - Host string - Onion string -} - -type siteKey int - -const ( - SiteKeyP2PoolIo = siteKey(iota) - SiteKeyLocalMonero - SiteKeyExploreMonero - SiteKeyMoneroCom - SiteKeyP2PoolObserver - SiteKeyP2PoolObserverMini - SiteKeyP2PoolObserverOld - SiteKeyP2PoolObserverOldMini - SiteKeyGitGammaspectraLive - SiteKeyXmrChainNet - SiteKeySethForPrivacy - SiteKeyXmrVsBeast -) - -var existingUrl = map[siteKey]urlEntry{ - SiteKeyP2PoolIo: { - Host: "https://p2pool.io", - Onion: "http://yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion", - }, - SiteKeyLocalMonero: { - Host: "https://localmonero.co", - Onion: "http://nehdddktmhvqklsnkjqcbpmb63htee2iznpcbs5tgzctipxykpj6yrid.onion", - }, - SiteKeyExploreMonero: { - Host: "https://www.exploremonero.com", - }, - SiteKeyMoneroCom: { - Host: "https://monero.com", - }, - SiteKeyP2PoolObserver: { - Host: "https://p2pool.observer", - Onion: "http://p2pool2giz2r5cpqicajwoazjcxkfujxswtk3jolfk2ubilhrkqam2id.onion", - }, - SiteKeyP2PoolObserverMini: { - Host: "https://mini.p2pool.observer", - Onion: "http://p2pmin25k4ei5bp3l6bpyoap6ogevrc35c3hcfue7zfetjpbhhshxdqd.onion", - }, - SiteKeyP2PoolObserverOld: { - Host: "https://old.p2pool.observer", - Onion: "http://temp2p7m2ddclcsqx2mrbqrmo7ccixpiu5s2cz2c6erxi2lppptdvxqd.onion", - }, - SiteKeyP2PoolObserverOldMini: { - Host: "https://old-mini.p2pool.observer", - Onion: "http://temp2pbud6av2jx3lh3yovrj4mjjy2k4p5rxydviosp356ndzs4nd6yd.onion", - }, - SiteKeyGitGammaspectraLive: { - Host: "https://git.gammaspectra.live", - Onion: "http://gitshn5x75sgs53q3pxwjva2z65ns5vadx3h7u3hrdssbxsova66cxid.onion", - }, - SiteKeyXmrChainNet: { - Host: "https://xmrchain.net", - }, - SiteKeySethForPrivacy: { - Host: "https://sethforprivacy.com", - Onion: "http://sfprivg7qec6tdle7u6hdepzjibin6fn3ivm6qlwytr235rh5vc6bfqd.onion", - }, - SiteKeyXmrVsBeast: { - Host: "https://xmrvsbeast.com", - }, -} - -func GetSiteUrl(k siteKey, tryOnion bool) string { - e := existingUrl[k] - if tryOnion && e.Onion != "" { - return e.Onion - } - return e.Host -} - -func GetSiteUrlByHost(host string, tryOnion bool) string { - findKey := "https://" + host - for k, e := range existingUrl { - if e.Host == findKey { - return GetSiteUrl(k, tryOnion) - } - } - return "" -} diff --git a/cmd/utils/webhook.go b/cmd/utils/webhook.go deleted file mode 100644 index cf7f7d6..0000000 --- a/cmd/utils/webhook.go +++ /dev/null @@ -1,401 +0,0 @@ -package utils - -import ( - "bytes" - "errors" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "net/url" - "os" - "strconv" - "time" -) - -type JSONWebHookDiscord struct { - Username string `json:"username,omitempty"` - AvatarUrl string `json:"avatar_url,omitempty"` - Content string `json:"content,omitempty"` - Embeds []JSONWebHookDiscordEmbed `json:"embeds,omitempty"` - Components []JSONWebHookDiscordComponentRow `json:"components,omitempty"` - TTS bool `json:"tts,omitempty"` - AllowedMentions *struct { - Parse []string `json:"parse,omitempty"` - Users []string `json:"users,omitempty"` - Roles []string `json:"roles,omitempty"` - } `json:"allowed_mentions,omitempty"` -} - -func NewJSONWebHookDiscordComponent(b ...JSONWebHookDiscordComponentButton) JSONWebHookDiscordComponentRow { - return JSONWebHookDiscordComponentRow{ - Type: 1, - Components: b, - } -} - -func NewJSONWebHookDiscordComponentButton(label, url string) JSONWebHookDiscordComponentButton { - return JSONWebHookDiscordComponentButton{ - Type: 2, - Style: 5, - Label: label, - Url: url, - } -} - -type JSONWebHookDiscordComponentRow struct { - Type int `json:"type"` - Components []JSONWebHookDiscordComponentButton `json:"components"` -} - -type JSONWebHookDiscordComponentButton struct { - Type int `json:"type"` - Style int `json:"style"` - Label string `json:"label,omitempty"` - Url string `json:"url,omitempty"` -} - -type JSONWebHookDiscordEmbed struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Url string `json:"url,omitempty"` - // Timestamp Time in time.RFC3339 YYYY-MM-DDTHH:MM:SS.MSSZ format - Timestamp string `json:"timestamp,omitempty"` - Color *uint64 `json:"color,omitempty"` - - Footer *struct { - Text string `json:"text,omitempty"` - } `json:"footer,omitempty"` - - Image *struct { - Url string `json:"url,omitempty"` - } `json:"image,omitempty"` - Thumbnail *struct { - Url string `json:"url,omitempty"` - } `json:"thumbnail,omitempty"` - - Provider *struct { - Name string `json:"name,omitempty"` - IconUrl string `json:"icon_url,omitempty"` - } `json:"provider,omitempty"` - - Author *struct { - Name string `json:"name,omitempty"` - Url string `json:"url,omitempty"` - IconUrl string `json:"icon_url,omitempty"` - } `json:"author,omitempty"` - - Fields []JSONWebHookDiscordEmbedField `json:"fields,omitempty"` -} - -type JSONWebHookDiscordEmbedField struct { - Name string `json:"name,omitempty"` - Value string `json:"value,omitempty"` - Inline bool `json:"inline,omitempty"` -} - -var ( - DiscordColorGreen uint64 = 5763719 - DiscordColorDarkGreen uint64 = 2067276 - DiscordColorOrange uint64 = 15105570 -) - -var WebHookRateLimit = time.NewTicker(time.Second / 20) -var WebHookUserAgent string - -var WebHookHost string - -var WebHookVersion = fmt.Sprintf("%d.%d", types.CurrentSoftwareVersion.Major(), types.CurrentSoftwareVersion.Minor()) - -func init() { - WebHookHost = os.Getenv("NET_SERVICE_ADDRESS") - WebHookUserAgent = fmt.Sprintf("Mozilla/5.0 (compatible;GoObserver %s; +https://%s/api#webhooks)", types.CurrentSoftwareVersion.String(), WebHookHost) -} - -var WebHookClient = http.Client{ - Timeout: 10 * time.Second, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, -} - -func SetWebHookProxy(hostPort string) { - WebHookClient.Transport = &http.Transport{ - Proxy: http.ProxyURL(&url.URL{ - Scheme: "socks5", - Host: hostPort, - }), - } -} - -func SendSideBlock(w *index.MinerWebHook, ts int64, source *address.Address, block *index.SideBlock) error { - if !CanSendToHook(w, "side_blocks") { - return nil - } - - blockValuation := func() string { - if block.IsOrphan() { - return "0%" - } else if block.IsUncle() { - return strconv.FormatUint(100-w.Consensus.UnclePenalty, 10) + "% (uncle)" - } else if block.IsUncle() { - return "100% + " + strconv.FormatUint(w.Consensus.UnclePenalty, 10) + "% of " + strconv.FormatUint(uint64(len(block.Uncles)), 10) + "% uncle(s)" - } else { - return "100%" - } - } - - blockWeight, _ := block.Weight(block.EffectiveHeight, w.Consensus.ChainWindowSize, w.Consensus.UnclePenalty) - - switch w.Type { - case index.WebHookDiscord: - - var color = &DiscordColorGreen - fields := []JSONWebHookDiscordEmbedField{ - { - Name: "Pool Height", - Value: strconv.FormatUint(block.SideHeight, 10), - Inline: true, - }, - } - if block.IsUncle() { - color = &DiscordColorDarkGreen - fields = append(fields, JSONWebHookDiscordEmbedField{ - Name: "Parent Height", - Value: "[" + strconv.FormatUint(block.EffectiveHeight, 10) + "](" + "https://" + WebHookHost + "/share/" + block.UncleOf.String() + ")", - Inline: true, - }) - } else { - fields = append(fields, JSONWebHookDiscordEmbedField{ - Name: "Monero Height", - Value: strconv.FormatUint(block.MainHeight, 10), - Inline: true, - }) - } - fields = append(fields, - JSONWebHookDiscordEmbedField{ - Name: "Found by", - Value: "[`" + string(utils.ShortenSlice(block.MinerAddress.ToBase58(), 24)) + "`](" + "https://" + WebHookHost + "/miner/" + string(block.MinerAddress.ToBase58()) + ")", - }, - JSONWebHookDiscordEmbedField{ - Name: "Template Id", - Value: "[`" + block.TemplateId.String() + "`](" + "https://" + WebHookHost + "/share/" + block.TemplateId.String() + ")", - }, - JSONWebHookDiscordEmbedField{ - Name: "Valuation", - Value: blockValuation(), - Inline: true, - }, - JSONWebHookDiscordEmbedField{ - Name: "Weight", - Value: utils.SiUnits(float64(blockWeight), 4), - Inline: true, - }, - ) - - return SendJsonPost(w, ts, source, &JSONWebHookDiscord{ - Embeds: []JSONWebHookDiscordEmbed{ - { - Color: color, - Title: "**" + func() string { - if block.IsUncle() { - return "UNCLE " - } else { - return "" - } - }() + "SHARE FOUND** on P2Pool " + func() string { - if w.Consensus.IsDefault() { - return "Main" - } else if w.Consensus.IsMini() { - return "Mini" - } else { - return "Unknown" - } - }(), - Url: "https://" + WebHookHost + "/share/" + block.MainId.String(), - Description: "", - Fields: fields, - Footer: &struct { - Text string `json:"text,omitempty"` - }{ - Text: "Share mined using " + block.SoftwareId.String() + " " + block.SoftwareVersion.String(), - }, - Timestamp: time.Unix(int64(block.Timestamp), 0).UTC().Format(time.RFC3339), - }, - }, - }) - case index.WebHookCustom: - return SendJsonPost(w, ts, source, &JSONEvent{ - Type: JSONEventSideBlock, - SideBlock: block, - }) - default: - return errors.New("unsupported hook type") - } -} - -func SendFoundBlock(w *index.MinerWebHook, ts int64, source *address.Address, block *index.FoundBlock, outputs index.MainCoinbaseOutputs) error { - if !CanSendToHook(w, "found_blocks") { - return nil - } - - switch w.Type { - case index.WebHookCustom: - return SendJsonPost(w, ts, source, &JSONEvent{ - Type: JSONEventFoundBlock, - FoundBlock: block, - MainCoinbaseOutputs: outputs, - }) - default: - return errors.New("unsupported hook type") - } -} - -func CanSendToHook(w *index.MinerWebHook, key string) bool { - if v, ok := w.Settings["send_"+key]; ok && v == "false" { - return false - } - return true -} - -func SendPayout(w *index.MinerWebHook, ts int64, source *address.Address, payout *index.Payout) error { - if !CanSendToHook(w, "payouts") { - return nil - } - - switch w.Type { - case index.WebHookDiscord: - return SendJsonPost(w, ts, source, &JSONWebHookDiscord{ - Embeds: []JSONWebHookDiscordEmbed{ - { - Color: &DiscordColorOrange, - Title: "**RECEIVED PAYOUT** on P2Pool " + func() string { - if w.Consensus.IsDefault() { - return "Main" - } else if w.Consensus.IsMini() { - return "Mini" - } else { - return "Unknown" - } - }(), - Url: "https://" + WebHookHost + "/proof/" + payout.MainId.String() + "/" + strconv.FormatUint(payout.Index, 10), - Description: "", - Fields: []JSONWebHookDiscordEmbedField{ - { - Name: "Pool Height", - Value: strconv.FormatUint(payout.SideHeight, 10), - Inline: true, - }, - { - Name: "Monero Height", - Value: strconv.FormatUint(payout.MainHeight, 10), - Inline: true, - }, - { - Name: "Monero Id", - Value: "[`" + payout.MainId.String() + "`](" + GetSiteUrl(SiteKeyP2PoolIo, false) + "/explorer/block/" + payout.MainId.String() + ")", - }, - { - Name: "Template Id", - Value: "[`" + payout.TemplateId.String() + "`](" + "https://" + WebHookHost + "/share/" + payout.TemplateId.String() + ")", - }, - - { - Name: "Coinbase Id", - Value: "[`" + payout.CoinbaseId.String() + "`](" + GetSiteUrl(SiteKeyP2PoolIo, false) + "/explorer/tx/" + payout.CoinbaseId.String() + ")", - }, - { - Name: "Coinbase Private Key", - Value: "[`" + payout.PrivateKey.String() + "`](" + "https://" + WebHookHost + "/proof/" + payout.MainId.String() + "/" + strconv.FormatUint(payout.Index, 10) + ")", - }, - { - Name: "Payout address", - Value: "[`" + string(utils.ShortenSlice(source.ToBase58(), 24)) + "`](" + "https://" + WebHookHost + "/miner/" + string(source.ToBase58()) + ")", - }, - { - Name: "Reward", - Value: "**" + utils.XMRUnits(payout.Reward) + " XMR" + "**", - Inline: true, - }, - { - Name: "Global Output Index", - Value: strconv.FormatUint(payout.GlobalOutputIndex, 10), - Inline: true, - }, - }, - Timestamp: time.Unix(int64(payout.Timestamp), 0).UTC().Format(time.RFC3339), - }, - }, - }) - case index.WebHookCustom: - return SendJsonPost(w, ts, source, &JSONEvent{ - Type: JSONEventPayout, - Payout: payout, - }) - default: - return errors.New("unsupported hook type") - } -} - -func SendOrphanedBlock(w *index.MinerWebHook, ts int64, source *address.Address, block *index.SideBlock) error { - if !CanSendToHook(w, "orphaned_blocks") { - return nil - } - - switch w.Type { - case index.WebHookCustom: - return SendJsonPost(w, ts, source, &JSONEvent{ - Type: JSONEventOrphanedBlock, - SideBlock: block, - }) - default: - return errors.New("unsupported hook type") - } -} - -func SendJsonPost(w *index.MinerWebHook, ts int64, source *address.Address, data any) error { - uri, err := url.Parse(w.Url) - if err != nil { - return err - } - - body, err := utils.MarshalJSON(data) - if err != nil { - return err - } - - headers := make(http.Header) - headers.Set("Accept", "*/*") - headers.Set("User-Agent", WebHookUserAgent) - headers.Set("Content-Type", "application/json") - headers.Set("X-P2Pool-Observer-Timestamp", strconv.FormatInt(ts, 10)) - headers.Set("X-P2Pool-Observer-Version", WebHookVersion) - headers.Set("X-P2Pool-Observer-Host", WebHookHost) - headers.Set("X-P2Pool-Observer-Consensus-ID", w.Consensus.Id.String()) - headers.Set("X-P2Pool-Observer-Address", string(source.ToBase58())) - - // apply rate limit - <-WebHookRateLimit.C - - response, err := WebHookClient.Do(&http.Request{ - Method: "POST", - URL: uri, - Header: headers, - Body: io.NopCloser(bytes.NewBuffer(body)), - ContentLength: int64(len(body)), - }) - if err != nil { - return err - } - defer response.Body.Close() - defer io.ReadAll(response.Body) - - if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNoContent { - data, _ := io.ReadAll(response.Body) - return fmt.Errorf("error code %d: %s", response.StatusCode, string(data)) - } - return nil -} diff --git a/cmd/web/api.go b/cmd/web/api.go deleted file mode 100644 index 6846290..0000000 --- a/cmd/web/api.go +++ /dev/null @@ -1,177 +0,0 @@ -package main - -import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "io" - "net/http" - "net/url" - "os" - "time" -) - -func getTypeFromAPI[T any](method string, cacheTime ...int) *T { - cTime := 0 - if len(cacheTime) > 0 { - cTime = cacheTime[0] - } - - return cacheResult[*T](method, time.Second*time.Duration(cTime), func() *T { - uri, _ := url.Parse(os.Getenv("API_URL") + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - var result T - decoder := utils.NewJSONDecoder(response.Body) - if decoder.Decode(&result) != nil { - return nil - } else { - return &result - } - } else { - return nil - } - } - }) -} - -func getSliceFromAPI[T any](method string, cacheTime ...int) []T { - cTime := 0 - if len(cacheTime) > 0 { - cTime = cacheTime[0] - } - - return cacheResult[[]T](method, time.Second*time.Duration(cTime), func() []T { - uri, _ := url.Parse(os.Getenv("API_URL") + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - var result []T - decoder := utils.NewJSONDecoder(response.Body) - if decoder.Decode(&result) != nil { - return nil - } else { - return result - } - } else { - return nil - } - } - }) -} - -func getStreamFromAPI[T any](method string) <-chan T { - result := make(chan T, 1) - - go func() { - defer close(result) - uri, _ := url.Parse(os.Getenv("API_URL") + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - - if response.StatusCode == http.StatusOK { - - var err error - - // Read opening - var b [1]byte - for { - if _, err = response.Body.Read(b[:]); err != nil { - return - } - if b[0] == '[' { - break - } else if b[0] != ' ' && b[0] != 0xa { - return - } - } - - decoder := utils.NewJSONDecoder(response.Body) - for decoder.More() { - var item T - if err := decoder.Decode(&item); err != nil { - return - } else { - result <- item - } - } - } - } - }() - - return result -} - -func getSideBlocksStreamFromAPI(method string) <-chan *index.SideBlock { - return getStreamFromAPI[*index.SideBlock](method) -} - -func getSideBlocksFromAPI(method string, cacheTime ...int) []*index.SideBlock { - return getSliceFromAPI[*index.SideBlock](method, cacheTime...) -} - -func getFromAPIRaw(method string, cacheTime ...int) []byte { - cTime := 0 - if len(cacheTime) > 0 { - cTime = cacheTime[0] - } - - return cacheResult[[]byte](method, time.Second*time.Duration(cTime), func() []byte { - uri, _ := url.Parse(os.Getenv("API_URL") + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - if response.StatusCode == http.StatusOK { - if data, err := io.ReadAll(response.Body); err != nil { - return nil - } else { - return data - } - } else { - return nil - } - } - }) -} - -func getFromAPI(method string) (statusCode int, data []byte) { - uri, _ := url.Parse(os.Getenv("API_URL") + method) - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "GET", - URL: uri, - }); err != nil { - return 0, nil - } else { - defer response.Body.Close() - defer io.ReadAll(response.Body) - - if data, err = io.ReadAll(response.Body); err != nil { - return response.StatusCode, nil - } else { - return response.StatusCode, data - } - } -} diff --git a/cmd/web/cache.go b/cmd/web/cache.go deleted file mode 100644 index 9a1c0a1..0000000 --- a/cmd/web/cache.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "sync" - "time" -) - -var cache = make(map[string]any) -var cacheLock sync.RWMutex - -type cachedResult[T any] struct { - t time.Time - result T -} - -func cacheResult[T any](k string, cacheTime time.Duration, result func() T) T { - var zeroVal T - if r, ok := func() (T, bool) { - if cacheTime > 0 { - cacheLock.RLock() - defer cacheLock.RUnlock() - - if r := cache[k]; r != nil { - if r2, ok := r.(*cachedResult[T]); ok && r2 != nil && r2.t.Add(cacheTime).After(time.Now()) { - return r2.result, true - } - } - } - - return zeroVal, false - }(); ok { - return r - } - - r := result() - - if cacheTime > 0 { - cacheLock.Lock() - defer cacheLock.Unlock() - cache[k] = &cachedResult[T]{ - t: time.Now(), - result: r, - } - } - - return r -} diff --git a/cmd/web/default.pgo b/cmd/web/default.pgo deleted file mode 100644 index 3fb5857..0000000 Binary files a/cmd/web/default.pgo and /dev/null differ diff --git a/cmd/web/go.mod b/cmd/web/go.mod deleted file mode 100644 index 6cd266d..0000000 --- a/cmd/web/go.mod +++ /dev/null @@ -1,44 +0,0 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer/cmd/web - -go 1.22 - -replace git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 => ../../ - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 => ../index - -replace git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 => ../utils - -require ( - git.gammaspectra.live/P2Pool/p2pool-observer v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index v0.0.0 - git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils v0.0.0 - github.com/goccy/go-json v0.10.2 - github.com/gorilla/mux v1.8.1 - github.com/mazznoer/colorgrad v0.9.1 - github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc - github.com/valyala/quicktemplate v1.7.0 -) - -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/holiman/uint256 v1.2.4 // indirect - github.com/jxskiss/base62 v1.1.0 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mazznoer/csscolorparser v0.1.3 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.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 diff --git a/cmd/web/go.sum b/cmd/web/go.sum deleted file mode 100644 index be2f209..0000000 --- a/cmd/web/go.sum +++ /dev/null @@ -1,76 +0,0 @@ -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/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/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -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/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mazznoer/colorgrad v0.9.1 h1:MB80JYVndKWSMEM1beNqnuOowWGhoQc3DXWXkFp6JlM= -github.com/mazznoer/colorgrad v0.9.1/go.mod h1:WX2R9wt9B47+txJZVVpM9LY+LAGIdi4lTI5wIyreDH4= -github.com/mazznoer/csscolorparser v0.1.2/go.mod h1:Aj22+L/rYN/Y6bj3bYqO3N6g1dtdHtGfQ32xZ5PJQic= -github.com/mazznoer/csscolorparser v0.1.3 h1:vug4zh6loQxAUxfU1DZEu70gTPufDPspamZlHAkKcxE= -github.com/mazznoer/csscolorparser v0.1.3/go.mod h1:Aj22+L/rYN/Y6bj3bYqO3N6g1dtdHtGfQ32xZ5PJQic= -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= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM= -github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -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/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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= diff --git a/cmd/web/views/.gitignore b/cmd/web/views/.gitignore deleted file mode 100644 index cec9707..0000000 --- a/cmd/web/views/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*qtpl.go.tmp -*qtpl.go \ No newline at end of file diff --git a/cmd/web/views/api.qtpl b/cmd/web/views/api.qtpl deleted file mode 100644 index 4da6e89..0000000 --- a/cmd/web/views/api.qtpl +++ /dev/null @@ -1,1208 +0,0 @@ -{% 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 -} -%} - -{% func (p *ApiPage) Style() %} -{%= p.BasePage.Style() %} -pre { - padding: 2px; - border: 1px dashed #BBB; -} -{% endfunc %} - -{% func (p *ApiPage) Title() %} -{%= p.BasePage.Title() %} - API Documentation -{% endfunc %} - -{% func (p *ApiPage) Content() %} - -
-
-

API Documentation

-

This site and API is in development. As such, returned data could be different, or methods be deprecated / replaced anytime. Though it will be avoided if possible without affecting performance :)

-

All the data showed on this site comes from the API, P2Pool or Monero. So if new features are added on site, it's possible to do so via the API.

-

At the moment there is no broad rate-limiting. If abuse is detected, temporary rate limits can be put in place. You can always run your own instance of this site / API / consensus, see source code.

-

Some historical data could be incomplete. Some block records do not exist.

-

In general, mandatory parameters are specified on the path, optional parameters on query.

-

Knowledge of P2Pool internals is strongly advised. Feel free to ask questions on IRC or Matrix.

-

Unless specified, all returned data is JSON with status code 200. A not found error will issue a status code 404 and JSON of {"error": "not_found"} or similar.

-
- -
-

Common types and fields

-

- Fields within JSON objects may be omitted if marked as omitempty on its definition within the source code. An empty/zero representation is assumed. -
- For example, a types.Hash marked as omitempty would represent the value 0000000000000000000000000000000000000000000000000000000000000000. -
- For pointers/structs, the zero or nil value is considered empty. In lists, an empty / length of zero is assumed when empty. -
- Refer to source code to check the struct definition when in doubt. -

- -

types.Hash

-

Represents a 256-bit hash value. This can refer to a block template id, main id, parents, transaction ids, proof of work hashes or any other value.

-

This value is encoded as 64 hexadecimal characters.

- -

crypto.PublicKeyBytes / crypto.PrivateKeyBytes

-

Represents an edwards25519 Point or Scalar in binary form. Encoded as a 256-bit value. This can refer to a public keys on a block, transaction keys or any other value.

-

This value is encoded as 64 hexadecimal characters.

- -

address.Address

-

Represents a Monero address. Includes valid checksums.

-

This value is encoded using special base58.

- -

types.Difficulty

-

Represents a 128-bit unsigned integer value. This can refer to weight, block difficulty or cumulative difficulty.

-

This value is encoded as a 64-bit unsigned integer when the highest 64-bits are not set. Otherwise, it is encoded as 32 hexadecimal characters.

-
- -
- -
-

/api/pool_info

-

Response contains general information on the current status of the P2Pool sidechain and Monero network, release versions, and other parameters.

-
-[Live result. Has been cut due to size, relevant portions shown]
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/pool_info
-{%= encodeJson(p.PoolInfoExample, true) %}
-        
-
- -
- -
-

/api/miner_info/<id|address>

-

Response contains general information on a specific miner, referred by its internal id or known Monero address.

-

If you do not need the precise count of shares please pass the ?shareEstimates parameter.

-
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/miner_info/47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw
-{
-    "id": 3,
-    "address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-    "shares": [
-        {
-            "shares": 1022,
-            "uncles": 0,
-            "last_height": 4371641
-        },
-        {
-            "shares": 559294,
-            "uncles": 17861,
-            "last_height": 4868281
-        },
-        {
-            "shares": 69,
-            "uncles": 0,
-            "last_height": 4624653
-        }
-    ],
-    "last_share_height": 4868281,
-    "last_share_timestamp": 1681256979
-}
-        
-
- -
- -
-

/api/side_blocks_in_window?window=[window_size]&from=[height][&noMainStatus][&noUncles][&noMiners]

-

Response contains SideChain Blocks on specified range window. Optional parameters are height, denoting the Tip where the window gets measured from, and window_size, max SIDECHAIN_PPLNS_WINDOW * 4 * 7

-

By default it will return current window blocks, from current tip.

-
-[result has been cut due to size, relevant portions shown]
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/side_blocks_in_window
-[
-    {
-        "main_id": "61c6463961c31584617f2ffd322fd32f91cef4a956b3ac431c128ab1ef8342c8",
-        "main_height": 2863488,
-        "template_id": "8782465759db40c11f64e78a26df4ec6221fb91202cc19a3beaef61acb8e1a50",
-        "side_height": 4882215,
-        "parent_template_id": "2ee8e55e2d0696d715049f5958c300c10b01d0a30b607c82afb8885a76f00d71",
-        "miner": 3,
-        "effective_height": 4882215,
-        "nonce": 3691022871,
-        "extra_nonce": 1665655721,
-        "timestamp": 1681402700,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 294,
-        "window_outputs": 30,
-        "transaction_count": 2,
-        "difficulty": 2107950958,
-        "cumulative_difficulty": 7610896210501088,
-        "pow_difficulty": 3167867138,
-        "pow_hash": "3e19a8e6c1db97bae77fb19b68b5bf6c927e3b212063ee219e26155b01000000",
-        "inclusion": 1,
-        "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-        "main_difficulty": 310626726029
-    },
-    {
-        "main_id": "30e8afa1c6961afae7686f4c9c2ee5ad77bd87bdcbf7da00958af53b3349264a",
-        "main_height": 2863488,
-        "template_id": "2ee8e55e2d0696d715049f5958c300c10b01d0a30b607c82afb8885a76f00d71",
-        "side_height": 4882214,
-        "parent_template_id": "e0b745e8e3004832d95ef7eb6aa5f43dd0f86a491b6edb8ccd8e8dfbf92f6768",
-        "miner": 2,
-        "effective_height": 4882214,
-        "nonce": 3791814950,
-        "extra_nonce": 610282123,
-        "timestamp": 1681402699,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 293,
-        "window_outputs": 30,
-        "transaction_count": 2,
-        "difficulty": 2108162718,
-        "cumulative_difficulty": 7610894102550130,
-        "pow_difficulty": 5140029450,
-        "pow_hash": "591082e76b158724c0a1a3ea8ffdec93eaa33a8d371eda2ad85ae9d500000000",
-        "inclusion": 1,
-        "miner_address": "435QwkC3fJ4YeX929gmaBMeuUwR1wdmRrQBNyoKfpJdHKDbqnukHJeA768yt8ngnS7UTHRJ2yJmnWP8gCz6s3kkiBX8C363",
-        "uncles": [
-            {
-                "template_id": "4005ede80787679e83d269f09f6435ac4952d9c2cf2fadf999620ed81a948d40",
-                "miner": 77,
-                "side_height": 4882213,
-                "difficulty": 2109635914
-            }
-        ],
-        "main_difficulty": 310626726029
-    },
-    {
-        "main_id": "6d1865f59d8cd38f5de6a498a2a5465040194f63122187b6e439743913aac202",
-        "main_height": 2863487,
-        "template_id": "4005ede80787679e83d269f09f6435ac4952d9c2cf2fadf999620ed81a948d40",
-        "side_height": 4882213,
-        "parent_template_id": "985d621e565da6da1f772dffef4b1154678cc724b6b18eff6edde5e68136a45d",
-        "miner": 77,
-        "uncle_of": "2ee8e55e2d0696d715049f5958c300c10b01d0a30b607c82afb8885a76f00d71",
-        "effective_height": 4882214,
-        "nonce": 3326443597,
-        "extra_nonce": 3803058762,
-        "timestamp": 1681402680,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 294,
-        "window_outputs": 31,
-        "transaction_count": 0,
-        "difficulty": 2109635914,
-        "cumulative_difficulty": "0000000000000000001b0a106401568a",
-        "pow_difficulty": 2153295646,
-        "pow_hash": 7610889884751498,
-        "inclusion": 1,
-        "miner_address": "457fCKqWYSacjDRLjqah9kTga8KCQRNwL4WP7EfBNQKghYF3RZunjGHCoPSd6F7Y6mVRpT6eCgkEtin3cKvSCf11AqcP33g",
-        "main_difficulty": 310568847048
-    },
-    {
-        "main_id": "f5582e27b94a34713b607d0f47821142c8b88f8a3335d5b2b1d679c909a9651f",
-        "main_height": 2863486,
-        "template_id": "e0b745e8e3004832d95ef7eb6aa5f43dd0f86a491b6edb8ccd8e8dfbf92f6768",
-        "side_height": 4882213,
-        "parent_template_id": "985d621e565da6da1f772dffef4b1154678cc724b6b18eff6edde5e68136a45d",
-        "miner": 18,
-        "effective_height": 4882213,
-        "nonce": 3506931857,
-        "extra_nonce": 1625389436,
-        "timestamp": 1681402674,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 294,
-        "window_outputs": 31,
-        "transaction_count": 115,
-        "difficulty": 2109635914,
-        "cumulative_difficulty": 7610889884751498,
-        "pow_difficulty": 3604028547,
-        "pow_hash": "1d01609ded322d6665fb96410042cc5cc9604be078b77967a818143101000000",
-        "inclusion": 1,
-        "miner_address": "4BBb6ZBPPPWivZnx2m3m7YUUGQv54F9HfjPWurf2Xy2UDQwwPyb956dGQ3p9whrxNJ568zc3Ygfr9McVT1UJyRKtLi1jcMb",
-        "main_difficulty": 310847993727
-    }
-]
-        
-
- -
- -
-

/api/side_blocks_in_window/<id|address>?window=[window_size]&from=[height][&noMainStatus][&noUncles][&noMiners]

-

Response contains SideChain Blocks on specified range window for a specific miner. Optional parameters are height, denoting the Tip where the window gets measured from, and window_size, max SIDECHAIN_PPLNS_WINDOW * 4 * 7

-

By default it will return current window blocks, from current tip.

-
-[result has been cut due to size, relevant portions shown]
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/side_blocks_in_window/47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw
-[
-        {
-        "main_id": "b41a9e78f11b9f7eaed30a4211f43ae7201f7c77031839f3e484b59c32088028",
-        "main_height": 2863492,
-        "template_id": "ce8de3ca5a9a066c0b4f5d5068783b17ace476f56236c65f5c949b9039c0ca3e",
-        "side_height": 4882250,
-        "parent_template_id": "a0cb288e467f5a4069603f4047c8607f1721a4b62fc8e2f794a5a942828fdf32",
-        "miner": 3,
-        "effective_height": 4882250,
-        "nonce": 2046825740,
-        "extra_nonce": 1477203696,
-        "timestamp": 1681403097,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 296,
-        "window_outputs": 28,
-        "transaction_count": 75,
-        "difficulty": 2099479471,
-        "cumulative_difficulty": 7610976044280876,
-        "pow_difficulty": 2770364525,
-        "pow_hash": "ea3d41d8874fb1de05aee76a0fb6df7267b07e34ee95cdf1f924e28c01000000",
-        "inclusion": 1,
-        "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-        "main_difficulty": 311336952550
-    },
-    {
-        "main_id": "fea4ee3769b350f2aad0571ff54eb555dbdf7f0cb811c19dea66bef286b041c4",
-        "main_height": 2863492,
-        "template_id": "74caf5d6ba885d05adbb4e4d611edabb55f0e05dcbb20cd3105d2adbcf4cdd1b",
-        "side_height": 4882248,
-        "parent_template_id": "3378e835d72a821b5157d8bebaf4f2193ebaf9cb86c93a106fc4fbbb1921c158",
-        "miner": 3,
-        "effective_height": 4882248,
-        "nonce": 3456175326,
-        "extra_nonce": 2803310737,
-        "timestamp": 1681403084,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 296,
-        "window_outputs": 28,
-        "transaction_count": 73,
-        "difficulty": 2103245329,
-        "cumulative_difficulty": 7610971844410631,
-        "pow_difficulty": 5249306449,
-        "pow_hash": "4ab091d18ac65c212feff8f1a971aee1da50c177620827326b5d75d100000000",
-        "inclusion": 1,
-        "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-        "main_difficulty": 311336952550
-    },
-    {
-        "main_id": "277a19bf024b4895553bbefc047eeb7733b95a97df6d8422759ec874d2d626c1",
-        "main_height": 2863492,
-        "template_id": "52ad4bb20d62b0e9d7762bab8d27de05563be6147cf3367d9bc79688f794217c",
-        "side_height": 4882246,
-        "parent_template_id": "40865b24eb4fbd1428ff1444d21b4337c8922b60b63a3ab6bdfef2586775390b",
-        "miner": 3,
-        "effective_height": 4882246,
-        "nonce": 754974898,
-        "extra_nonce": 2953378118,
-        "timestamp": 1681403082,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 296,
-        "window_outputs": 28,
-        "transaction_count": 72,
-        "difficulty": 2104858109,
-        "cumulative_difficulty": 7610967638144376,
-        "pow_difficulty": 4514527717,
-        "pow_hash": "fc127598502b38c4977f7faef3f7db6bab11bab42353baed9ab58cf300000000",
-        "inclusion": 1,
-        "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-        "uncles": [
-            {
-                "template_id": "ab07fe1e4a6a00ae799bedc637c1b642b4938d302ec16db5b513923937614abe",
-                "miner": 7,
-                "side_height": 4882244,
-                "difficulty": 2105207888
-            }
-        ],
-        "main_difficulty": 311336952550
-    },
-    {
-        "main_id": "ff68eedc72252289e066d2dd59c209dc1502500d2cf4501fb69a91b69e2415b8",
-        "main_height": 2863492,
-        "template_id": "4764acc29d604f1491d600504d2802fea156a47ad691420b63d2c52924570925",
-        "side_height": 4882241,
-        "parent_template_id": "12f8b044e315669904f81caa055eac84f9a7d30d93bdd446cf304d38f18097cf",
-        "miner": 3,
-        "effective_height": 4882241,
-        "nonce": 4026633700,
-        "extra_nonce": 2755066571,
-        "timestamp": 1681403011,
-        "software_id": 0,
-        "software_version": 196610,
-        "window_depth": 296,
-        "window_outputs": 29,
-        "transaction_count": 47,
-        "difficulty": 2099621378,
-        "cumulative_difficulty": 7610955003350240,
-        "pow_difficulty": 5985480395,
-        "pow_hash": "6e0cc4b1e989a18a8f7a65c5aa5ad252a071cd57d877cac4f14bb2b700000000",
-        "inclusion": 1,
-        "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-        "main_difficulty": 311336952550
-    }
-]
-        
-
- -
- -
-

/api/payouts/<id|address>?search_limit=[search_limit]

-

Response contains payouts received by this miner due to blocks on Monero network. Optional parameters are search_limit, maximum amount of records to return, set 0 to return all. Defaults to 10

-

.[].coinbase_private_key can be used to prove the payout is correct.

-
-[result has been cut due to size, relevant portions shown]
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/payouts/47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw
-[
-    {
-        "miner": 3,
-        "template_id": "cebb0c11be67c43c1a01cf2ac43e6bafe745a92012bbbb04be03842f56c5cc78",
-        "side_height": 5428093,
-        "main_id": "8e0c7b08f92be1bdc5c4153f7a8c3e536f1353aff898684477cc6ba857b8510e",
-        "main_height": 2910747,
-        "timestamp": 1687085695,
-        "coinbase_id": "e1b20914f4ff768277c19daa65ef5f2ace7cda6f3f2586caf569c3008b7510b1",
-        "coinbase_reward": 3400854816,
-        "coinbase_private_key": "9438ef07d1bb10ae05e68cdf900938807fe8639acf7dab27feedc08119072b02",
-        "coinbase_output_index": 26,
-        "global_output_index": 75393989,
-        "including_height": 5427726
-    },
-    {
-        "miner": 3,
-        "template_id": "9bab936c12790d7e006ef1e97bf00572e85eb2f5647ce6320491fc93ba47f6a0",
-        "side_height": 5427897,
-        "main_id": "8bf0f5f17c53a68e5fb36a89781acd84c5936c4b06287aad09c038dac8b52860",
-        "main_height": 2910737,
-        "timestamp": 1687083786,
-        "coinbase_id": "413b6cac611c49d543d058a7d4ea908ba8a3774ced6781c2fdcdbbc0b187f24a",
-        "coinbase_reward": 103013108631,
-        "coinbase_private_key": "4344896bdacc7c58758090b955c7fd098f7414cdf773ec39fd12808f1fb4090f",
-        "coinbase_output_index": 11,
-        "global_output_index": 75393229,
-        "including_height": 5427531
-    },
-    {
-        "miner": 3,
-        "template_id": "566257bf73bc937a7b5bb6c3b6c1a2008db078a56d4fcbb2e34bde6f2b847d55",
-        "side_height": 5427764,
-        "main_id": "d74f9c0201c248911d6c306f7e6b5d58dea469ecf40ffd891e59dd0f6d082aea",
-        "main_height": 2910725,
-        "timestamp": 1687082281,
-        "coinbase_id": "561ed794fbdf5502fbd934fe7d7e4b9f4ccd4a8f7436fb93bc51c1e6dd431357",
-        "coinbase_reward": 170359596687,
-        "coinbase_private_key": "0332c944b6919f8e284993c35f810ce6b0c43cbf5f743f339fd4aaa197787109",
-        "coinbase_output_index": 14,
-        "global_output_index": 75392201,
-        "including_height": 5427402
-    }
-]
-        
-
- -
- -
-

/api/found_blocks?limit=[limit]&miner=[id|address]

-

Response contains last found block records on the Monero network by P2Pool. Optional parameters are limit, maximum amount of records to return, defaults to 50, max 1000. Additionally, you can pass the miner parameter to select blocks from that miner only. coinbase parameter can also be used here to decode coinbase outputs.

-
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/found_blocks?limit=2
-[
-    {
-        "main_block": {
-            "id": "be284c7fc784ca83f92c2f1eac2e02524a99a63876179dae6c370a199d533d95",
-            "height": 2942191,
-            "timestamp": 1690866882,
-            "reward": 600373170000,
-            "coinbase_id": "691eb1e5b9222df2c667a481370acdd751c7f5f3cd0345ac9fc0f1feea396f7c",
-            "difficulty": 260315236919,
-            "side_template_id": "b76318caa4822826573ceebcabbd0309d2a710dd83700cb5d0cae206d9835cb4",
-            "coinbase_private_key": "5cdea4c89482c19588527238d32bdad97151c4e40800882183222d080ff5f203"
-        },
-        "side_height": 5789985,
-        "miner": 2915,
-        "effective_height": 5789985,
-        "window_depth": 438,
-        "window_outputs": 41,
-        "transaction_count": 2,
-        "difficulty": 1301336116,
-        "cumulative_difficulty": 9202373148639334,
-        "inclusion": 1,
-        "miner_address": "42HEEF3NM9cHkJoPpDhNyJHuZ6DFhdtymCohF9CwP5KPM1Mp3eH2RVXCPRrxe4iWRogT7299R8PP7drGvThE8bHmRDq1qWp"
-    },
-    {
-        "main_block": {
-            "id": "ac6ae3cfe0f241e22aae6b7a165e7254f380d50c5a690994aa462120c00c4f16",
-            "height": 2942190,
-            "timestamp": 1690866866,
-            "reward": 608015760000,
-            "coinbase_id": "a7f784cc724f9e172d3bea13a18569b339e96d218b3fa54f9189abacbeb349ae",
-            "difficulty": 260396879898,
-            "side_template_id": "be9e779f4f8b2cd96c6e30a94c28c6d36af261ecf7cc195394471f112b850c49",
-            "coinbase_private_key": "896cde95bf6713d60b7ed4bafa88018e7418cd55db9232db08dc2de174cebd0b"
-        },
-        "side_height": 5789983,
-        "miner": 2915,
-        "effective_height": 5789983,
-        "window_depth": 438,
-        "window_outputs": 41,
-        "transaction_count": 49,
-        "difficulty": 1301263505,
-        "cumulative_difficulty": 9202370546222535,
-        "inclusion": 1,
-        "miner_address": "42HEEF3NM9cHkJoPpDhNyJHuZ6DFhdtymCohF9CwP5KPM1Mp3eH2RVXCPRrxe4iWRogT7299R8PP7drGvThE8bHmRDq1qWp"
-    }
-]
-        
-
- - -
- -
-

/api/shares?limit=[limit]&miner=[id|address][&onlyBlocks][&noMainStatus][&noUncles][&noMiners]

-

Response contains last found SideChain block records on P2Pool. Optional parameters are limit, maximum amount of records to return, defaults to 50, max SIDECHAIN_PPLNS_WINDOW. Additionally, you can pass the miner parameter to select shares from that miner only. onlyBlocks can be set to not receive uncle share entries.

-
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/shares?limit=2
-[
-   {
-       "main_id": "7717e2e59fb345ce3e5c5585b51632f32db2e98bb0a69b64a0b91f7b370be3b7",
-       "main_height": 2942216,
-       "template_id": "7f0ce404ab3d576d97dc4affacb5e443fb4c1f36f1bb09feed5b9af3dcca0169",
-       "side_height": 5790240,
-       "parent_template_id": "7070785f6ac7c486ab2ae7df443e6afc31010f87206b44c90be1339cd2ecfc70",
-       "miner": 2915,
-       "effective_height": 5790240,
-       "nonce": 1660977225,
-       "extra_nonce": 2706560389,
-       "timestamp": 1690869415,
-       "software_id": 0,
-       "software_version": 196613,
-       "window_depth": 424,
-       "window_outputs": 40,
-       "difficulty": 1327149158,
-       "cumulative_difficulty": 9202726518962104,
-       "pow_difficulty": 1720691274,
-       "pow_hash": "36685a8106e8b6a7450820f97cba0fa3cd2aa017fb4119f3737ffe7e02000000",
-       "inclusion": 1,
-       "transaction_count": 9,
-       "miner_address": "42HEEF3NM9cHkJoPpDhNyJHuZ6DFhdtymCohF9CwP5KPM1Mp3eH2RVXCPRrxe4iWRogT7299R8PP7drGvThE8bHmRDq1qWp",
-       "main_difficulty": 260565791438
-   },
-   {
-       "main_id": "9ce6483c303ada746b9e969c65235bcba0433865f6afd11153e94fb0e6d41046",
-       "main_height": 2942216,
-       "template_id": "7070785f6ac7c486ab2ae7df443e6afc31010f87206b44c90be1339cd2ecfc70",
-       "side_height": 5790239,
-       "parent_template_id": "d7477275674bd90bba39e4604cf2539ae99460856567895369cda165370ba081",
-       "miner": 5695,
-       "effective_height": 5790239,
-       "nonce": 460496,
-       "extra_nonce": 346986124,
-       "timestamp": 1690869412,
-       "software_id": 0,
-       "software_version": 196612,
-       "window_depth": 425,
-       "window_outputs": 40,
-       "difficulty": 1326689571,
-       "cumulative_difficulty": 9202725191812946,
-       "pow_difficulty": 1929563612,
-       "pow_hash": "1ba49ad252103dc9e5eba606a3c1fac0875e01b6163201d6b6f0d23902000000",
-       "inclusion": 1,
-       "transaction_count": 9,
-       "miner_address": "48bSCfGTKE4Kb3Fe3YWYgmNjgxjGqxZsP4mwHEQDVUQST2HxRGEX2566LbfmBE5bW2JcByTnjsWZiQZWj6f8XSpX4EDE9Gj",
-       "main_difficulty": 260565791438
-   }
-]
-        
-
- - -
- -
-

/api/block_by_id/<blockId>[/full|/light|/raw|/info|/payouts|/coinbase]

-

Response contains a block/share record on P2Pool by Template, Main Id, or Coinbase Id. You can select the JSON version or the binary format, or the full/light JSON format of the raw share.

-

Using /payouts you can query all payouts that include weights by this share.

-

Using /coinbase you can query all possible outputs and ownership for found and not found blocks.

-
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/block_by_id/179cf9d77c7d198dc4d9a5deb03561b6121336fe9ea72fbda00fbf6eea34a34d
-{
-    "main_id": "abf18da3388c1c5c8ca372ac4804c6b1b2adb511d6a6cc5f781d883211568eb0",
-    "main_height": 2863113,
-    "template_id": "179cf9d77c7d198dc4d9a5deb03561b6121336fe9ea72fbda00fbf6eea34a34d",
-    "side_height": 4877733,
-    "parent_template_id": "ffd6a96ce3131f043f273582a69d1eaa42b7557f47f7b241140cb22af9fbf556",
-    "miner": 3,
-    "effective_height": 4877733,
-    "nonce": 1694662668,
-    "extra_nonce": 2797508428,
-    "timestamp": 1681355864,
-    "software_id": 0,
-    "software_version": 196610,
-    "window_depth": 327,
-    "window_outputs": 33,
-    "difficulty": 1956175240,
-    "cumulative_difficulty": 7601017513575704,
-    "pow_difficulty": 1301275836828,
-    "pow_hash": "602d39809af709405eb8660028fdfa5bf80e71e6a4ec1fbb924ed80000000000",
-    "inclusion": 1,
-    "transaction_count": 5,
-    "mined_main_at_height": true,
-    "miner_address": "47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-    "main_difficulty": 332971955880
-}
-        
-
- - - -
- -
-

/api/block_by_height/<blockHeight>[/full|/light|/raw|/info|/payouts|/coinbase]

-

Response contains a block/share record on P2Pool by SideChain height. You can select the JSON version or the binary format, or the full/light JSON format of the raw share.

-

Using /payouts you can query all payouts that include weights by this share.

-

Using /coinbase you can query all possible outputs and ownership for found and not found blocks.

-
-curl --silent https://{%s p.Context().NetServiceAddress %}/api/block_by_height/4868351
-{
-    "main_id": "5b468e062dc4aa1570f956e6314f6d0634a772aa8d2905d8ab9c83026a7ac6af",
-    "main_height": 2862309,
-    "template_id": "09d1974eea6de73d3cb95bc495b0b24a7b6f6405eaada53c4ec1ad3f513a4cc8",
-    "side_height": 4868351,
-    "parent_template_id": "b74acca03b7593386dd54b8132bf79522c9861b41f7797d0ce4f2af5f35cc170",
-    "miner": 1,
-    "effective_height": 4868351,
-    "nonce": 3271624915,
-    "extra_nonce": 1072029533,
-    "timestamp": 1681257577,
-    "software_id": 0,
-    "software_version": 196609,
-    "window_depth": 323,
-    "window_outputs": 30,
-    "difficulty": 2002581944,
-    "cumulative_difficulty": 7579715115168858,
-    "pow_difficulty": 3782914221,
-    "pow_hash": "fe471ad3fbd112fabe2abebafeccd2cb8eb0077864e7cfe939eaa62201000000",
-    "inclusion": 1,
-    "transaction_count": 28,
-    "miner_address": "4ApeMQzcRS7huBW8HEGkug2e5kQxhhC2Ub9y8F9bc7arKHsCktAV24n5ezKM9PjX5yS3Pv4PPNMXT8rTAFVuKCRi1jEqJZd",
-    "main_difficulty": 326506434325
-}
-        
-
- - -
-
-

/api/events WebSocket Event API

-

- Connect to this using a WebSocket library. Produces a stream of JSON events for new blocks (type side_block), found blocks (type found_block), orphaned blocks (type orphaned_block). -

-[result has been cut due to size, relevant portions shown]
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"77d54ef67b566439c7df43614928ee6a503d82fb16f69720ed2ca0852b4ea5b6",
-      "main_height":2871492,
-      "template_id":"7c5db68e5af86e66df94d03688a8f61d7e7cd4544dbadc3f4d2f7972c0c72b17",
-      "side_height":4973833,
-      "parent_template_id":"10e2b50eb5c688bea4852f25b0d3550fe0e4e8192ea0602a373fb48454496e20",
-      "miner":19,
-      "uncle_of":"372ee3a80d8ab9131878a56e5e2802aa3f797a55d38f76871f1eeeafd60cd828",
-      "effective_height":4973834,
-      "nonce":4127591028,
-      "extra_nonce":3737539655,
-      "timestamp":1682358001,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":333,
-      "window_outputs":31,
-      "transaction_count":2,
-      "difficulty":1902041368,
-      "cumulative_difficulty":"0000000000000000001ba95c40814041",
-      "pow_difficulty":39725018671,
-      "pow_hash":"f6ddc7bdb80b6050fc7dd9fea1ad009434b7bcda3e3cb309a295ad1b00000000",
-      "inclusion":1,
-      "miner_address":"4B4mXWBqrFYGk9W8t1J3UifSdnU723HHKf8VaWGSZGzLZUncHRJetPCjbDUha1udPd2ZpJrwYCmPv1jzvg3NCf7Z1YViuoa",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"808c9c2a413b8480cc5b445575580a6edd5d237cc120f18a668c6f602dff9c1b",
-      "main_height":2871492,
-      "template_id":"372ee3a80d8ab9131878a56e5e2802aa3f797a55d38f76871f1eeeafd60cd828",
-      "side_height":4973834,
-      "parent_template_id":"8d691190875157d160c8bc8787ab140b1855c1afcc7b8f3076bc3366711bb085",
-      "miner":2,
-      "effective_height":4973834,
-      "nonce":402718979,
-      "extra_nonce":3611692200,
-      "timestamp":1682358008,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":2,
-      "difficulty":1900059444,
-      "cumulative_difficulty":"0000000000000000001ba95d2320b48d",
-      "pow_difficulty":4388053454,
-      "pow_hash":"727019817d63c52ac2ce2067652804cbfc8d3be778fe5548d0bf91fa00000000",
-      "inclusion":1,
-      "miner_address":"435QwkC3fJ4YeX929gmaBMeuUwR1wdmRrQBNyoKfpJdHKDbqnukHJeA768yt8ngnS7UTHRJ2yJmnWP8gCz6s3kkiBX8C363",
-      "uncles":[
-         {
-            "template_id":"7c5db68e5af86e66df94d03688a8f61d7e7cd4544dbadc3f4d2f7972c0c72b17",
-            "miner":19,
-            "side_height":4973833,
-            "difficulty":1902041368
-         }
-      ],
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"220534c981f71a7d2d935825b5cf479d960345177387eb7fcd196a762b018453",
-      "main_height":2871492,
-      "template_id":"320a1a1d8849d48243670938efa640a271b289fae392ea35eb1a7daf88d3e2c8",
-      "side_height":4973835,
-      "parent_template_id":"372ee3a80d8ab9131878a56e5e2802aa3f797a55d38f76871f1eeeafd60cd828",
-      "miner":3,
-      "effective_height":4973835,
-      "nonce":3825436994,
-      "extra_nonce":2417583792,
-      "timestamp":1682358009,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":3,
-      "difficulty":1899187725,
-      "cumulative_difficulty":"0000000000000000001ba95d9454029a",
-      "pow_difficulty":4870458263,
-      "pow_hash":"d6b5911b6b13e6258e2f1d23ab5256cf9b3c4a002e0722f15b4cc0e100000000",
-      "inclusion":1,
-      "miner_address":"47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"3e735ce653c2f1c312125309367ad6016e3f55c80834efd63fdb94d0929d017a",
-      "main_height":2871492,
-      "template_id":"3f089efbc7fd31eb98705665acbbe1e7d3b290866cb94f39a0d2e072f2a11f14",
-      "side_height":4973836,
-      "parent_template_id":"320a1a1d8849d48243670938efa640a271b289fae392ea35eb1a7daf88d3e2c8",
-      "miner":9,
-      "effective_height":4973836,
-      "nonce":132358,
-      "extra_nonce":1448692177,
-      "timestamp":1682358019,
-      "software_id":0,
-      "software_version":196609,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":7,
-      "difficulty":1896666403,
-      "cumulative_difficulty":"0000000000000000001ba95e0560d7bd",
-      "pow_difficulty":15371353282,
-      "pow_hash":"cf9774d250b905729cb988b24437ac5f27ccc55d78d6388ca7a8874700000000",
-      "inclusion":1,
-      "miner_address":"4AxaapsBMRYMJHFV5uApnFJth7stf4GiK9sqnLAH8SizguBbZ8YYtE2C3aF3UGVbh3ATRKdtd7ai1Hi1zfzMkdqmAZLqaGN",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"3ecbfae652abf9ecfd043d8921b3c18f9acc6f08a8521bd4a06c46b8ff5181f9",
-      "main_height":2871492,
-      "template_id":"ba8514bd7403c16a61b7e20f34981d9cb72de09a7b0593d55ef14d55bff1908e",
-      "side_height":4973837,
-      "parent_template_id":"3f089efbc7fd31eb98705665acbbe1e7d3b290866cb94f39a0d2e072f2a11f14",
-      "miner":2,
-      "effective_height":4973837,
-      "nonce":1879082776,
-      "extra_nonce":1309685883,
-      "timestamp":1682358030,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":8,
-      "difficulty":1897117455,
-      "cumulative_difficulty":"0000000000000000001ba95e76748ecc",
-      "pow_difficulty":4828639998,
-      "pow_hash":"979dbacd27e0c15bfa93a72bda30f9e4e472ab86cfc9bc5f71ceb4e300000000",
-      "inclusion":1,
-      "miner_address":"435QwkC3fJ4YeX929gmaBMeuUwR1wdmRrQBNyoKfpJdHKDbqnukHJeA768yt8ngnS7UTHRJ2yJmnWP8gCz6s3kkiBX8C363",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"f7aab8fda85491a5b3847435fa16ae925d55b60791c01faf5a23c6d5365ef6ca",
-      "main_height":2871492,
-      "template_id":"19278cd3a3022a25edf4b91a5544a88b8c8f6f78f84e862b01781de493b562c8",
-      "side_height":4973838,
-      "parent_template_id":"ba8514bd7403c16a61b7e20f34981d9cb72de09a7b0593d55ef14d55bff1908e",
-      "miner":3,
-      "effective_height":4973838,
-      "nonce":2868937852,
-      "extra_nonce":869103007,
-      "timestamp":1682358036,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":8,
-      "difficulty":1894821734,
-      "cumulative_difficulty":"0000000000000000001ba95ee7653e32",
-      "pow_difficulty":2019716446,
-      "pow_hash":"2d03c8818798cb1bde4a6d2d64d0a59a04b8228e94aff841599c632002000000",
-      "inclusion":1,
-      "miner_address":"47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"877b4df53e2347e19d94ba52e04522601d461c0dc5d0439e8ec87a5ecb58a31f",
-      "main_height":2871492,
-      "template_id":"000c39fc469e4062fe4da6fc8444c3c990ad817397c94abe13c46720a4c9ab75",
-      "side_height":4973839,
-      "parent_template_id":"19278cd3a3022a25edf4b91a5544a88b8c8f6f78f84e862b01781de493b562c8",
-      "miner":2,
-      "effective_height":4973839,
-      "nonce":822086856,
-      "extra_nonce":1665899192,
-      "timestamp":1682358038,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":10,
-      "difficulty":1893298342,
-      "cumulative_difficulty":"0000000000000000001ba95f583eaed8",
-      "pow_difficulty":29696623614,
-      "pow_hash":"b1aa60a163d39b0524f7448954f770de8cbb873e346b63e37659062500000000",
-      "inclusion":1,
-      "miner_address":"435QwkC3fJ4YeX929gmaBMeuUwR1wdmRrQBNyoKfpJdHKDbqnukHJeA768yt8ngnS7UTHRJ2yJmnWP8gCz6s3kkiBX8C363",
-      "main_difficulty":344857075709
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"b6516743e3693824f0d8eceab7109c4242661f78e498f78e42a57bd602b7b167",
-      "main_height":2871493,
-      "template_id":"af88e464df1f31761996031f5c851f1a59d6ecb4b6e917407e1164ce6a569a07",
-      "side_height":4973840,
-      "parent_template_id":"000c39fc469e4062fe4da6fc8444c3c990ad817397c94abe13c46720a4c9ab75",
-      "miner":19,
-      "effective_height":4973840,
-      "nonce":1543602719,
-      "extra_nonce":2911703055,
-      "timestamp":1682358053,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":0,
-      "difficulty":1893967043,
-      "cumulative_difficulty":"0000000000000000001ba95fc922539b",
-      "pow_difficulty":1157101996504,
-      "pow_hash":"d855eed5d88837eb44c3d0a80457981fd39e9af67e8b58a03142f30000000000",
-      "inclusion":1,
-      "miner_address":"4B4mXWBqrFYGk9W8t1J3UifSdnU723HHKf8VaWGSZGzLZUncHRJetPCjbDUha1udPd2ZpJrwYCmPv1jzvg3NCf7Z1YViuoa",
-      "mined_main_at_height":true,
-      "main_difficulty":344633368046
-   }
-}
-{
-   "type":"found_block",
-   "found_block":{
-      "main_block":{
-         "id":"b6516743e3693824f0d8eceab7109c4242661f78e498f78e42a57bd602b7b167",
-         "height":2871493,
-         "timestamp":1682358053,
-         "reward":600000000000,
-         "coinbase_id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "difficulty":344633368046,
-         "side_template_id":"af88e464df1f31761996031f5c851f1a59d6ecb4b6e917407e1164ce6a569a07",
-         "coinbase_private_key":"1331ab860507192987e98497b971daac66e8804c51ad115ab6b2bf3d2abd270d"
-      },
-      "side_height":4973840,
-      "miner":19,
-      "effective_height":4973840,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":0,
-      "difficulty":1893967043,
-      "cumulative_difficulty":"0000000000000000001ba95fc922539b",
-      "inclusion":1,
-      "miner_address":"4B4mXWBqrFYGk9W8t1J3UifSdnU723HHKf8VaWGSZGzLZUncHRJetPCjbDUha1udPd2ZpJrwYCmPv1jzvg3NCf7Z1YViuoa"
-   },
-   "main_coinbase_outputs":[
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":0,
-         "global_output_index":72132937,
-         "miner":30,
-         "value":1747977812,
-         "miner_address":"47J5KAEgevN8TKMhstn85mbASkhpZq5BtSt9afcseHPEVtDggZ8uBdxgoTRjdP2ooS4Px1y1pXeryhr9UFQqaykY5J4Ev85"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":1,
-         "global_output_index":72132938,
-         "miner":195,
-         "value":1736642632,
-         "miner_address":"47goCBjSdpqAGUFkc1nM9F7gNcSfSjtKuTqcLxGpEUn6DgboSZMpn2CMRkQ2JKWbxRJvHy2fbSJxtFqXVDbQqs5wPpxgbYD"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":2,
-         "global_output_index":72132939,
-         "miner":10,
-         "value":1737989471,
-         "miner_address":"46vavwU3QDmK2Jdw6CgezsB5538rbwFGi67EPWqkEBfvWuuiFTNaPVQPNSbxVc1UNbN6E7xakVkCxCnttvgyohaN4U9F7kf"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":3,
-         "global_output_index":72132940,
-         "miner":1,
-         "value":3139170892,
-         "miner_address":"4ApeMQzcRS7huBW8HEGkug2e5kQxhhC2Ub9y8F9bc7arKHsCktAV24n5ezKM9PjX5yS3Pv4PPNMXT8rTAFVuKCRi1jEqJZd"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":4,
-         "global_output_index":72132941,
-         "miner":47,
-         "value":1751320020,
-         "miner_address":"47Eqp7fsvVnPPSU4rsXrKJhyAme6LhDRZDzFky9xWsWUS9pd6FPjJCMDCNX1NnNiDzTwfbAgGMk2N6A1aucNcrkhLffta1p"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":5,
-         "global_output_index":72132942,
-         "miner":26,
-         "value":6966089461,
-         "miner_address":"42yAHAH5at9ZHvMXvb1xPV2aKnoibjZWCfTgQg2HdxumFx857g3DK75GbDKzmvu8QMW8CnWxuPAYyfQADjwKw6dy78x2gbp"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":6,
-         "global_output_index":72132943,
-         "miner":6,
-         "value":1739307539,
-         "miner_address":"4AzyJ87Tp3yLXjA3E5v9iAEnTTLu6UTeSdatiwto92n91z7NE11QmpV4YGatwS3ihvFsmvTfz85bfKMLBBAsZMGFKcJJ8jG"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":7,
-         "global_output_index":72132944,
-         "miner":18,
-         "value":11494230879,
-         "miner_address":"4BBb6ZBPPPWivZnx2m3m7YUUGQv54F9HfjPWurf2Xy2UDQwwPyb956dGQ3p9whrxNJ568zc3Ygfr9McVT1UJyRKtLi1jcMb"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":8,
-         "global_output_index":72132945,
-         "miner":19,
-         "value":39395535008,
-         "miner_address":"4B4mXWBqrFYGk9W8t1J3UifSdnU723HHKf8VaWGSZGzLZUncHRJetPCjbDUha1udPd2ZpJrwYCmPv1jzvg3NCf7Z1YViuoa"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":9,
-         "global_output_index":72132946,
-         "miner":842,
-         "value":3477271817,
-         "miner_address":"457Ht8LpLt2iq8y5HShzySduWTC58JvGLj6eWiURzUxNVXrDBWJc9jd3niHHLZ4o6hHYpufyd8zhL9EMktn3p3EdHQ6LAkj"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":10,
-         "global_output_index":72132947,
-         "miner":1104,
-         "value":1746268557,
-         "miner_address":"48LszLe98KgS1H4ELW9rmEH6dTar1uWdZDx7ttDHNSZvcANADw9PUbDU3KWREmgozxMbzofNmj8m5ZuieukEsj7aHjfo3in"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":11,
-         "global_output_index":72132948,
-         "miner":445,
-         "value":1748663264,
-         "miner_address":"44XavwVX8pP2U5NxtwGusU2obpoK55nNVBBqWLC2aJBX2yhAozsC1tvVG8NcC2PEQy2SRmBuB4WqTfVgfBCS3NkY1wYHZ3T"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":12,
-         "global_output_index":72132949,
-         "miner":156,
-         "value":1755605809,
-         "miner_address":"42FUiB6725E4UDkVXMJ3nZ21dhV9atHP9Z1yqxMGfNhnDkJUhdxDaTGaCej35H346eS5vcbEVY4kzdLVhKM63LjNJxFYMUF"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":13,
-         "global_output_index":72132950,
-         "miner":23,
-         "value":1749533284,
-         "miner_address":"46aSNdCK35w9fdX3iCwk3GLt3iZ4hnR34j2mJtxE14MWLXB5JRXxvzmjMUoz2mmFZk9mkqQRpCbPd2TbFqcQxqGkHxJPHUK"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":14,
-         "global_output_index":72132951,
-         "miner":61,
-         "value":3491378711,
-         "miner_address":"44YdBost8RfFjVT9W712c5KN1ZeTq5rPC6Uu12D4nBkUMBHxeb7TxKfGnAGLbfsvbaKtBvwzqKLqBWf7cpU7d2nZ55pXeCU"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":15,
-         "global_output_index":72132952,
-         "miner":647,
-         "value":24776511644,
-         "miner_address":"4AQ3YkqG2XdWsPHEgrDGdyQLq1qMMGFqWTFJfrVQW99qPmCzZKvJqzxgf5342KC17o9bchfJcUzLhVW9QgNKTYUBLg876Gt"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":16,
-         "global_output_index":72132953,
-         "miner":11,
-         "value":1739154107,
-         "miner_address":"42safVv7VKVLrGc1RWEgVqi9YJRV8XiUeFViStfLPWE8FAH1i4HvovcXntT8k5MvVuKkpYLcjELwL4Azrcb65MZC2AkoDoJ"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":17,
-         "global_output_index":72132954,
-         "miner":4,
-         "value":82984625360,
-         "miner_address":"4ADyLD6c7hzT5gCYxWnw7k2q5RPDxwqNTMsHcz9n91ir22YJbjBQ4LjGZgcLUVV7tyG33vXfB5SR1iCWaCjEQ7BNJUg6SyJ"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":18,
-         "global_output_index":72132955,
-         "miner":111,
-         "value":1741890162,
-         "miner_address":"45vMeqsvpW5UoQ4HQDjbUoLpmrLkhyVKgiSeR9WwuCpLV9egZtkwGGYPJ5HDvdAVtgF4bcu93dzEc6mLFbYRqVJzAQWHRnL"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":19,
-         "global_output_index":72132956,
-         "miner":8,
-         "value":5240626741,
-         "miner_address":"47oeGVdUWXDPSqjgFd2dQS57S3W4KPGnneq1o8hAdGE2ZwMZzKHeLFhiQoguKMFq4jScJ2qL7jrHoiP8xh2Su6SuFtxk3Da"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":20,
-         "global_output_index":72132957,
-         "miner":5283,
-         "value":1750270617,
-         "miner_address":"45j2xT1JbU27Mv8ScdumCC5bg3P9EU1HEE2DKZ9TzVje6wVE8kKqviwFuvwaoNywwzh7uiJoxfpWe4jp5HvYDZgyKdU2iEt"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":21,
-         "global_output_index":72132958,
-         "miner":179,
-         "value":1749846757,
-         "miner_address":"42TUuapMMbs4taYHYFnFTeFAM57WzVKDNXn4yeAQcJcgHq1m6q993EedsF2ebd7WP5Gjixw3NPu6ncH7WFWfsPDiPSWEnr9"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":22,
-         "global_output_index":72132959,
-         "miner":639,
-         "value":3497561198,
-         "miner_address":"49bNJ3mdVBfGjr3vWTVhXNig75gCxHQCwNG2FyvRErxs7U4A1c8K6Pn39Tq8oZAxtbXsfNsBtyKzT8apMkYD67QTS2v42oW"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":23,
-         "global_output_index":72132960,
-         "miner":15,
-         "value":5247959737,
-         "miner_address":"44N5jui2nbtQaxWjs9hoJBX5ELrCk5ftja7Zu3QCGTA1FftJhDKrEDL92Rw12j8FALHKnJEUb6KZ45SAncJ6iqKN6nShj2S"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":24,
-         "global_output_index":72132961,
-         "miner":2161,
-         "value":1750436503,
-         "miner_address":"44WHz1PGu8mYWxVE1p6vVtXDtvKtkd7eY23DdXncavA9Ybb6kP9i6BrhZschXpxGCHdTfBoLDznv55SYUqDhoCyJJxV84tD"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":25,
-         "global_output_index":72132962,
-         "miner":91,
-         "value":2100792894,
-         "miner_address":"48tdvJBtQN5Vt3jmLDTTYX7dpS9oDXATAdD8XFhuE1CshWUdShUsEif6UivD5iGsroKGEKqxGqA1aFA2XVx8rtmiFAqKT6E"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":26,
-         "global_output_index":72132963,
-         "miner":2,
-         "value":125897735171,
-         "miner_address":"435QwkC3fJ4YeX929gmaBMeuUwR1wdmRrQBNyoKfpJdHKDbqnukHJeA768yt8ngnS7UTHRJ2yJmnWP8gCz6s3kkiBX8C363"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":27,
-         "global_output_index":72132964,
-         "miner":5190,
-         "value":1741473336,
-         "miner_address":"49trSMSTsAmE2uMeCo1MmEUvfk6i9DWF15skLn9hut11ehcq1vLHkYi9sTujrcDG61dLFk61LeWMafa3EkamaGEUD1fSvRe"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":28,
-         "global_output_index":72132965,
-         "miner":9,
-         "value":50246935661,
-         "miner_address":"4AxaapsBMRYMJHFV5uApnFJth7stf4GiK9sqnLAH8SizguBbZ8YYtE2C3aF3UGVbh3ATRKdtd7ai1Hi1zfzMkdqmAZLqaGN"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":29,
-         "global_output_index":72132966,
-         "miner":4990,
-         "value":15680539194,
-         "miner_address":"4B4aCvEcZr6GcusVJfEds2LXixCeJ2dQBaDUCguWmzi5L7PW5tVXfAnE4cn1mQdiNzH6zWcEPMQTiYTsNcX44ryxCJWZKZH"
-      },
-      {
-         "id":"9ba3e0449f6ff3a108a877f2f37eedf0b87f811ff5725634e109ba0d2789d479",
-         "index":30,
-         "global_output_index":72132967,
-         "miner":3,
-         "value":190176655762,
-         "miner_address":"47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw"
-      }
-   ]
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"5594278518615f55a7e911cca93280fd0c3d2dff1d1fd184db6602e0afb69e0b",
-      "main_height":2871494,
-      "template_id":"0ef3e5ddcb0c4f6398eb833361b3749f92fc232a049c52052c5da956c9bbc868",
-      "side_height":4973841,
-      "parent_template_id":"af88e464df1f31761996031f5c851f1a59d6ecb4b6e917407e1164ce6a569a07",
-      "miner":4,
-      "effective_height":4973841,
-      "nonce":2466322468,
-      "extra_nonce":893603150,
-      "timestamp":1682358055,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":0,
-      "difficulty":1894526953,
-      "cumulative_difficulty":"0000000000000000001ba9603a0e8384",
-      "pow_difficulty":11901446960,
-      "pow_hash":"bbe4a67f6a9bca4028ca1faa940d692f834978d10ed18e34d07b625c00000000",
-      "inclusion":1,
-      "miner_address":"4ADyLD6c7hzT5gCYxWnw7k2q5RPDxwqNTMsHcz9n91ir22YJbjBQ4LjGZgcLUVV7tyG33vXfB5SR1iCWaCjEQ7BNJUg6SyJ",
-      "main_difficulty":344205037143
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"6c50a9aa027a09b41c56f965f8e21041649c1638a956576fc3fc350441e0f4ab",
-      "main_height":2871494,
-      "template_id":"5f72a35b571746ac3835b204020e548e65a7350d84d5d7a7aa3a749dc058f7ad",
-      "side_height":4973842,
-      "parent_template_id":"0ef3e5ddcb0c4f6398eb833361b3749f92fc232a049c52052c5da956c9bbc868",
-      "miner":19,
-      "effective_height":4973842,
-      "nonce":1090620480,
-      "extra_nonce":162834581,
-      "timestamp":1682358073,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":1,
-      "difficulty":1893992163,
-      "cumulative_difficulty":"0000000000000000001ba960aaf28a67",
-      "pow_difficulty":4704880707,
-      "pow_hash":"bc26c47392a94004bc736822520f67bbf56dfba432e6d75aa12ab2e900000000",
-      "inclusion":1,
-      "miner_address":"4B4mXWBqrFYGk9W8t1J3UifSdnU723HHKf8VaWGSZGzLZUncHRJetPCjbDUha1udPd2ZpJrwYCmPv1jzvg3NCf7Z1YViuoa",
-      "main_difficulty":344205037143
-   }
-}
-{
-   "type":"side_block",
-   "side_block":{
-      "main_id":"617ea6fe4a47a3e7f51c8cc719a5158b88a4ca2583cd2f0cd5ed0b006bca3564",
-      "main_height":2871494,
-      "template_id":"b95dcd9d440fcd49a050c76f006189c01e1857d11bfc0b41f3fa560c15a4750f",
-      "side_height":4973843,
-      "parent_template_id":"5f72a35b571746ac3835b204020e548e65a7350d84d5d7a7aa3a749dc058f7ad",
-      "miner":3,
-      "effective_height":4973843,
-      "nonce":4026630147,
-      "extra_nonce":2353672144,
-      "timestamp":1682358101,
-      "software_id":0,
-      "software_version":196610,
-      "window_depth":332,
-      "window_outputs":31,
-      "transaction_count":15,
-      "difficulty":1895322145,
-      "cumulative_difficulty":"0000000000000000001ba9611beadc88",
-      "pow_difficulty":6105552648,
-      "pow_hash":"5c06f2ebd7fc22dca35bed2a0faa9e1aad7e36a5c89af94d587915b400000000",
-      "inclusion":1,
-      "miner_address":"47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw",
-      "main_difficulty":344205037143
-   }
-}
-    
-

-
- -
-
-

Redirect APIs

-

Several endpoints are available that redirect/forward your request via Location HTTP header to the relevant API listed above.

-

-

/api/last_found[/raw|...]?...

- Redirects to the /api/block_by_id/... record for the last block found on the Monero network by P2Pool. -

-

-

/api/tip[/raw|...]?...

- Redirects to the /api/block_by_id/... record for the last share found by P2Pool. -

-
-
- -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/base.qtpl b/cmd/web/views/base.qtpl deleted file mode 100644 index b2e553f..0000000 --- a/cmd/web/views/base.qtpl +++ /dev/null @@ -1,77 +0,0 @@ -{% import ( - _ "embed" -) %} - -{% code -//go:embed "css/style.css" -var styleCssContent string -%} - -{% interface -Page { - Title() - Content() - Style() -} -%} - -{% code -type ContextProviderPage interface { - Page - Context() *GlobalRequestContext -} -type ContextSetterPage interface { - ContextProviderPage - SetContext(ctx *GlobalRequestContext) -} -%} - -Page prints a page implementing Page interface. -{% func PageTemplate(p ContextProviderPage) %} - - - - - - - {%=h p.Title() %} - - - - {%= Header(p.Context()) %} - -
- {%= p.Content() %} -
- - {%= Footer(p.Context()) %} - - -{% endfunc %} - -Base page implementation. Other pages may inherit from it if they need -overriding only certain Page methods -{% code type BasePage struct { - ctx *GlobalRequestContext -} %} -{% func (p *BasePage) Title() %} - {% if p.Context().SiteTitle == "" %} - P2Pool Observer - {% else %} - {%s p.Context().SiteTitle %} - {% endif %} -{% endfunc %} -{% func (p *BasePage) Body() %}{% endfunc %} -{% code -func (p *BasePage) Context() *GlobalRequestContext { - return p.ctx -} -func (p *BasePage) SetContext(ctx *GlobalRequestContext) { - p.ctx = ctx -} -%} -{% func (p *BasePage) Style() %} -{%s= styleCssContent %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/blocks.qtpl b/cmd/web/views/blocks.qtpl deleted file mode 100644 index 0c1ff0b..0000000 --- a/cmd/web/views/blocks.qtpl +++ /dev/null @@ -1,42 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} - -{% code -type BlocksPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - FoundBlocks []*index.FoundBlock - Miner *address.Address -} -%} - -{% func (p *BlocksPage) Title() %} -{% if p.Miner == nil %} -{%= p.BasePage.Title() %} - Recent Found Monero blocks -{% else %} -{%= p.BasePage.Title() %} - Found Monero blocks by {%z= p.Miner.ToBase58() %} -{% endif %} -{% endfunc %} - -{% func (p *BlocksPage) Content() %} -{% if p.Miner == nil %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
-{%endif %} - -
- {% if p.Miner == nil %} -

Recent Found Monero blocks

- {% else %} -

{%= p.BasePage.Title() %} - Found Monero blocks by {%z= p.Miner.ToBase58() %}

- {% endif %} - {%= TemplateFoundBlocks(p.Context(), p.FoundBlocks, p.Miner != nil) %} -
- -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/calculate-share-time.qtpl b/cmd/web/views/calculate-share-time.qtpl deleted file mode 100644 index 1275f23..0000000 --- a/cmd/web/views/calculate-share-time.qtpl +++ /dev/null @@ -1,170 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/utils" %} -{% code -type CalculateShareTimePage struct { - // inherit from base page, so its' title is used in error page. - BasePage - - Hashrate float64 - Magnitude float64 - Efforts []CalculateShareTimePageEffortEntry - EstimatedRewardPerDay uint64 - EstimatedSharesPerDay float64 - EstimatedBlocksPerDay float64 -} -%} - -{% code -type CalculateShareTimePageEffortEntry struct { - Effort float64 - Probability float64 - Between float64 - BetweenSolo float64 - ShareProbabilities []float64 -} -%} - -{% func (p *CalculateShareTimePage) Title() %} -{%= p.BasePage.Title() %} - Average Share Time Calculator -{% endfunc %} - -{% func (p *CalculateShareTimePage) Content() %} - - - -
-
-

Average Share Time Calculator

-

- This tool calculates your average expected time between shares on the pool at current difficulty. -
- Do note this time will vary (can get shares sooner, or later, even way later), depending on your luck, and changing difficulty of the pool due to increase / reductions in hashrate. -
- For lower hashrate, this average is what statistically will be achieved long term. -
- The long term average per-share effort would be 100% -

-
-
- - -
-
- -
-
-
- - - -{% if p.Hashrate > 0 %} -
-
- - - {% 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)) %} - - - - - - - - - - - - - - - - - -
P2Pool Difficulty
{%s si_units(p.Context().Pool.SideChain.LastBlock.Difficulty, 2) %}
P2Pool Hashrate
{%s si_units(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s
Your Hashrate
{%s si_units(p.Hashrate * p.Magnitude, 2) %}H/s
Your Share Mean
{%s time_duration_long(between) %}
Your Daily Mean Shares
{%f.3 p.EstimatedSharesPerDay %} share(s)
 
Monero Difficulty
{%s si_units(p.Context().Pool.MainChain.Difficulty.Lo, 2) %}
Monero Hashrate
{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.Consensus.BlockTime)), 2) %}H/s
P2Pool Block Mean
{%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime))) %}
Your Solo Block Mean
{%s time_duration_long(between_solo) %}
Your Solo Daily Mean Blocks
{%f.3 p.EstimatedBlocksPerDay %} block(s)
 
Your Estimated Daily Reward
{%s monero_to_xmr(p.EstimatedRewardPerDay) %} XMR
- -
-

Single Share Effort

- - - - - - - - - {% for _, e := range p.Efforts %} - - - - - - - {% endfor %} -
Share EffortFound ProbabilityP2Pool EstimateSolo Estimate
- {%f.0 e.Effort %}% - - {%f.5 e.Probability %}% - - {%s time_duration_long(e.Between) %} - - {%s time_duration_long(e.BetweenSolo) %} -
- -
-

Cumulative Share Effort

- - - - - - - - - {% for i := range p.Efforts[0].ShareProbabilities %} - - {% endfor %} - - - {% for _, e := range p.Efforts %} - - - - {% code var mode = utils.ProbabilityMode(e.ShareProbabilities...) %} - {% for _, p := range e.ShareProbabilities %} - {% if p*100 > 1 %} - - {% else %} - - {% endif %} - {% endfor %} - - {% endfor %} - - - - {% for i := range p.Efforts[0].ShareProbabilities %} - - {% endfor %} - -
Number of shares
Cumulative EffortCumulative Time{%d i %}
- {%f.0 e.Effort %}% - - {%s time_duration_long(e.Between) %} - {%f.0 p*100 %}%
{%d i %}
-
-{% endif %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/connectivity-check.qtpl b/cmd/web/views/connectivity-check.qtpl deleted file mode 100644 index 18afda9..0000000 --- a/cmd/web/views/connectivity-check.qtpl +++ /dev/null @@ -1,128 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} -{% import p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" %} -{% import "net/netip" %} - -{% code -type ConnectivityCheckPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - - Address netip.AddrPort - YourTip *index.SideBlock - YourTipRaw *sidechain.PoolBlock - OurTip *index.SideBlock - Check *p2pooltypes.P2PoolConnectionCheckInformation[*sidechain.PoolBlock] -} -%} - -{% func (p *ConnectivityCheckPage) Title() %} -{%= p.BasePage.Title() %} - Connectivity Check -{% endfunc %} - -{% func (p *ConnectivityCheckPage) Content() %} -
-
-

Connectivity Check

-

- This tool tries to connect or looks up a running P2Pool node to verify ports have been properly opened. -
- You need to have external ports opened for this tool to work. -

-
-
- -
-
- -
-
-
- - - -{% if p.Address.IsValid() %} -
-
-

Connectivity Check Result

- {% if p.Check == nil %} -

Could not connect to host.

- {% elseif p.Check.Error != "" && p.Check.Address == "" %} -

Error connecting to host: {%s p.Check.Error %}.

- {% if p.Check.Error == "disconnected before finishing handshake" %} -

This peer is likely running on a different SideChain or is running an incompatible P2Pool software version. Try a different Observer or verify your software.

- {% elseif p.Check.Error == "not_valid_ip" %} -

The IP Address you are trying to connect to is not valid or accepted on this tool.

- {% elseif p.Check.Error == "not_valid_port" %} -

The Port you are trying to connect to is not valid or accepted on this tool.

- {% endif %} - {% else %} - - - - - - - - - - {% if !p.Check.Closed || p.Check.PeerId > 0 %} - - - - - - - - - {% endif %} - {% if p.Check.BanError != "" %} - - - {% endif %} - {% if p.Check.Error != "" %} - - - {% if p.Check.Error == "disconnected before finishing handshake" %} - - {% endif %} - {% endif %} - {% if p.Check.Tip != nil %} - - - - - - - - - - - - - - - - - - - {% if ((p.YourTipRaw.Main.Coinbase.GenHeight < (p.OurTip.MainHeight-3)) || (p.YourTipRaw.Main.Coinbase.GenHeight > (p.OurTip.MainHeight+3))) %} - - {% endif %} - {% if p.YourTip == nil && ((p.YourTipRaw.Side.Height < (p.OurTip.SideHeight-3)) || (p.YourTipRaw.Side.Height > (p.OurTip.SideHeight+3))) %} - - {% elseif (p.YourTipRaw.Side.Height < (p.OurTip.SideHeight-3)) || (p.YourTipRaw.Side.Height > (p.OurTip.SideHeight+3)) %} - - {% else %} - - {% endif %} - - - {% endif %} - {% endif %} -
Peer Address
{%s p.Check.Address %}
Peer Port
{%dul uint64(p.Check.Port) %}
Listen Port
{% if p.Check.ListenPort > 0 %}{%dul uint64(p.Check.ListenPort) %}{% else %}-{% endif %}
Peer Id
{% if p.Check.PeerId > 0 %}{%dul p.Check.PeerId %}{% else %}-{% endif %}
Connection Direction
{% if p.Check.Incoming %}Incoming{% else %}Outgoing{% endif %}
Connection Status
{% if p.Check.Closed %}Closed{% else %}Active{% endif %}{% if !p.Check.AlreadyConnected %}, new connection{% endif %}{% if p.Check.Banned %} (banned){% endif %}
Peer Software
{%s p.Check.SoftwareId %} {%s p.Check.SoftwareVersion %}
Peer Protocol
{%s p.Check.ProtocolVersion %}
Handshake Complete
{% if p.Check.HandshakeComplete %}Yes{% else %}No{% endif %}
Last Broadcast
{%s time_elapsed_short(p.Check.BroadcastTime) %}
Connection Duration
{%s time_elapsed_short(p.Check.ConnectionTime) %}
Connection Latency
{% if p.Check.Latency > 0 %}{%dul p.Check.Latency %}ms{% else %}-{% endif %}
 
Previous Ban Error: {%s p.Check.BanError %}
 
Error: {%s p.Check.Error %}
This peer is likely running on a different SideChain or is running an incompatible P2Pool software version. Try a different Observer or verify your software.
Alternatively this peer could have banned the Observer node and we cannot connect properly.
 
Peer SideChain Height
{%dul p.YourTipRaw.Side.Height %}
Peer SideChain Id
{%= shorten(p.Context(), p.YourTipRaw.SideTemplateId(p.Context().Consensus), 10) %}
Peer MainChain Height
{%dul p.YourTipRaw.Main.Coinbase.GenHeight %}
Peer Difficulty
{%s si_units(p.YourTipRaw.Side.Difficulty.Lo, 4) %}
Peer Cumulative Difficulty
{%s si_units(p.YourTipRaw.Side.CumulativeDifficulty.Lo, 4) %}
Peer Timestamp
{%s utc_date(p.YourTipRaw.Main.Timestamp) %}
Observer SideChain Height
{%dul p.OurTip.SideHeight %}
Observer SideChain Id
{%= shorten(p.Context(), p.OurTip.TemplateId, 10) %}
Observer MainChain Height
{%dul p.OurTip.MainHeight %}
Observer Difficulty
{%s si_units(p.OurTip.Difficulty, 4) %}
Observer Cumulative Difficulty
{%s si_units(p.OurTip.CumulativeDifficulty.Lo, 4) %}
Observer Timestamp
{%s utc_date(p.OurTip.Timestamp) %}
 
Peer Monero node is on a wildly different Monero height than Observer.
Either peer node is lagging behind or your monerod is not up to sync.
Could not find Peer SideChain Tip on Observer.
Either peer node is lagging behind or you are on a forked SideChain.
The peer is connectable and on the SideChain.
Peer SideChain Tip is on a wildly different SideChain height than Observer.
Either peer node is lagging behind or your p2pool is not up to sync.
The peer is connectable and on the SideChain.
{% if p.OurTip.TemplateId == p.YourTipRaw.SideTemplateId(p.Context().Consensus) %}The peer Tip matches exactly the Observer Tip.{% else %}Verify Peer SideChain Height against Observer SideChain Height, so it's not consistently different.{% endif %}
 
- {%= TemplatePoolBlock(p.Context(), p.YourTipRaw, "Peer Tip Share information") %} -
-
-{% endif %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/context.go b/cmd/web/views/context.go deleted file mode 100644 index e82c4a6..0000000 --- a/cmd/web/views/context.go +++ /dev/null @@ -1,32 +0,0 @@ -package views - -import ( - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" -) - -type GlobalRequestContext struct { - IsOnion bool - DonationAddress string - SiteTitle string - NetServiceAddress string - TorServiceAddress string - Consensus *sidechain.Consensus - Pool *cmdutils.PoolInfoResult - Socials struct { - Irc struct { - Title string - Link string - WebChat string - } - Matrix struct { - Link string - } - } - HexBuffer [types.HashSize * 2]byte -} - -func (ctx *GlobalRequestContext) GetUrl(host string) string { - return cmdutils.GetSiteUrlByHost(host, ctx.IsOnion) -} diff --git a/cmd/web/views/css/style.css b/cmd/web/views/css/style.css deleted file mode 100644 index f88a53b..0000000 --- a/cmd/web/views/css/style.css +++ /dev/null @@ -1,76 +0,0 @@ -body { - margin: 0; - padding: 0; - color: #d1d1d1; - background-color: #4c4c4c; - - font-family: Helvetica, Arial, sans-serif; -} - -h1, h2, h3, h4, h5, h6 { - text-align: inherit; -} - -ul{ - list-style-position: inside; -} - -.datatable tr, li, code.mono { - font-size: 12px; -} - -.content { - margin-top: 15px; - margin-bottom: 30px; -} - -.center { - margin-left: auto; - margin-right: auto; - width: 96%; -} - -.hl-found{ - background: rgba(0%, 75.3%, 0%, 0.5); -} - -.mono { - font-family: "Lucida Console", Monaco, monospace; - white-space: nowrap; - line-break: strict; -} - -a, a:hover, a:visited, a:link, a:active { - text-decoration: underline dotted #333; - color: inherit; -} - -form { - display: inline-block; - text-align: center; -} - -h1 { - color: #ff6600; - font-weight: bold; -} - -h1 a { - text-decoration: none !important; -} - -.small { - font-size: 80%; -} - -.smaller { - font-size: 60%; -} - -code.small { - font-size: 10px; -} - -code.smaller { - font-size: 8px; -} \ No newline at end of file diff --git a/cmd/web/views/error.qtpl b/cmd/web/views/error.qtpl deleted file mode 100644 index 1a5a08e..0000000 --- a/cmd/web/views/error.qtpl +++ /dev/null @@ -1,32 +0,0 @@ -{% import "fmt" %} -{% code -type ErrorPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - - Code int - Message string - Error any -} -%} - -{% func (p *ErrorPage) Title() %} -{%= p.BasePage.Title() %} - Error {%d p.Code %} -{% endfunc %} - -{% func (p *ErrorPage) Content() %} -
-

Error {%d p.Code %} - {%s p.Message %}

- {% if p.Error != nil %} -
{%s fmt.Sprintf("%s", p.Error) %}
- {% endif %} -
-{% endfunc %} - -{% code - -func NewErrorPage(code int, message string, err any) *ErrorPage { - return &ErrorPage{Code: code, Message: message, Error: err} -} - -%} \ No newline at end of file diff --git a/cmd/web/views/footer.qtpl b/cmd/web/views/footer.qtpl deleted file mode 100644 index a0888d8..0000000 --- a/cmd/web/views/footer.qtpl +++ /dev/null @@ -1,15 +0,0 @@ -{% func Footer(ctx *GlobalRequestContext) %} -
- -
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/funcs.go b/cmd/web/views/funcs.go deleted file mode 100644 index 254b9d4..0000000 --- a/cmd/web/views/funcs.go +++ /dev/null @@ -1,184 +0,0 @@ -package views - -import ( - hex2 "encoding/hex" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/mazznoer/colorgrad" - "strconv" - "strings" - "time" -) - -func utc_date[T int64 | uint64 | int | float64](v T) string { - return time.Unix(int64(v), 0).UTC().Format("02-01-2006 15:04:05 MST") -} - -func time_elapsed_short[T int64 | uint64 | int | float64](v T) string { - diff := time.Since(time.Unix(int64(v), 0).UTC()) - - days := int64(diff.Hours() / 24) - hours := int64(diff.Hours()) % 24 - minutes := int64(diff.Minutes()) % 60 - seconds := int64(diff.Seconds()) % 60 - - var result []string - if days > 0 { - result = append(result, strconv.FormatInt(days, 10)+"d") - } - if hours > 0 { - result = append(result, strconv.FormatInt(hours, 10)+"h") - } - if minutes > 0 { - result = append(result, strconv.FormatInt(minutes, 10)+"m") - } - if seconds > 0 { - result = append(result, strconv.FormatInt(seconds, 10)+"s") - } - - if v == 0 { - return "never" - } else if len(result) == 0 { - return "just now" - } else { - return strings.Join(result[0:1], " ") + " ago" - } -} - -func side_block_weight(s *index.SideBlock, tipHeight, windowSize uint64, consensus *sidechain.Consensus) uint64 { - w, _ := s.Weight(tipHeight, windowSize, consensus.UnclePenalty) - return w -} - -func si_units[T int64 | uint64 | int | float64](v T, n ...int) string { - if len(n) > 0 { - return utils.SiUnits(float64(v), n[0]) - } else { - return utils.SiUnits(float64(v), 3) - } -} -func date_diff_short[T int64 | uint64 | int | float64](v T) string { - diff := time.Since(time.Unix(int64(v), 0).UTC()) - s := fmt.Sprintf("%02d:%02d:%02d", int64(diff.Hours())%24, int64(diff.Minutes())%60, int64(diff.Seconds())%60) - - days := int64(diff.Hours() / 24) - if days > 0 { - return strconv.FormatInt(days, 10) + ":" + s - } - - return s -} - -func time_duration_long[T int64 | uint64 | int | float64](v T) string { - diff := time.Second * time.Duration(v) - diff += time.Microsecond * time.Duration((float64(v)-float64(int64(v)))*1000000) - days := int64(diff.Hours() / 24) - hours := int64(diff.Hours()) % 24 - minutes := int64(diff.Minutes()) % 60 - seconds := int64(diff.Seconds()) % 60 - ms := int64(diff.Milliseconds()) % 1000 - - var result []string - if days > 0 { - result = append(result, strconv.FormatInt(days, 10)+"d") - } - if hours > 0 { - result = append(result, strconv.FormatInt(hours, 10)+"h") - } - if minutes > 0 { - result = append(result, strconv.FormatInt(minutes, 10)+"m") - } - if seconds > 0 { - result = append(result, strconv.FormatInt(seconds, 10)+"s") - } - - if len(result) == 0 || (len(result) == 1 && seconds > 0) { - result = append(result, strconv.FormatInt(ms, 10)+"ms") - } - - return strings.Join(result, " ") -} - -func benc(n uint64) string { - return utils.EncodeBinaryNumber(n) -} - -func bencstr(val string) string { - if n, err := strconv.ParseUint(val, 10, 0); err == nil { - return utils.EncodeBinaryNumber(n) - } else { - panic(err) - } -} - -func str(val any) string { - switch t := val.(type) { - case float32: - return strconv.FormatFloat(float64(t), 'f', -1, 64) - case float64: - return strconv.FormatFloat(t, 'f', -1, 64) - case uint32: - return strconv.FormatFloat(float64(t), 'f', -1, 64) - case uint64: - return strconv.FormatFloat(float64(t), 'f', -1, 64) - case int: - return strconv.FormatFloat(float64(t), 'f', -1, 64) - case fmt.Stringer: - return t.String() - default: - return fmt.Sprintf("%v", val) - } -} - -func found_block_effort(b, previous *index.FoundBlock) float64 { - if previous == nil { - return 0 - } - - return float64(b.CumulativeDifficulty.Sub(previous.CumulativeDifficulty).Mul64(100).Lo) / float64(b.MainBlock.Difficulty) -} - -var effortColorGradient = colorgrad.RdYlBu() - -const effortRangeStart = 0.15 -const effortRangeEnd = 0.85 - -func effort_color(effort float64) string { - probability := utils.ProbabilityEffort(effort) - - // rescale - probability *= effortRangeEnd - effortRangeStart - probability += effortRangeStart - - return effortColorGradient.At(1 - probability).Hex() -} - -func monero_to_xmr(v uint64) string { - return utils.XMRUnits(v) -} - -func diff_hashrate(v any, blockTime uint64) uint64 { - if strVal, ok := v.(string); ok { - if d, err := types.DifficultyFromString(strVal); err == nil { - return d.Div64(blockTime).Lo - } - } 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 -} - -func coinbase_extra(b *sidechain.PoolBlock) string { - coinbaseExtra := b.Main.Coinbase.Extra - var result []string - for _, t := range coinbaseExtra { - buf, _ := t.MarshalBinary() - result = append(result, hex2.EncodeToString(buf)) - } - return strings.Join(result, " ") -} diff --git a/cmd/web/views/funcs.qtpl b/cmd/web/views/funcs.qtpl deleted file mode 100644 index 421913f..0000000 --- a/cmd/web/views/funcs.qtpl +++ /dev/null @@ -1,180 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/types" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/utils" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" %} - -{% import "encoding/binary" %} -{% import "fmt" %} -{% import "slices" %} -{% 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) %} - {% case string %} - {%s s %} - {% case types.Difficulty %} - {% code - fasthex.Encode(ctx.HexBuffer[:types.DifficultySize*2], s.Bytes()) - %} - {%z= ctx.HexBuffer[:types.DifficultySize*2] %} - {% case crypto.PrivateKeyBytes %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= ctx.HexBuffer[:] %} - {% case crypto.PublicKeyBytes %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= ctx.HexBuffer[:] %} - {% case crypto.PrivateKey %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s.AsSlice()) - %} - {%z= ctx.HexBuffer[:] %} - {% case crypto.PublicKey %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s.AsSlice()) - %} - {%z= ctx.HexBuffer[:] %} - {% case types.Hash %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= ctx.HexBuffer[:] %} - {% case []byte %} - {%s= hex2.EncodeToString(s) %} - {% case uint32 %} - {% code - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], s) - fasthex.Encode(ctx.HexBuffer[:4*2], buf[:]) - %} - {%z= ctx.HexBuffer[:4*2] %} - {% case uint64 %} - {% code - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], s) - fasthex.Encode(ctx.HexBuffer[:8*2], buf[:]) - %} - {%z= ctx.HexBuffer[:8*2] %} - {% default %} - {%v val %} - {% endswitch %} -{% endfunc %} - -{% func shorten(ctx *GlobalRequestContext, val any, n int) %} - {% switch s := val.(type) %} - {% case string %} - {%s utils.Shorten(s, n) %} - {% case []byte %} - {%z= utils.ShortenSlice(slices.Clone(s), n) %} - {% case crypto.PrivateKeyBytes %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= utils.ShortenSlice(ctx.HexBuffer[:], n) %} - {% case crypto.PublicKeyBytes %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= utils.ShortenSlice(ctx.HexBuffer[:], n) %} - {% case crypto.PrivateKey %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s.AsSlice()) - %} - {%z= utils.ShortenSlice(ctx.HexBuffer[:], n) %} - {% case crypto.PublicKey %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s.AsSlice()) - %} - {%z= utils.ShortenSlice(ctx.HexBuffer[:], n) %} - {% case types.Hash %} - {% code - fasthex.Encode(ctx.HexBuffer[:], s[:]) - %} - {%z= utils.ShortenSlice(ctx.HexBuffer[:], n) %} - {% case fmt.Stringer %} - {%s utils.Shorten(s.String(), n) %} - {% default %} - {%s utils.Shorten(fmt.Sprintf("%v", val), n) %} - {% endswitch %} -{% endfunc %} - - - -{% func henc(val any) %} - {% code - var buf [types.HashSize*2+1]byte - %} - {% switch s := val.(type) %} - {% case types.Hash %} - {% code - dst := utils.EncodeSliceBinaryNumber(buf[:], s[:]) - %} - {%z= dst[:] %} - {% case crypto.PrivateKeyBytes %} - {% code - dst := utils.EncodeSliceBinaryNumber(buf[:], s[:]) - %} - {%z= dst[:] %} - {% case string %} - {%s= utils.EncodeHexBinaryNumber(s) %} - {% case fmt.Stringer %} - {%s= utils.EncodeHexBinaryNumber(s.String()) %} - {% default %} - panic("type not allowed") - {% endswitch %} -{% endfunc %} - -{% func software_info(softwareId p2pooltypes.SoftwareId, softwareVersion p2pooltypes.SoftwareVersion) %} - {% if softwareId == 0 && softwareVersion == 0 %} - Not present - {% else %} - {%s= softwareId.String() %}{%s= ` ` %}{%s= softwareVersion.String() %} - {% endif %} -{% endfunc %} - -{% func side_block_valuation(b any, consensus *sidechain.Consensus) %} - {% switch block := b.(type) %} - {% case *index.SideBlock %} - {% if block.IsOrphan() %} - 0% - {% elseif block.IsUncle() %} - {%dul 100-consensus.UnclePenalty %}%{%s= ` ` %}(uncle) - {% elseif len(block.Uncles) > 0 %} - 100%{%s= ` ` %}+{%s= ` ` %}{%dul consensus.UnclePenalty %}%{%s= ` ` %}of{%s= ` ` %}{%d len(block.Uncles) %}{%s= ` ` %}uncle(s) - {% else %} - 100% - {% endif %} - {% case *sidechain.PoolBlock %} - {% if len(block.Side.Uncles) > 0 %} - 100%{%s= ` ` %}+{%s= ` ` %}{%dul consensus.UnclePenalty %}%{%s= ` ` %}of{%s= ` ` %}{%d len(block.Side.Uncles) %}{%s= ` ` %}uncle(s) - {% else %} - 100% - {% endif %} - {% default %} - panic("type not allowed") - {% endswitch %} -{% endfunc %} - -{% endstripspace %} \ No newline at end of file diff --git a/cmd/web/views/header.qtpl b/cmd/web/views/header.qtpl deleted file mode 100644 index ea689cf..0000000 --- a/cmd/web/views/header.qtpl +++ /dev/null @@ -1,46 +0,0 @@ -{% func Header(ctx *GlobalRequestContext) %} -
-
-

{% if ctx.SiteTitle == "" %}Monero P2Pool Observer{% else %}Monero {%s ctx.SiteTitle %}{% endif %}

-

- [Gupax] P2Pool plug and play Mining GUI, by hinto-janai - :: - P2Pool Setup Help - :: - P2Pool FAQ - :: - What is P2Pool? - :: - Observer API Documentation -

- {% if ctx.Socials.Irc.Link != "" %} -

- Join chat to talk, send feedback, ask questions and be notified of changes on IRC on {%s ctx.Socials.Irc.Title %}{% if ctx.Socials.Irc.WebChat != "" %}, on WebIRC{% endif %}{% if ctx.Socials.Matrix.Link != "" %}, on Matrix{% endif %} -

- {% endif %} -

- How to join P2Pool - :: - Guide on how to run a Monero and P2Pool node by Seth - :: - P2Pool Bonus Hash Rate Raffle by XMRvsBEAST -

- {% if pool := ctx.Pool; pool != nil %} - - {% 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 %} -

- {% else %} -

- {% endif %} - Latest releases: P2Pool {%s pool.Versions.P2Pool.Version %} {%f.1 float64((pool.SideChain.LastBlock.Timestamp - uint64(pool.Versions.P2Pool.Timestamp))) / (3600*24) %} day(s) ago - :: - Monero {%s pool.Versions.Monero.Version %} {%f.1 float64((pool.SideChain.LastBlock.Timestamp - uint64(pool.Versions.Monero.Timestamp))) / (3600*24) %} day(s) ago -

- - {% endif %} - - - -
-
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/index.qtpl b/cmd/web/views/index.qtpl deleted file mode 100644 index cc08b9a..0000000 --- a/cmd/web/views/index.qtpl +++ /dev/null @@ -1,137 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} - -{% code -type IndexPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - Positions struct { - BlocksFound *cmdutils.PositionChart - } - - Shares []*index.SideBlock - FoundBlocks []*index.FoundBlock -} -%} - -{% func (p *IndexPage) Content() %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
- - -
-

P2Pool statistics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if p.Context().Pool.SideChain.LastFound != nil %} - - {% else %} - - {% endif %} - - - - - - - - - - - - - - -
P2Pool HeightP2Pool HashrateMonero HeightMonero Hashrate
{%dul p.Context().Pool.SideChain.LastBlock.SideHeight %}{%s si_units(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime), 2) %}H/s{%dul p.Context().Pool.MainChain.Height %}{%s si_units(diff_hashrate(p.Context().Pool.MainChain.Difficulty, uint64(p.Context().Pool.MainChain.Consensus.BlockTime)), 2) %}H/s
 
P2Pool DifficultyP2Pool Monero %Monero DifficultyBlocks Found
{%s si_units(p.Context().Pool.SideChain.LastBlock.Timestamp, 2) %}{%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 %}%{%s si_units(p.Context().Pool.MainChain.Difficulty.Lo, 2) %}{%dul p.Context().Pool.SideChain.Found %}
 
Miners KnownAverage EffortBlock Found FrequencyLast Found Block
{%dul p.Context().Pool.SideChain.Miners %} - {%f.2 p.Context().Pool.SideChain.Effort.Average10 %}% - / - {%f.2 p.Context().Pool.SideChain.Effort.Average50 %}% - / - {%f.2 p.Context().Pool.SideChain.Effort.Average200 %}% - {%s time_duration_long(float64(p.Context().Pool.MainChain.Difficulty.Lo) / float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime))) %}{%s time_elapsed_short(p.Context().Pool.SideChain.LastFound.MainBlock.Timestamp) %}-
 
Window MinersCurrent EffortWindow BlocksLast Share
{%d p.Context().Pool.SideChain.Window.Miners %} - {%f.2 p.Context().Pool.SideChain.Effort.Current %}% - {%d p.Context().Pool.SideChain.Window.Blocks %} blocks (+{%d p.Context().Pool.SideChain.Window.Uncles %} uncles){%s time_elapsed_short(p.Context().Pool.SideChain.LastBlock.Timestamp) %}
-
- -
-
-

Lookup miner statistics

-
-
- -
-
- -
-
-

- [Average share time calculator] :: [Connectivity Check] -

-

- [Current Window Miners] :: [Weekly Miners] -

-

- [Sweep Transaction Lookup] :: [Recent Likely Sweep Transactions] -

-
- -
- -
-

Recent Monero blocks found by P2Pool miners

- - {%= TemplateFoundBlocks(p.Context(), p.FoundBlocks, false) %} -
[show more found blocks]
- -

Blocks found during last day

- {%s p.Positions.BlocksFound.String() %} -
- -
- -
-

Recent P2Pool shares found

- {%= TemplateShares(p.Context(), p.Shares, false, nil) %} -
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/miner-options.qtpl b/cmd/web/views/miner-options.qtpl deleted file mode 100644 index 4a1e88e..0000000 --- a/cmd/web/views/miner-options.qtpl +++ /dev/null @@ -1,183 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} - -{% code -type MinerOptionsPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Miner *cmdutils.MinerInfoResult - SignedAction *cmdutils.SignedAction - WebHooks []*index.MinerWebHook -} -%} - -{% func (p *MinerOptionsPage) Title() %} -{%= p.BasePage.Title() %} - Miner Options {%z= p.Miner.Address.ToBase58() %} -{% endfunc %} - -{% func (p *MinerOptionsPage) Content() %} - -
-

Miner Options

-

Payout Address: {%z= p.Miner.Address.ToBase58() %}

- - {% if p.Miner.Alias != "" %} -

Miner Alias: {%s p.Miner.Alias %}

-

Miner Alias is user content and not verified. This value should only be used for vanity purposes.

- {% endif %} -
- -{% if p.SignedAction != nil %} - -
- -
-

Sign and submit changes

-

To submit any changes you will need to sign a message using your wallet to prove ownership.

-

On the Monero GUI, go to Advanced -> Sign/verify -> Select Mode "Message".

-

Enter the Message listed below in the "Message" field, then press Sign Message. Copy the Signature and paste it on the field below.

-

Do not modify the message, add spaces or add any new lines.

-

Signatures generated by a View-Only wallet are not supported. Monero GUI does not generate valid signatures for hardware wallets.

-
-
- -
-
-
- -
-
- -
-
-
-{% else %} - -
-

Miner Alias

- -
-
-

Set Miner Alias

-

- Miner Alias must be 4 to 20 characters long, and only 0-9, a-z, A-Z, and _-. are allowed, and cannot start with a number or symbol. No spaces are allowed either. -
- Alias are unique for the observer instance, and can be removed or changed after adding one. -

-
-
- -
-
- -
-
-
-
- {% if p.Miner.Alias != "" %} -
-

Unset Miner Alias

-

You can remove your miner alias below if you don't want one set.

-
- -
-
- {% endif %} -
-
- - -
-

Notification WebHooks

- -
-
-

Add WebHook

-

- Configure notification URLs that can be used to post messages or other actions in other platforms or services. -
- Only one URL of each type is allowed. To adjust an existing hook settings you will need to add it with the new preference. -

-
-
- -
-
-
-
- -
-
-
- WebHook Events -
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- -
-
- {% if len(p.WebHooks) > 0 %} - -

Current WebHooks

- - - - - - - - - - - - - {% for _, w := range p.WebHooks %} - - - - - - - - - - - {% endfor %} -
TypeURL HashSharesPayoutsFound BlocksOrphaned BlocksOther
{%s string(w.Type) %}{%s w.Url %}{% if cmdutils.CanSendToHook(w, "side_blocks") %}Yes{% else %}No{% endif %}{% if cmdutils.CanSendToHook(w, "payouts") %}Yes{% else %}No{% endif %}{% if cmdutils.CanSendToHook(w, "found_blocks") %}Yes{% else %}No{% endif %}{% if cmdutils.CanSendToHook(w, "orphaned_blocks") %}Yes{% else %}No{% endif %}{% if cmdutils.CanSendToHook(w, "other") %}Yes{% else %}No{% endif %}[Remove]
- {% endif %} -
-
-{% endif %} - -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/miner.qtpl b/cmd/web/views/miner.qtpl deleted file mode 100644 index fb13275..0000000 --- a/cmd/web/views/miner.qtpl +++ /dev/null @@ -1,277 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} - -{% import p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} - -{% code -type MinerPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - Positions struct { - Resolution int - ResolutionWindow int - SeparatorIndex int - Blocks *cmdutils.PositionChart - Uncles *cmdutils.PositionChart - BlocksInWindow *cmdutils.PositionChart - UnclesInWindow *cmdutils.PositionChart - Payouts *cmdutils.PositionChart - } - ExpectedRewardPerWindow uint64 - ExpectedRewardPerDay uint64 - WindowWeight uint64 - Weight uint64 - Miner *cmdutils.MinerInfoResult - LastPoolBlock *sidechain.PoolBlock - LastShares []*index.SideBlock - LastSharesEfforts []float64 - LastOrphanedShares []*index.SideBlock - LastFound []*index.FoundBlock - LastPayouts []*index.Payout - LastSweeps <-chan *index.MainLikelySweepTransaction - - HashrateSubmit bool - HashrateLocal float64 - MagnitudeLocal float64 -} -%} - -{% func (p *MinerPage) Title() %} -{%= p.BasePage.Title() %} - Miner {%z= p.Miner.Address.ToBase58() %} -{% endfunc %} - -{% func (p *MinerPage) Content() %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
- - - - -
- {% if p.Miner.LastShareTimestamp == 0 %} -
-

No shares have been reported to this P2Pool network in the past for this miner.

- -

Finding shares is a random process based on your hashrate vs this P2Pool hashrate. This can take several days for low hashrate miners, and depending on your luck.

-

Please use the calculator below to find your average share time (with average luck).

-

You can also verify you are reachable by opening ports with the tool below.

-
- -
-

Average Share Time Calculator

-
- - -
- -
- - -
-

Connectivity Check

-
-
- -
-
-
- -
-
-
- -
- {% endif %} - - {% if p.LastPoolBlock != nil && p.LastPoolBlock.ShareVersion() < sidechain.ShareVersion_V2 && p.LastPoolBlock.ShareVersionSignaling() != sidechain.ShareVersion_V2 %} -
-

Recent shares indicate you are running an outdated version of P2Pool

- -

A new version of P2Pool (v3.0+) has been released with several improvements, which requires a consensus change.

-

P2Pool (not Monero!) has hardforked to new consensus rules on March 18th, 2023 at 21:00 UTC. All versions before P2Pool v3.0 are incompatible. P2Pool v3.3+ is recommended.

-

If you keep using previous versions, you will keep mining as usual, but become almost a solo miner, as incompatible clients will mine on their own.

-

{% if p.Context().NetServiceAddress == "p2pool.observer" %}After the fork, you can check on OLD.P2Pool.Observer.{% elseif p.Context().NetServiceAddress == "mini.p2pool.observer" %}After the fork, you can check on OLD-MINI.P2Pool.Observer.{% else %}Please check on an observer tracking the old chain.{% endif %}

-

After upgrading to a supported P2Pool version and mining a share, this message will be dismissed.

-
-
- -
- {% elseif p.LastPoolBlock != nil && p.LastPoolBlock.ShareVersion() > sidechain.ShareVersion_V1 && p.LastPoolBlock.Side.ExtraBuffer.SoftwareId == p2pooltypes.SoftwareIdP2Pool && p.LastPoolBlock.Side.ExtraBuffer.SoftwareVersion.String() != p.Context().Pool.Versions.P2Pool.ShortVersion().String() %} -
-

Recent shares indicate you are running an older version of P2Pool

- -

P2Pool {%s p.Context().Pool.Versions.P2Pool.Version %} has been released.

-

Your most recent share indicates are currently running {%= software_info(p.LastPoolBlock.Side.ExtraBuffer.SoftwareId, p.LastPoolBlock.Side.ExtraBuffer.SoftwareVersion) %}. It is recommended to upgrade.

-

After upgrading to this P2Pool version and mining a share, this message will be dismissed.

-
-
- -
- {% endif %} - -

Payout Address: {%z= p.Miner.Address.ToBase58() %}

- - {% if p.Miner.Alias != "" %} -

Miner Alias: {%s p.Miner.Alias %}

-

Miner Alias is user content and not verified. This value should only be used for vanity purposes.

- {% endif %} - -

[Change Miner Alias] :: [Configure WebHook notifications]

- - - - - - - - - - - - - {% code windowWeightRatio := float64(p.WindowWeight) / float64(p.Context().Pool.SideChain.Window.Weight.Lo) %} - - - - - - - - - - - - - - - - {% 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)))) %} - - - - -
Last ShareCurrent SharesEstimated HashratePool Share %Estimated Window Reward
{%s time_elapsed_short(p.Miner.LastShareTimestamp) %}{%dul p.Positions.BlocksInWindow.Total() %} blocks (+{%dul p.Positions.UnclesInWindow.Total() %} uncles){%s si_units(windowWeightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s{%f.3 windowWeightRatio*100 %}%{%s monero_to_xmr(p.ExpectedRewardPerWindow) %} XMR
 
Estimated Total SharesDay SharesDay HashrateDay Share %Estimated Daily Reward
{% if p.LastPoolBlock != nil %}Around {%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 %}{%dul p.Positions.Blocks.Total() %} blocks (+{%dul p.Positions.Uncles.Total() %} uncles){%s si_units(weightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s{%f.3 weightRatio*100 %}%{%s monero_to_xmr(p.ExpectedRewardPerDay) %} XMR
-
- - -
- -
-

Share positions

-

- Shares appear on the right, and get older towards the left. -
- Number denotes the amount of shares per slice, with the plus (+) character being more than 9, and dot (.) being none. -
- Each slice accounts for {%d p.Positions.Resolution %} P2Pool blocks, or around {%dul (uint64(p.Positions.Resolution) * p.Context().Consensus.TargetBlockTime) / 60 %} minutes. -

-

Shares and uncles in PPLNS window

-

- Each slice accounts for {%d p.Positions.ResolutionWindow %} P2Pool block heights, or around {%dul (uint64(p.Positions.ResolutionWindow) * p.Context().Consensus.TargetBlockTime) / 60 %} minutes. -
- Shares within the PPLNS window will be weighted towards receiving a payout when any Monero block is found by any P2Pool miner. -

- {%s p.Positions.BlocksInWindow.String() %} -
- {%s p.Positions.UnclesInWindow.String() %} - -

Shares and uncles during last day

-

- Each slice accounts for {%d p.Positions.Resolution %} P2Pool block heights, or around {%dul (uint64(p.Positions.Resolution) * p.Context().Consensus.TargetBlockTime) / 60 %} minutes. -
- The pipe (|) character denotes roughly the PPLNS window end. -

- {%s p.Positions.Blocks.StringWithSeparator(p.Positions.SeparatorIndex) %} -
- {%s p.Positions.Uncles.StringWithSeparator(p.Positions.SeparatorIndex) %} - - {% if p.Positions.Payouts.Total() > 0 %} -

Payouts during last day

- {%s p.Positions.Payouts.StringWithSeparator(p.Positions.SeparatorIndex) %} - {% endif %} - -
- -
-
- -
- -
-

Most recent payouts

- {%= TemplatePayoutsSlice(p.Context(), p.LastPayouts) %} -
[show all historical payouts]
-
- -
- -
-

Most recent shares

- {% if p.HashrateSubmit || (p.Positions.Blocks.Total() + p.Positions.Uncles.Total()) > 2 %} - {%= TemplateShares(p.Context(), p.LastShares, true, &p.LastSharesEfforts) %} - {% else %} - {%= TemplateShares(p.Context(), p.LastShares, true, nil) %} - {% endif %} -
- -
- -{% if len(p.LastOrphanedShares) > 0 %} -
-

Most recent orphaned shares

- {%= TemplateShares(p.Context(), p.LastOrphanedShares, true, nil) %} -
- -
-{% endif %} - -
-

Most recent Monero blocks found

- {%= TemplateFoundBlocks(p.Context(), p.LastFound, true) %} -
[show more found blocks]
-
- -
- -
-

Most recent likely sweeps

- {%= TemplateSweeps(p.Context(), p.LastSweeps, true) %} -
[show more likely sweeps]
-
- - - -{% if p.Miner.LastShareTimestamp != 0 %} -
- -
-
-

Effort Calculation

-

Local hashrate of each P2Pool miner is not known by the network. A guess is calculated based on daily estimation. If you provide a value here, it will be more accurate for effort calculation.

-

This data will not be saved.

-
- - -
- -
-
-{% endif %} -{% endfunc %} diff --git a/cmd/web/views/miners.qtpl b/cmd/web/views/miners.qtpl deleted file mode 100644 index a0186a6..0000000 --- a/cmd/web/views/miners.qtpl +++ /dev/null @@ -1,100 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} -{% import types "git.gammaspectra.live/P2Pool/p2pool-observer/types" %} -{% import p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" %} - -{% code -type MinersPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - Weekly bool - Miners []*MinersPageMinerEntry - WindowWeight types.Difficulty -} -%} - -{% code -type MinersPageMinerEntry struct { - Address *address.Address - Alias string - SoftwareId p2pooltypes.SoftwareId - SoftwareVersion p2pooltypes.SoftwareVersion - Weight types.Difficulty - Shares *cmdutils.PositionChart - Uncles *cmdutils.PositionChart -} -%} - -{% func (p *MinersPage) Title() %} -{% if p.Weekly %} -{%= p.BasePage.Title() %} - Current Window Miners -{% else %} -{%= p.BasePage.Title() %} - Weekly Miners -{% endif %} -{% endfunc %} - -{% func (p *MinersPage) Content() %} -{% if p.Weekly %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
-{% else %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
-{% endif %} - - -
- -{% if p.Weekly %} -

Weekly Miners

-

This is a list of the miners that have shares in the last 28 full windows, or about seven days.

-

Entries are sorted by current window "weight". There are more total miners currently active, but without a share to show at the moment.

-

Pool share % is relative to whole pool hashrate. Miners can join or leave anytime and there is no ceiling limit.

- -{% else %} -

Current Window Miners

-

This is a list of the miners that have shares in the current window, and would be rewarded when a Monero block is found within the window.

-

Entries are sorted by current window "weight". There are more total miners currently active, but without a share to show at the moment.

-

Pool share % is relative to whole pool hashrate. Miners can join or leave anytime and there is no ceiling limit.

-
-{% endif %} - - - - - - - - - - {% for i, m := range p.Miners %} - - - {%= TemplateRowMinerWithTag(p.Context(), m.Address, m.Alias, "th") %} - - {% code minerRatio := float64(m.Weight.Lo) / float64(p.WindowWeight.Lo) %} - - - - - - {% endfor %} -
#MinerSoftwarePool share %Estimated HashrateShares foundShares position
{%d i+1 %}{%= software_info(m.SoftwareId, m.SoftwareVersion) %}{%f.3 minerRatio*100 %}%{%s si_units(minerRatio * (float64(p.Context().Pool.SideChain.LastBlock.Difficulty) / float64(p.Context().Consensus.TargetBlockTime)), 3) %}H/s{%dul m.Shares.Total() %} block(s) +{%dul m.Uncles.Total() %} uncle(s) - {%s m.Shares.String() %} -
- {%s m.Uncles.String() %} -
-
- -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/payouts.qtpl b/cmd/web/views/payouts.qtpl deleted file mode 100644 index 0155003..0000000 --- a/cmd/web/views/payouts.qtpl +++ /dev/null @@ -1,40 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} - -{% code -type PayoutsPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - Miner *address.Address - Payouts <-chan *index.Payout -} -%} - -{% func (p *PayoutsPage) Title() %} -{%= p.BasePage.Title() %} - Historical miner payouts {%z= p.Miner.ToBase58() %} -{% endfunc %} - -{% func (p *PayoutsPage) Content() %} - - -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
- -
-

Historical miner payouts

-

Payout Address: {%z= p.Miner.ToBase58() %}

- - {% code var total uint64 %} - - {%= TemplatePayouts(p.Context(), p.Payouts, &total) %} - -

Estimated total: {%s monero_to_xmr(total) %} XMR

-
- -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/proof.qtpl b/cmd/web/views/proof.qtpl deleted file mode 100644 index 717e805..0000000 --- a/cmd/web/views/proof.qtpl +++ /dev/null @@ -1,52 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} - -{% code -type ProofPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Output *index.MainCoinbaseOutput - Block *index.SideBlock - Raw *sidechain.PoolBlock -} -%} - -{% func (p *ProofPage) Title() %} -{%= p.BasePage.Title() %} - Payout Proof for output #{%dul uint64(p.Output.Index) %} on Monero block {%dul p.Block.MainHeight %} -{% endfunc %} - -{% func (p *ProofPage) Content() %} -
-

Payout Proof for output #{%dul uint64(p.Output.Index) %} on Monero block {%dul p.Block.MainHeight %}

- {% code encodedMinerAddress := p.Output.MinerAddress.ToBase58() %} - {% if p.Output.MinerAlias != "" %} -

Payout Address: {%s p.Output.MinerAlias %} ({%z= encodedMinerAddress %})

- {% else %} -

Payout Address: {%z= encodedMinerAddress %}

- {% endif %} - -

Received {%s monero_to_xmr(p.Output.Value) %} XMR on transaction id {%= hex(p.Context(), p.Output.Id) %} (output index #{%dul uint64(p.Output.Index) %}, global output index #{%dul p.Output.GlobalOutputIndex %}).

-

{%dul p.Context().Pool.MainChain.Height - p.Block.MainHeight + 1 %} confirmation(s). Coinbase outputs will unlock after 60 confirmations.

- -

Stealth Address: {%s address.GetEphemeralPublicKey(p.Output.MinerAddress, &p.Raw.Side.CoinbasePrivateKey, uint64(p.Output.Index)).String() %}

-
-

Payment Proofs

-
-

Transaction Private Key: {%= hex(p.Context(), p.Raw.Side.CoinbasePrivateKey) %}

-

Verify on Monero CLI: check_tx_proof {%= hex(p.Context(), p.Output.Id) %} {%z= encodedMinerAddress %} {%= hex(p.Context(), p.Raw.Side.CoinbasePrivateKey) %}

-

- Verify on LocalMonero
- Verify on Explore Monero
- Verify on Monero.com
-

-
- - -
-

OutProofV2: {%= hex(p.Context(), address.GetTxProofV2(p.Output.MinerAddress, p.Output.Id, &p.Raw.Side.CoinbasePrivateKey, "")) %}

-

OutProofV1: {%= hex(p.Context(), address.GetTxProofV1(p.Output.MinerAddress, p.Output.Id, &p.Raw.Side.CoinbasePrivateKey, "")) %}

-

Verify on Monero GUI (Advanced -> Prove/check -> Check Transaction)

-
-
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/share.qtpl b/cmd/web/views/share.qtpl deleted file mode 100644 index 6a529be..0000000 --- a/cmd/web/views/share.qtpl +++ /dev/null @@ -1,24 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} - -{% code -type SharePage struct { - // inherit from base page, so its' title is used in error page. - BasePage - - Block *index.SideBlock - PoolBlock *sidechain.PoolBlock - Payouts <-chan *index.Payout - CoinbaseOutputs index.MainCoinbaseOutputs - SweepsCount int - Sweeps [][]*index.MainLikelySweepTransaction -} -%} - -{% func (p *SharePage) Title() %} -{%= p.BasePage.Title() %} - Share Height {%dul p.Block.SideHeight %} -{% endfunc %} - -{% func (p *SharePage) Content() %} -{%= TemplatePoolBlockWithSideBlock(p.Context(), p.PoolBlock, "Share information", true, p.Block, p.Payouts, p.CoinbaseOutputs, p.SweepsCount, p.Sweeps) %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/sweeps.qtpl b/cmd/web/views/sweeps.qtpl deleted file mode 100644 index f3dd60c..0000000 --- a/cmd/web/views/sweeps.qtpl +++ /dev/null @@ -1,45 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} - -{% code -type SweepsPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - Refresh int - Sweeps <-chan *index.MainLikelySweepTransaction - Miner *address.Address -} -%} - -{% func (p *SweepsPage) Title() %} -{% if p.Miner == nil %} -{%= p.BasePage.Title() %} - Recent Likely Sweeps -{% else %} -{%= p.BasePage.Title() %} - Recent Likely Sweeps by {%z= p.Miner.ToBase58() %} -{% endif %} -{% endfunc %} - -{% func (p *SweepsPage) Content() %} -{% if p.Miner == nil %} -
- {% if p.Refresh > 0 %} - Autorefresh is ON ({%d p.Refresh %} s) - {% else %} - Autorefresh is OFF - {% endif %} -
-{%endif %} - -
- {% if p.Miner == nil %} -

Recent Likely Sweeps

- {% else %} -

Recent Likely Sweeps by {%z= p.Miner.ToBase58() %}

- {% endif %} -

- [Sweep Transaction Lookup] -

- {%= TemplateSweeps(p.Context(), p.Sweeps, p.Miner != nil) %} -
- -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_found_blocks.qtpl b/cmd/web/views/tpl_found_blocks.qtpl deleted file mode 100644 index 889532c..0000000 --- a/cmd/web/views/tpl_found_blocks.qtpl +++ /dev/null @@ -1,57 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/types" %} -{% import "slices" %} - -{% func TemplateFoundBlocks(ctx *GlobalRequestContext, foundBlocks []*index.FoundBlock, isMiner bool) %} - - - - - - {% if !isMiner %} - - - {% endif %} - - - - - - - {% for i, b := range foundBlocks %} - - - {% if b.UncleOf != types.ZeroHash %} - - {% else %} - - {% endif %} - - {% if !isMiner %} - {% if len(foundBlocks) > (i+1) %} - - {% elseif effortIndex := slices.IndexFunc(ctx.Pool.SideChain.Effort.Last, func(e cmdutils.PoolInfoResultSideChainEffortLastEntry) bool { return e.Id == b.MainBlock.Id }); effortIndex != -1 %} - - {% else %} - - {% endif %} - {%= TemplateRowMiner(ctx, b.MinerAddress, b.MinerAlias) %} - {% endif %} - - - - - - - {% endfor %} -
Monero HeightP2Pool HeightAge [h:m:s]EffortFound byTransactionsTotal RewardOutputsCoinbase TransactionCoinbase Tx Private Key
{%dul b.MainBlock.Height %} - {%dul b.SideHeight %}* - - {%dul b.SideHeight %} - {%s date_diff_short(b.MainBlock.Timestamp) %} - {%f.2 found_block_effort(b, foundBlocks[i+1]) %}% - - {%f.2 ctx.Pool.SideChain.Effort.Last[effortIndex].Effort %}% - unknown{%dul uint64(b.TransactionCount) %}{%s monero_to_xmr(b.MainBlock.Reward) %} XMR{%dul uint64(b.WindowOutputs) %}{%= shorten(ctx, b.MainBlock.CoinbaseId, 10) %}{%= hex(ctx, b.MainBlock.CoinbasePrivateKey) %}
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_payouts.qtpl b/cmd/web/views/tpl_payouts.qtpl deleted file mode 100644 index 5f58746..0000000 --- a/cmd/web/views/tpl_payouts.qtpl +++ /dev/null @@ -1,52 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} - -{% func TemplatePayouts(ctx *GlobalRequestContext, payouts <-chan *index.Payout, total *uint64) %} - - - - - - - - - - - {% for p := range payouts %} - {% code *total = *total + p.Reward %} - - - - - - - - - - {% endfor %} -
Monero HeightP2Pool HeightAge [h:m:s]RewardGlobal Output IndexCoinbase TransactionPayout Proof
{%dul p.MainHeight %}{%dul p.SideHeight %}{%s date_diff_short(p.Timestamp) %}{%s monero_to_xmr(p.Reward) %} XMR{%dul p.GlobalOutputIndex %}{%= shorten(ctx, p.CoinbaseId, 10) %}[Payout Proof #{%dul uint64(p.Index) %}]
-{% endfunc %} - -{% func TemplatePayoutsSlice(ctx *GlobalRequestContext, payouts []*index.Payout) %} - - - - - - - - - - - {% for _, p := range payouts %} - - - - - - - - - - {% endfor %} -
Monero HeightP2Pool HeightAge [h:m:s]RewardGlobal Output IndexCoinbase TransactionPayout Proof
{%dul p.MainHeight %}{%dul p.SideHeight %}{%s date_diff_short(p.Timestamp) %}{%s monero_to_xmr(p.Reward) %} XMR{%dul p.GlobalOutputIndex %}{%= shorten(ctx, p.CoinbaseId, 10) %}[Payout Proof #{%dul uint64(p.Index) %}]
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_poolblock.qtpl b/cmd/web/views/tpl_poolblock.qtpl deleted file mode 100644 index 7db39dd..0000000 --- a/cmd/web/views/tpl_poolblock.qtpl +++ /dev/null @@ -1,238 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/types" %} - -{% func TemplatePoolBlock(ctx *GlobalRequestContext, poolBlock *sidechain.PoolBlock, header string) %} -{%= TemplatePoolBlockWithSideBlock(ctx, poolBlock, header, false, nil, nil, nil, 0, nil) %} -{% endfunc %} - -{% func TemplatePoolBlockWithSideBlock(ctx *GlobalRequestContext, poolBlock *sidechain.PoolBlock, header string, navigation bool, sideBlock *index.SideBlock, payouts <-chan *index.Payout, coinbaseOutputs index.MainCoinbaseOutputs, sweepsCount int, sweeps [][]*index.MainLikelySweepTransaction) %} -
- {% if sideBlock != nil %} - {% if sideBlock.MinedMainAtHeight %} -

This share mined Monero block at height {%dul sideBlock.MainHeight %}

- {% elseif (sideBlock.MainDifficulty > 0) && (sideBlock.PowDifficulty >= sideBlock.MainDifficulty) && !sideBlock.MinedMainAtHeight %} -

This share mined Monero block at height {%dul sideBlock.MainHeight %}, but was orphaned

- {% endif %} - {% if sideBlock.IsOrphan() %} -

This share has been orphaned

- {% endif %} -
- {% endif %} - - - {% if navigation && sideBlock != nil %} - - {% if sideBlock.SideHeight == 0 %} - - {% elseif sideBlock.IsUncle() %} - - {% else %} - - {% endif %} - {% if sideBlock.IsOrphan() %} - - {% elseif sideBlock.IsUncle() %} - - {% else %} - - {% endif %} - - {% endif %} - - - - - - - - - - {% if sideBlock != nil %} - - {% else %} - - {% endif %} - - {% if sideBlock != nil %} - {% if sideBlock.MinedMainAtHeight %} - - - {% else %} - - - {% endif %} - {% else %} - - - {% endif %} - - - - - - - - - - - {% if sideBlock != nil %} - - {%= TemplateRowMiner(ctx, sideBlock.MinerAddress, sideBlock.MinerAlias) %} - - {% else %} - - {%= TemplateRowMiner(ctx, poolBlock.GetAddress().Reference().ToAddress(ctx.Consensus.NetworkType.AddressNetwork()), "") %} - - {% endif %} - - - - - - - - - - {% if sideBlock != nil %} - - {% else %} - - {% endif %} - {% if sideBlock != nil && sideBlock.MinedMainAtHeight %} - - {% else %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Genesis Block<< Parent share<< Previous shareOrphans do not have next blocksUncles do not have next blocksNext share >>

{%s header %}

P2Pool HeightP2Pool Template IdMonero HeightMonero Id
{%dul poolBlock.Side.Height %}{%= hex(ctx, sideBlock.TemplateId) %}{%= hex(ctx, poolBlock.SideTemplateId(ctx.Consensus)) %}{%dul sideBlock.MainHeight %}{%= hex(ctx, sideBlock.MainId) %}{%dul sideBlock.MainHeight %}{%= hex(ctx, sideBlock.MainId) %}{%dul poolBlock.Main.Coinbase.GenHeight %}{%= hex(ctx, poolBlock.MainId()) %}
 
Age [h:m:s]PoW HashFound byDifficulty
{%s date_diff_short(poolBlock.Main.Timestamp) %}{%= hex(ctx, sideBlock.PowHash) %}{%dul sideBlock.Difficulty %} / {%dul sideBlock.PowDifficulty %}-{%dul poolBlock.Side.Difficulty.Lo %}
 
ValuationCoinbase IdCoinbase RewardCoinbase Private Key
{%= side_block_valuation(sideBlock, ctx.Consensus) %}{%= side_block_valuation(poolBlock, ctx.Consensus) %}{%= hex(ctx, poolBlock.CoinbaseId()) %}{%= hex(ctx, poolBlock.CoinbaseId()) %}{%s monero_to_xmr(poolBlock.Main.Coinbase.TotalReward) %} XMR{%= hex(ctx, poolBlock.Side.CoinbasePrivateKey) %}
 
NonceCumulative DifficultyExtra NonceMonero Target Difficulty
{%dul uint64(poolBlock.Main.Nonce) %}{%= hex(ctx, poolBlock.Side.CumulativeDifficulty) %}{%dul uint64(poolBlock.ExtraNonce()) %}{% if sideBlock != nil && sideBlock.MainDifficulty != 0 %}{%dul sideBlock.MainDifficulty %}{% endif %}
 
SoftwareVersion Signaling{% if poolBlock.ShareVersion() > sidechain.ShareVersion_V1 %}Template Extra Buffer{% endif %}Deterministic Private Key Seed
{%= software_info(poolBlock.Side.ExtraBuffer.SoftwareId, poolBlock.Side.ExtraBuffer.SoftwareVersion) %}{%s= poolBlock.ShareVersion().String() %} ({% if poolBlock.ShareVersionSignaling() != sidechain.ShareVersion_None %}signaling {%s= poolBlock.ShareVersionSignaling().String() %} support{% else %}no known signaling{% endif %}){% if poolBlock.ShareVersion() > sidechain.ShareVersion_V1 %}{%= hex(ctx, poolBlock.Side.ExtraBuffer.RandomNumber) %} / {%= hex(ctx, poolBlock.Side.ExtraBuffer.SideChainExtraNonce) %}{% endif %}{% if poolBlock.GetPrivateKeySeed() != types.ZeroHash %}{%= hex(ctx, poolBlock.GetPrivateKeySeed()) %}{% else %}Not Deterministic{% endif %}
-
-
- {% if len(poolBlock.Side.Uncles) > 0 %} -

Uncle shares

- {% if sideBlock != nil && sideBlock.IsUncle() %} -

NOTE: Uncle shares do not have their listed uncles accounted for weight, the following list is just for reference.

- {% endif %} - - {% endif %} - - {% if sideBlock != nil && payouts != nil %} -

Payouts share was weighted into

- {% code var total uint64 %} - - {%= TemplatePayouts(ctx, payouts, &total) %} - {% if sideBlock.EffectiveHeight > (ctx.Pool.SideChain.LastBlock.SideHeight - uint64(ctx.Pool.SideChain.Window.Blocks)) %} -

Share is inside the PPLNS window. Any Monero blocks found during this period by any P2Pool miner will provide a direct payout.

- {% else %} -
Share is outside the PPLNS window. No more payouts for this share will be provided by other P2Pool miners.
- {% endif %} - {% endif %} - -

Coinbase Transaction

- - {% if sideBlock != nil && sideBlock.MinedMainAtHeight && sweepsCount > 0 %} - - - - {% elseif sideBlock != nil && sideBlock.MinedMainAtHeight %} -
{%s coinbase_extra(poolBlock) %}
 
- - - {% else %} -
{%s coinbase_extra(poolBlock) %}
 
- - - {% endif %} - - - - {% if sideBlock != nil && sideBlock.MinedMainAtHeight %} - {% if len(coinbaseOutputs) >= len(poolBlock.Main.Coinbase.Outputs) %} - - {% endif %} - - {% else %} - {% if len(coinbaseOutputs) >= len(poolBlock.Main.Coinbase.Outputs) %} - - {% endif %} - - {% endif %} - {% if sideBlock != nil && sideBlock.MinedMainAtHeight %} - - - {% if sweepsCount > 0 %} - - {% endif %} - {% endif %} - - {% for _, t := range poolBlock.Main.Coinbase.Outputs %} - - - - {% code var globalOutputIndex uint64 %} - {% if len(coinbaseOutputs) > int(t.Index) %} - {% code globalOutputIndex = coinbaseOutputs[t.Index].GlobalOutputIndex %} - {%= TemplateRowMiner(ctx, coinbaseOutputs[t.Index].MinerAddress, coinbaseOutputs[t.Index].MinerAlias) %} - {% endif %} - - {% if sideBlock != nil && sideBlock.MinedMainAtHeight %} - - - {% if sweepsCount > 0 %} - {% code outputSweeps := sweeps[t.Index] %} - {% if len(outputSweeps) > 0 %} - {% code sweep := outputSweeps[0] %} - - {% elseif (ctx.Pool.MainChain.Height - sideBlock.MainHeight + 1) < 60 %} - - {% else %} - - {% endif %} - {% endif %} - {% endif %} - - {% endfor %} -
{%s coinbase_extra(poolBlock) %}
 
Output IndexEphemeral Public KeyReceiver AddressRewardMiner AddressReward ShareGlobal Output IndexProofSwept
{%dul t.Index %}{%= hex(ctx, t.EphemeralPublicKey) %}{%s monero_to_xmr(t.Reward) %} XMR{%dul globalOutputIndex %}[Payout Proof #{%dul t.Index %}]{%= shorten(ctx, sweep.Id, 10) %} {%d sweep.MinerCount %} / {%d sweep.InputCount %}Not unlockedNot known
- {% if len(poolBlock.Main.Transactions) > 0 %} -

Included Transactions

- - {% endif %} -
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_row_miner.qtpl b/cmd/web/views/tpl_row_miner.qtpl deleted file mode 100644 index 7bc62ec..0000000 --- a/cmd/web/views/tpl_row_miner.qtpl +++ /dev/null @@ -1,14 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" %} - -{% func TemplateRowMiner(ctx *GlobalRequestContext, addr *address.Address, alias string) %} -{%= TemplateRowMinerWithTag(ctx, addr, alias, "td") %} -{% endfunc %} - -{% func TemplateRowMinerWithTag(ctx *GlobalRequestContext, addr *address.Address, alias string, tag string) %} - {% code encodedMinerAddress := addr.ToBase58() %} - {% if alias != "" %} - <{%s tag %} title="{%z= encodedMinerAddress %} ({%s alias %})" class="mono small">{%= shorten(ctx, alias, 10) %} - {% else %} - <{%s tag %} title="{%z= encodedMinerAddress %}" class="mono small">{%= shorten(ctx, encodedMinerAddress, 10) %} - {% endif %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_shares.qtpl b/cmd/web/views/tpl_shares.qtpl deleted file mode 100644 index ec9a1b0..0000000 --- a/cmd/web/views/tpl_shares.qtpl +++ /dev/null @@ -1,48 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} - -{% func TemplateShares(ctx *GlobalRequestContext, shares []*index.SideBlock, isMiner bool, efforts *[]float64) %} - - - - - - - {% if efforts != nil %} - - {% endif %} - {% if !isMiner %} - - {% endif %} - - - - - {% for i, s := range shares %} - - - - {% if s.MinedMainAtHeight %} - - {% else %} - - {% endif %} - - {% if efforts != nil %} - {% if effort := (*efforts)[i]; effort >= 0 %} - - {% else %} - - {% endif %} - {% endif %} - {% if !isMiner %} - {%= TemplateRowMiner(ctx, s.MinerAddress, s.MinerAlias) %} - {% endif %} - - - - - {% endfor %} -
P2Pool HeightP2Pool IdMonero HeightAge [h:m:s]Estimated EffortFound bySoftwareWeightValuation
{%dul s.SideHeight %}{%= shorten(ctx, s.TemplateId, 10) %}{%dul s.MainHeight %}{%dul s.MainHeight %}{%s date_diff_short(s.Timestamp) %} - {%f.2 effort %}% - unknown{%= software_info(s.SoftwareId, s.SoftwareVersion) %}{%s si_units(side_block_weight(s, s.SideHeight, ctx.Consensus.ChainWindowSize, ctx.Consensus), 2) %}{%= side_block_valuation(s, ctx.Consensus) %}
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/tpl_sweeps.qtpl b/cmd/web/views/tpl_sweeps.qtpl deleted file mode 100644 index 298fd00..0000000 --- a/cmd/web/views/tpl_sweeps.qtpl +++ /dev/null @@ -1,45 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} - -{% func TemplateSweeps(ctx *GlobalRequestContext, sweeps <-chan *index.MainLikelySweepTransaction, isMiner bool) %} -{% if isMiner %} - -{% else %} -
-{% endif %} - - - - {% if !isMiner %} - - {% endif %} - - - - - - - - {% for s := range sweeps %} - - - - {% if !isMiner %} - {% code - addr := s.Address.ToBase58() - %} - - {% endif %} - - - - - - {% if s.Value == 0 %} - - {% else %} - - {% endif %} - - {% endfor %} -
Transaction IdAge [h:m:s]Owned byDecoys per InputInputs / OutputsSelf DecoysOther DecoysUnknown DecoysSwept Coinbase Value
{%= shorten(ctx, s.Id, 10) %}{%s date_diff_short(s.Timestamp) %}{%= shorten(ctx, addr, 10) %}{%d s.InputDecoyCount %}{%d s.InputCount %} / {%d len(s.GlobalOutputIndices) %}{%d s.MinerCount %} ({%f.2 float64(s.MinerRatio)*100 %}%){%d s.OtherMinersCount %} ({%f.2 float64(s.OtherMinersRatio)*100 %}%){%d s.NoMinerCount %} ({%f.2 float64(s.NoMinerRatio)*100 %}%)-{%s monero_to_xmr(s.Value) %} XMR
-{% endfunc %} \ No newline at end of file diff --git a/cmd/web/views/transaction-lookup.qtpl b/cmd/web/views/transaction-lookup.qtpl deleted file mode 100644 index e5f9932..0000000 --- a/cmd/web/views/transaction-lookup.qtpl +++ /dev/null @@ -1,184 +0,0 @@ -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %} -{% import "git.gammaspectra.live/P2Pool/p2pool-observer/types" %} -{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %} - -{% code -type TransactionLookupPage struct { - // inherit from base page, so its' title is used in error page. - BasePage - - TransactionId types.Hash - - Result *cmdutils.TransactionLookupResult - - Miner *index.TransactionInputQueryResultsMatch - LikelyMiner bool - - MinerCount uint64 - NoMinerCount uint64 - OtherMinerCount uint64 - - MinerRatio float64 - NoMinerRatio float64 - OtherMinerRatio float64 - - TopTimestamp uint64 - BottomTimestamp uint64 - - Positions struct { - MinerCoinbase *cmdutils.PositionChart - OtherMinerCoinbase *cmdutils.PositionChart - MinerSweep *cmdutils.PositionChart - OtherMinerSweep *cmdutils.PositionChart - NoMiner *cmdutils.PositionChart - } -} -%} - -{% func (p *TransactionLookupPage) Title() %} -{%= p.BasePage.Title() %} - Sweep Transaction Lookup -{% endfunc %} - -{% func (p *TransactionLookupPage) Content() %} -
-
-

Sweep Transaction Lookup

-

- This tool looks up inputs used in the transaction decoys and correlates it against known P2Pool outputs. It then displays the likeliness of the transaction being a sweep transaction by a specific p.Miner -
- Do note false positives can be found. However, large sweep transactions with many inputs will most likely be correct. -
- If available, this tool will query other P2Pool SideChains. -

-

- [Recent Likely Sweep Transactions] -

-
-
- -
-
- -
-
-
- - - -{% if p.TransactionId != types.ZeroHash %} -
-
-

Transaction Lookup Result

- {% if p.Miner == nil %} -

Not enough miner results found.

- {% else %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% code totalInputCount := 0 %} - {% for index, input := range p.Result.Inputs %} - - {% code outputCount := 0 %} - {% for oindex, o := range input.MatchedOutputs %} - {% if o == nil %} - {% if outputCount > 0 %} - - {% else %} - - - - {% endif %} - - {% code out := p.Result.Outs[totalInputCount] %} - - - - - - {% else %} - {% if outputCount > 0 %} - - {% else %} - - - - {% endif %} - {% code - addr := o.Address.ToBase58() - %} - - {% if o.Coinbase != nil %} - - - - - {% elseif o.Sweep != nil %} - - - - - {% else %} - - - - - {% endif %} - - {% endif %} - {% code outputCount++ %} - {% code totalInputCount++ %} - {% endfor %} - - {% endfor %} -
-

Transaction Id: {%= hex(p.Context(), p.TransactionId) %}

- {% code minerAddress := p.Miner.Address.ToBase58() %} -

Miner Payout Address: {%z= minerAddress %}

-

Likely: {% if p.LikelyMiner %}Yes{% else %}Not likely{% endif %}

-
Total Inputs
{%d len(p.Result.Inputs) %}
Decoys per Input
{%d len(p.Result.Inputs[0].Input.KeyOffsets) %}
Unknown Inputs
{%dul p.NoMinerCount %} ({%f.3 p.NoMinerRatio %}%)
Other Miner Inputs
{%dul p.OtherMinerCount %} ({%f.3 p.OtherMinerRatio %}%)
Top Miner Inputs
{%dul p.MinerCount %} ({%f.3 p.MinerRatio %}%)
Coinbase Inputs Amount
{%s monero_to_xmr(p.Miner.CoinbaseAmount) %} XMR
Coinbase / Sweep Inputs
{%dul p.Miner.CoinbaseCount %} / {%dul p.Miner.SweepCount %}
 
-

Miner inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})

- {%s p.Positions.MinerCoinbase.String() %} - {%s p.Positions.MinerSweep.String() %} -
-

Other Miner inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})

- {%s p.Positions.OtherMinerCoinbase.String() %} - {%s p.Positions.OtherMinerSweep.String() %} -
-

Unknown inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})

- {%s p.Positions.NoMiner.String() %} -
 
Input IndexInput Key ImageDecoy OwnerSource TransactionGlobal Output IndexSource TypeSource Value
{%d index %}{%= shorten(p.Context(), input.Input.KeyImage, 10) %}-{%= shorten(p.Context(), out.TransactionId, 10) %}{%dul input.Input.KeyOffsets[oindex] %}--
{%d index %}{%= shorten(p.Context(), input.Input.KeyImage, 10) %}{%= shorten(p.Context(), addr, 10) %}{%= shorten(p.Context(), o.Coinbase.Id, 10) %} #{%dul uint64(o.Coinbase.Index) %}{%dul o.GlobalOutputIndex %}Coinbase{%s monero_to_xmr(o.Coinbase.Value) %} XMR{%= shorten(p.Context(), o.Sweep.Id, 10) %} - {% for oindex, goi := range o.Sweep.GlobalOutputIndices %} - {% if goi == o.GlobalOutputIndex %} - #{%d oindex %}/{%d len(o.Sweep.GlobalOutputIndices) %} - {% endif %} - {% endfor %}{%dul o.GlobalOutputIndex %}Previous Sweep(?) {%s monero_to_xmr(o.Sweep.Value) %} XMRUnknown{%dul o.GlobalOutputIndex %}--
 
- - {% endif %} -
-{% endif %} -{% endfunc %} \ No newline at end of file diff --git a/cmd/web/web.go b/cmd/web/web.go deleted file mode 100644 index d76951d..0000000 --- a/cmd/web/web.go +++ /dev/null @@ -1,1275 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" - cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" - "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/web/views" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - address2 "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - types2 "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" - "github.com/goccy/go-json" - "github.com/gorilla/mux" - "github.com/valyala/quicktemplate" - "io" - "math" - "net/http" - _ "net/http/pprof" - "net/netip" - "net/url" - "os" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -func toUint64(t any) uint64 { - if x, ok := t.(uint64); ok { - return x - } else if x, ok := t.(int64); ok { - return uint64(x) - } else if x, ok := t.(uint); ok { - return uint64(x) - } else if x, ok := t.(int); ok { - return uint64(x) - } else if x, ok := t.(uint32); ok { - return uint64(x) - } else if x, ok := t.(types2.SoftwareId); ok { - return uint64(x) - } else if x, ok := t.(types2.SoftwareVersion); ok { - return uint64(x) - } else if x, ok := t.(int32); ok { - return uint64(x) - } else if x, ok := t.(float64); ok { - return uint64(x) - } else if x, ok := t.(float32); ok { - return uint64(x) - } else if x, ok := t.(string); ok { - if n, err := strconv.ParseUint(x, 10, 0); err == nil { - return n - } - } - - return 0 -} - -func toString(t any) string { - - if s, ok := t.(string); ok { - return s - } else if h, ok := t.(types.Hash); ok { - return h.String() - } - - return "" -} - -func toInt64(t any) int64 { - if x, ok := t.(uint64); ok { - return int64(x) - } else if x, ok := t.(int64); ok { - return x - } else if x, ok := t.(uint); ok { - return int64(x) - } else if x, ok := t.(uint32); ok { - return int64(x) - } else if x, ok := t.(int32); ok { - return int64(x) - } else if x, ok := t.(int); ok { - return int64(x) - } else if x, ok := t.(float64); ok { - return int64(x) - } else if x, ok := t.(float32); ok { - return int64(x) - } else if x, ok := t.(string); ok { - if n, err := strconv.ParseInt(x, 10, 0); err == nil { - return n - } - } - - return 0 -} - -func toFloat64(t any) float64 { - if x, ok := t.(float64); ok { - return x - } else if x, ok := t.(float32); ok { - return float64(x) - } else if x, ok := t.(uint64); ok { - return float64(x) - } else if x, ok := t.(int64); ok { - return float64(x) - } else if x, ok := t.(uint); ok { - return float64(x) - } else if x, ok := t.(int); ok { - return float64(x) - } else if x, ok := t.(string); ok { - if n, err := strconv.ParseFloat(x, 0); err == nil { - return n - } - } - - return 0 -} - -//go:generate go run github.com/valyala/quicktemplate/qtc@v1.7.0 -dir=views -func main() { - - var responseBufferPool sync.Pool - responseBufferPool.New = func() any { - return make([]byte, 0, 1024*1024) //1 MiB allocations - } - - //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") - debugListen := flag.String("debug-listen", "", "Provide a bind address and port to expose a pprof HTTP API on it.") - flag.Parse() - - client.SetDefaultClientSettings(fmt.Sprintf("http://%s:%d", *moneroHost, *moneroRpcPort)) - - var ircLinkTitle, ircLink, webchatLink, matrixLink string - ircUrl, err := url.Parse(os.Getenv("SITE_IRC_URL")) - if err == nil && ircUrl.Host != "" { - ircLink = ircUrl.String() - humanHost := ircUrl.Host - splitChan := strings.Split(ircUrl.Fragment, "/") - - if len(splitChan) > 1 { - ircLink = strings.ReplaceAll(ircLink, "/"+splitChan[1], "") - } - - switch strings.Split(humanHost, ":")[0] { - case "irc.libera.chat": - if len(splitChan) > 1 { - matrixLink = fmt.Sprintf("https://matrix.to/#/#%s:%s?via=matrix.org&via=%s", splitChan[0], splitChan[1], splitChan[1]) - webchatLink = fmt.Sprintf("https://web.libera.chat/?nick=Guest?#%s", splitChan[0]) - } else { - humanHost = "libera.chat" - matrixLink = fmt.Sprintf("https://matrix.to/#/#%s:%s?via=matrix.org", ircUrl.Fragment, humanHost) - webchatLink = fmt.Sprintf("https://web.libera.chat/?nick=Guest%%3F#%s", ircUrl.Fragment) - } - case "irc.hackint.org": - if len(splitChan) > 1 { - matrixLink = fmt.Sprintf("https://matrix.to/#/#%s:%s?via=matrix.org&via=%s", splitChan[0], splitChan[1], splitChan[1]) - } else { - humanHost = "hackint.org" - matrixLink = fmt.Sprintf("https://matrix.to/#/#%s:%s?via=matrix.org", ircUrl.Fragment, humanHost) - } - default: - if len(splitChan) > 1 { - matrixLink = fmt.Sprintf("https://matrix.to/#/#%s:%s?via=matrix.org&via=%s", splitChan[0], splitChan[1], splitChan[1]) - } - } - ircLinkTitle = fmt.Sprintf("#%s@%s", splitChan[0], humanHost) - } - - var basePoolInfo *cmdutils.PoolInfoResult - - for { - t := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info") - if t == nil { - time.Sleep(1) - continue - } - if t.SideChain.LastBlock != nil { - basePoolInfo = t - break - } - time.Sleep(1) - } - - consensusData, _ := utils.MarshalJSON(basePoolInfo.SideChain.Consensus) - consensus, err := sidechain.NewConsensusFromJSON(consensusData) - if err != nil { - utils.Panic(err) - } - - utils.Logf("Consensus", "Consensus id = %s", consensus.Id) - - 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"), - //TODO change to args - NetServiceAddress: os.Getenv("NET_SERVICE_ADDRESS"), - TorServiceAddress: os.Getenv("TOR_SERVICE_ADDRESS"), - Consensus: consensus, - Pool: nil, - } - - baseContext.Socials.Irc.Link = ircLink - baseContext.Socials.Irc.Title = ircLinkTitle - baseContext.Socials.Irc.WebChat = webchatLink - baseContext.Socials.Matrix.Link = matrixLink - - renderPage := func(request *http.Request, writer http.ResponseWriter, page views.ContextSetterPage, pool ...*cmdutils.PoolInfoResult) { - w := bytes.NewBuffer(responseBufferPool.Get().([]byte)) - defer func() { - defer responseBufferPool.Put(w.Bytes()[:0]) - _, _ = writer.Write(w.Bytes()) - }() - - ctx := baseContext - ctx.IsOnion = request.Host == ctx.TorServiceAddress - if len(pool) == 0 || pool[0] == nil { - ctx.Pool = lastPoolInfo.Load() - } else { - ctx.Pool = pool[0] - } - - defer func() { - if err := recover(); err != nil { - - defer func() { - // error page error'd - if err := recover(); err != nil { - w = bytes.NewBuffer(nil) - writer.Header().Set("content-type", "text/plain") - _, _ = w.Write([]byte(fmt.Sprintf("%s", err))) - } - }() - w = bytes.NewBuffer(nil) - writer.WriteHeader(http.StatusInternalServerError) - errorPage := views.NewErrorPage(http.StatusInternalServerError, "Internal Server Error", err) - errorPage.SetContext(&ctx) - - views.WritePageTemplate(w, errorPage) - } - }() - - page.SetContext(&ctx) - - bufferedWriter := quicktemplate.AcquireWriter(w) - defer quicktemplate.ReleaseWriter(bufferedWriter) - views.StreamPageTemplate(bufferedWriter, page) - } - - serveMux := mux.NewRouter() - - serveMux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - refresh := 0 - if params.Has("refresh") { - writer.Header().Set("refresh", "120") - refresh = 100 - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - 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) - - blocks := getSliceFromAPI[*index.FoundBlock](fmt.Sprintf("found_blocks?limit=%d", blocksToFetch), 5) - shares := getSideBlocksFromAPI("side_blocks?limit=50", 5) - - blocksFound := cmdutils.NewPositionChart(30*4, consensus.ChainWindowSize*4) - - tip := int64(poolInfo.SideChain.LastBlock.SideHeight) - for _, b := range blocks { - blocksFound.Add(int(tip-int64(b.SideHeight)), 1) - } - - if len(blocks) > 20 { - blocks = blocks[:20] - } - - renderPage(request, writer, &views.IndexPage{ - Refresh: refresh, - Positions: struct { - BlocksFound *cmdutils.PositionChart - }{ - BlocksFound: blocksFound, - }, - Shares: shares, - FoundBlocks: blocks, - }, poolInfo) - }) - - serveMux.HandleFunc("/api", func(writer http.ResponseWriter, request *http.Request) { - 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) { - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - hashRate := float64(0) - magnitude := float64(1000) - - params := request.URL.Query() - if params.Has("hashrate") { - hashRate = toFloat64(params.Get("hashrate")) - } - if params.Has("magnitude") { - magnitude = toFloat64(params.Get("magnitude")) - } - - currentHashRate := magnitude * hashRate - - var effortSteps = []float64{25, 50, 75, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000} - const shareSteps = 15 - - calculatePage := &views.CalculateShareTimePage{ - Hashrate: hashRate, - Magnitude: magnitude, - Efforts: nil, - EstimatedRewardPerDay: 0, - EstimatedSharesPerDay: 0, - EstimatedBlocksPerDay: 0, - } - - if currentHashRate > 0 { - var efforts []views.CalculateShareTimePageEffortEntry - - for _, effort := range effortSteps { - e := views.CalculateShareTimePageEffortEntry{ - Effort: effort, - Probability: utils.ProbabilityEffort(effort) * 100, - Between: (float64(poolInfo.SideChain.LastBlock.Difficulty) * (effort / 100)) / currentHashRate, - BetweenSolo: (float64(poolInfo.MainChain.Difficulty.Lo) * (effort / 100)) / currentHashRate, - ShareProbabilities: make([]float64, shareSteps+1), - } - - for i := uint64(0); i <= shareSteps; i++ { - e.ShareProbabilities[i] = utils.ProbabilityNShares(i, effort) - } - - efforts = append(efforts, e) - } - calculatePage.Efforts = efforts - - longWeight := types.DifficultyFrom64(uint64(currentHashRate)).Mul64(3600 * 24) - calculatePage.EstimatedSharesPerDay = float64(longWeight.Mul64(1000).Div64(poolInfo.SideChain.LastBlock.Difficulty).Lo) / 1000 - calculatePage.EstimatedBlocksPerDay = float64(longWeight.Mul64(1000).Div(poolInfo.MainChain.NextDifficulty).Lo) / 1000 - calculatePage.EstimatedRewardPerDay = longWeight.Mul64(poolInfo.MainChain.BaseReward).Div(poolInfo.MainChain.NextDifficulty).Lo - } - - renderPage(request, writer, calculatePage, poolInfo) - }) - - serveMux.HandleFunc("/connectivity-check", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var addressPort netip.AddrPort - var err error - if params.Has("address") { - addressPort, err = netip.ParseAddrPort(params.Get("address")) - if err != nil { - addr, err := netip.ParseAddr(params.Get("address")) - if err == nil { - addressPort = netip.AddrPortFrom(addr, consensus.DefaultPort()) - } - } - } - - if addressPort.IsValid() && !addressPort.Addr().IsUnspecified() { - checkInformation := getTypeFromAPI[types2.P2PoolConnectionCheckInformation[*sidechain.PoolBlock]]("consensus/connection_check/" + addressPort.String()) - var rawTip *sidechain.PoolBlock - ourTip := getTypeFromAPI[index.SideBlock]("redirect/tip") - var theirTip *index.SideBlock - if checkInformation != nil { - if checkInformation.Tip != nil { - rawTip = checkInformation.Tip - theirTip = getTypeFromAPI[index.SideBlock](fmt.Sprintf("block_by_id/%s", types.HashFromBytes(rawTip.CoinbaseExtra(sidechain.SideTemplateId)))) - } - } - renderPage(request, writer, &views.ConnectivityCheckPage{ - Address: addressPort, - YourTip: theirTip, - YourTipRaw: rawTip, - OurTip: ourTip, - Check: checkInformation, - }) - } else { - - renderPage(request, writer, &views.ConnectivityCheckPage{ - Address: netip.AddrPort{}, - YourTip: nil, - YourTipRaw: nil, - OurTip: nil, - Check: nil, - }) - } - }) - - serveMux.HandleFunc("/transaction-lookup", func(writer http.ResponseWriter, request *http.Request) { - - params := request.URL.Query() - - var txId types.Hash - if params.Has("txid") { - txId, _ = types.HashFromString(params.Get("txid")) - } - - if txId != types.ZeroHash { - fullResult := getTypeFromAPI[cmdutils.TransactionLookupResult](fmt.Sprintf("transaction_lookup/%s", txId.String())) - - if fullResult != nil && fullResult.Id == txId { - var topMiner *index.TransactionInputQueryResultsMatch - for i, m := range fullResult.Match { - if m.Address == nil { - continue - } else if topMiner == nil { - topMiner = &fullResult.Match[i] - } else { - if topMiner.Count <= 2 && topMiner.Count == m.Count { - //if count is not greater - topMiner = nil - } - break - } - } - - var topTimestamp, bottomTimestamp uint64 = 0, math.MaxUint64 - - getOut := func(outputIndex uint64) *client.Output { - if i := slices.IndexFunc(fullResult.Outs, func(output client.Output) bool { - return output.GlobalOutputIndex == outputIndex - }); i != -1 { - return &fullResult.Outs[i] - } - return nil - } - - for _, i := range fullResult.Inputs { - for j, o := range i.MatchedOutputs { - if o == nil { - if oi := getOut(i.Input.KeyOffsets[j]); oi != nil { - if oi.Timestamp != 0 { - if topTimestamp < oi.Timestamp { - topTimestamp = oi.Timestamp - } - if bottomTimestamp > oi.Timestamp { - bottomTimestamp = oi.Timestamp - } - } - } - continue - } - if o.Timestamp != 0 { - if topTimestamp < o.Timestamp { - topTimestamp = o.Timestamp - } - if bottomTimestamp > o.Timestamp { - bottomTimestamp = o.Timestamp - } - } - } - } - - timeScaleItems := topTimestamp - bottomTimestamp - if bottomTimestamp == math.MaxUint64 { - timeScaleItems = 1 - } - minerCoinbaseChart := cmdutils.NewPositionChart(170, timeScaleItems) - minerCoinbaseChart.SetIdle('_') - minerSweepChart := cmdutils.NewPositionChart(170, timeScaleItems) - minerSweepChart.SetIdle('_') - otherCoinbaseMinerChart := cmdutils.NewPositionChart(170, timeScaleItems) - otherCoinbaseMinerChart.SetIdle('_') - otherSweepMinerChart := cmdutils.NewPositionChart(170, timeScaleItems) - otherSweepMinerChart.SetIdle('_') - noMinerChart := cmdutils.NewPositionChart(170, timeScaleItems) - noMinerChart.SetIdle('_') - - if topMiner != nil { - var noMinerCount, minerCount, otherMinerCount uint64 - for _, i := range fullResult.Inputs { - var isNoMiner, isMiner, isOtherMiner bool - for j, o := range i.MatchedOutputs { - if o == nil { - if oi := getOut(i.Input.KeyOffsets[j]); oi != nil { - if oi.Timestamp != 0 { - noMinerChart.Add(int(topTimestamp-oi.Timestamp), 1) - } - } - isNoMiner = true - } else if topMiner.Address.Compare(o.Address) == 0 { - isMiner = true - if o.Timestamp != 0 { - if o.Coinbase != nil { - minerCoinbaseChart.Add(int(topTimestamp-o.Timestamp), 1) - } else if o.Sweep != nil { - minerSweepChart.Add(int(topTimestamp-o.Timestamp), 1) - } - } - } else { - isOtherMiner = true - if o.Timestamp != 0 { - if o.Coinbase != nil { - otherCoinbaseMinerChart.Add(int(topTimestamp-o.Timestamp), 1) - } else if o.Sweep != nil { - otherSweepMinerChart.Add(int(topTimestamp-o.Timestamp), 1) - } - } - } - } - - if isMiner { - minerCount++ - } else if isOtherMiner { - otherMinerCount++ - } else if isNoMiner { - noMinerCount++ - } - } - - minerRatio := float64(minerCount) / float64(len(fullResult.Inputs)) - noMinerRatio := float64(noMinerCount) / float64(len(fullResult.Inputs)) - otherMinerRatio := float64(otherMinerCount) / float64(len(fullResult.Inputs)) - var likelyMiner bool - if (len(fullResult.Inputs) > 8 && minerRatio >= noMinerRatio && minerRatio > otherMinerRatio) || (len(fullResult.Inputs) > 8 && minerRatio > 0.35 && minerRatio > otherMinerRatio) || (len(fullResult.Inputs) >= 4 && minerRatio > 0.75) { - likelyMiner = true - } - - renderPage(request, writer, &views.TransactionLookupPage{ - TransactionId: txId, - Result: fullResult, - Miner: topMiner, - LikelyMiner: likelyMiner, - MinerCount: minerCount, - NoMinerCount: noMinerCount, - OtherMinerCount: otherMinerCount, - MinerRatio: minerRatio * 100, - NoMinerRatio: noMinerRatio * 100, - OtherMinerRatio: otherMinerRatio * 100, - TopTimestamp: topTimestamp, - BottomTimestamp: bottomTimestamp, - Positions: struct { - MinerCoinbase *cmdutils.PositionChart - OtherMinerCoinbase *cmdutils.PositionChart - MinerSweep *cmdutils.PositionChart - OtherMinerSweep *cmdutils.PositionChart - NoMiner *cmdutils.PositionChart - }{ - MinerCoinbase: minerCoinbaseChart, - OtherMinerCoinbase: otherCoinbaseMinerChart, - MinerSweep: minerSweepChart, - OtherMinerSweep: otherSweepMinerChart, - NoMiner: noMinerChart, - }, - }) - return - } - } - } - - renderPage(request, writer, &views.TransactionLookupPage{ - TransactionId: txId, - }) - }) - - serveMux.HandleFunc("/sweeps", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - refresh := 0 - if params.Has("refresh") { - writer.Header().Set("refresh", "600") - refresh = 600 - } - - var miner *cmdutils.MinerInfoResult - if params.Has("miner") { - miner = getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", params.Get("miner"))) - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - if miner != nil { - renderPage(request, writer, &views.SweepsPage{ - Refresh: refresh, - Sweeps: getStreamFromAPI[*index.MainLikelySweepTransaction](fmt.Sprintf("sweeps/%d?limit=100", miner.Id)), - Miner: miner.Address, - }, poolInfo) - } else { - renderPage(request, writer, &views.SweepsPage{ - Refresh: refresh, - Sweeps: getStreamFromAPI[*index.MainLikelySweepTransaction]("sweeps?limit=100"), - Miner: nil, - }, poolInfo) - } - }) - - serveMux.HandleFunc("/blocks", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - refresh := 0 - if params.Has("refresh") { - writer.Header().Set("refresh", "600") - refresh = 600 - } - - var miner *cmdutils.MinerInfoResult - if params.Has("miner") { - miner = getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", params.Get("miner"))) - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - if miner != nil { - renderPage(request, writer, &views.BlocksPage{ - Refresh: refresh, - FoundBlocks: getSliceFromAPI[*index.FoundBlock](fmt.Sprintf("found_blocks?&limit=100&miner=%d", miner.Id)), - Miner: miner.Address, - }, poolInfo) - } else { - renderPage(request, writer, &views.BlocksPage{ - Refresh: refresh, - FoundBlocks: getSliceFromAPI[*index.FoundBlock]("found_blocks?limit=100", 30), - Miner: nil, - }, poolInfo) - } - }) - - serveMux.HandleFunc("/miners", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - if params.Has("refresh") { - writer.Header().Set("refresh", "600") - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - currentWindowSize := uint64(poolInfo.SideChain.Window.Blocks) - windowSize := currentWindowSize - if poolInfo.SideChain.LastBlock.SideHeight <= windowSize { - windowSize = consensus.ChainWindowSize - } - size := uint64(30) - cacheTime := 30 - if params.Has("weekly") { - windowSize = consensus.ChainWindowSize * 4 * 7 - size *= 2 - if params.Has("refresh") { - writer.Header().Set("refresh", "3600") - } - cacheTime = 60 - } - - shares := getSideBlocksFromAPI(fmt.Sprintf("side_blocks_in_window?window=%d&noMainStatus&noUncles", windowSize), cacheTime) - - miners := make(map[uint64]*views.MinersPageMinerEntry) - - tipHeight := poolInfo.SideChain.LastBlock.SideHeight - wend := tipHeight - windowSize - - tip := shares[0] - - createMiner := func(miner uint64, share *index.SideBlock) { - if _, ok := miners[miner]; !ok { - miners[miner] = &views.MinersPageMinerEntry{ - Address: share.MinerAddress, - Alias: share.MinerAlias, - SoftwareId: share.SoftwareId, - SoftwareVersion: share.SoftwareVersion, - Shares: cmdutils.NewPositionChart(size, windowSize), - Uncles: cmdutils.NewPositionChart(size, windowSize), - } - } - } - - var totalWeight types.Difficulty - var uncleShareIndex int - for i, share := range shares { - miner := share.Miner - - if share.IsUncle() { - if share.SideHeight <= wend { - continue - } - createMiner(share.Miner, share) - miners[miner].Uncles.Add(int(int64(tip.SideHeight)-int64(share.SideHeight)), 1) - - uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(share.Difficulty)) - - if shares[uncleShareIndex].TemplateId == share.UncleOf { - parent := shares[uncleShareIndex] - createMiner(parent.Miner, parent) - miners[parent.Miner].Weight = miners[parent.Miner].Weight.Add64(unclePenalty.Lo) - } - miners[miner].Weight = miners[miner].Weight.Add64(uncleWeight.Lo) - - totalWeight = totalWeight.Add64(share.Difficulty) - } else { - uncleShareIndex = i - createMiner(share.Miner, share) - miners[miner].Shares.Add(int(int64(tip.SideHeight)-int64(share.SideHeight)), 1) - miners[miner].Weight = miners[miner].Weight.Add64(share.Difficulty) - totalWeight = totalWeight.Add64(share.Difficulty) - } - } - - minerKeys := utils.Keys(miners) - slices.SortFunc(minerKeys, func(a uint64, b uint64) int { - return miners[a].Weight.Cmp(miners[b].Weight) * -1 - }) - - sortedMiners := make([]*views.MinersPageMinerEntry, len(minerKeys)) - - for i, k := range minerKeys { - sortedMiners[i] = miners[k] - } - - renderPage(request, writer, &views.MinersPage{ - Refresh: 0, - Weekly: params.Has("weekly"), - Miners: sortedMiners, - WindowWeight: totalWeight, - }, poolInfo) - }) - - serveMux.HandleFunc("/share/{block:[0-9a-f]+|[0-9]+}", func(writer http.ResponseWriter, request *http.Request) { - identifier := mux.Vars(request)["block"] - params := request.URL.Query() - - var block *index.SideBlock - var coinbase index.MainCoinbaseOutputs - var rawBlock []byte - if len(identifier) == 64 { - block = getTypeFromAPI[index.SideBlock](fmt.Sprintf("block_by_id/%s", identifier)) - } else { - block = getTypeFromAPI[index.SideBlock](fmt.Sprintf("block_by_height/%s", identifier)) - } - - if block == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Share Not Found", nil)) - return - } - rawBlock = getFromAPIRaw(fmt.Sprintf("block_by_id/%s/raw", block.MainId)) - - coinbase = getSliceFromAPI[index.MainCoinbaseOutput](fmt.Sprintf("block_by_id/%s/coinbase", block.MainId)) - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - var raw *sidechain.PoolBlock - b := &sidechain.PoolBlock{} - if b.UnmarshalBinary(consensus, &sidechain.NilDerivationCache{}, rawBlock) == nil { - raw = b - } - - payouts := getStreamFromAPI[*index.Payout](fmt.Sprintf("block_by_id/%s/payouts", block.MainId)) - - sweepsCount := 0 - - var likelySweeps [][]*index.MainLikelySweepTransaction - if params.Has("sweeps") && block.MinedMainAtHeight && ((int64(poolInfo.MainChain.Height)-int64(block.MainHeight))+1) >= monero.MinerRewardUnlockTime { - indices := make([]uint64, len(coinbase)) - for i, o := range coinbase { - indices[i] = o.GlobalOutputIndex - } - data, _ := utils.MarshalJSON(indices) - uri, _ := url.Parse(os.Getenv("API_URL") + "sweeps_by_spending_global_output_indices") - if response, err := http.DefaultClient.Do(&http.Request{ - Method: "POST", - URL: uri, - Body: io.NopCloser(bytes.NewReader(data)), - }); err == nil { - func() { - defer response.Body.Close() - if response.StatusCode == http.StatusOK { - if data, err := io.ReadAll(response.Body); err == nil { - r := make([][]*index.MainLikelySweepTransaction, 0, len(indices)) - if utils.UnmarshalJSON(data, &r) == nil && len(r) == len(indices) { - likelySweeps = r - } - } - } - }() - } - - //remove not likely matching outputs - for oi, sweeps := range likelySweeps { - likelySweeps[oi] = likelySweeps[oi][:0] - for _, s := range sweeps { - if s == nil { - continue - } - if s.Address.Compare(coinbase[oi].MinerAddress) == 0 { - likelySweeps[oi] = append(likelySweeps[oi], s) - sweepsCount++ - } - } - } - - } - - if block.Timestamp < uint64(time.Now().Unix()-60) { - writer.Header().Set("cache-control", "public; max-age=604800") - } else { - writer.Header().Set("cache-control", "public; max-age=60") - } - - renderPage(request, writer, &views.SharePage{ - Block: block, - PoolBlock: raw, - Payouts: payouts, - CoinbaseOutputs: coinbase, - SweepsCount: sweepsCount, - Sweeps: likelySweeps, - }, poolInfo) - }) - - serveMux.HandleFunc("/miner/{miner:[^ ]+}", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - refresh := 0 - if params.Has("refresh") { - writer.Header().Set("refresh", "300") - refresh = 300 - } - address := mux.Vars(request)["miner"] - miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?shareEstimates", address)) - if miner == nil || miner.Address == nil { - if addr := address2.FromBase58(address); addr != nil { - miner = &cmdutils.MinerInfoResult{ - Id: 0, - Address: addr, - LastShareHeight: 0, - LastShareTimestamp: 0, - } - } else { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Invalid Address", nil)) - return - } - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - const totalWindows = 4 - wsize := consensus.ChainWindowSize * totalWindows - - currentWindowSize := uint64(poolInfo.SideChain.Window.Blocks) - - tipHeight := poolInfo.SideChain.LastBlock.SideHeight - - var shares, lastShares, lastOrphanedShares []*index.SideBlock - - var lastFound []*index.FoundBlock - var payouts []*index.Payout - var sweeps <-chan *index.MainLikelySweepTransaction - - var raw *sidechain.PoolBlock - - if miner.Id != 0 { - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - shares = getSideBlocksFromAPI(fmt.Sprintf("side_blocks_in_window/%d?from=%d&window=%d&noMiner&noMainStatus&noUncles", miner.Id, tipHeight, wsize)) - }() - wg.Add(1) - go func() { - defer wg.Done() - lastShares = getSideBlocksFromAPI(fmt.Sprintf("side_blocks?limit=50&miner=%d", miner.Id)) - - if len(lastShares) > 0 { - raw = getTypeFromAPI[sidechain.PoolBlock](fmt.Sprintf("block_by_id/%s/light", lastShares[0].MainId)) - if raw == nil || raw.ShareVersion() == sidechain.ShareVersion_None { - raw = nil - } - } - }() - wg.Add(1) - go func() { - defer wg.Done() - lastOrphanedShares = getSideBlocksFromAPI(fmt.Sprintf("side_blocks?limit=10&miner=%d&inclusion=%d", miner.Id, index.InclusionOrphan)) - }() - wg.Add(1) - go func() { - defer wg.Done() - lastFound = getSliceFromAPI[*index.FoundBlock](fmt.Sprintf("found_blocks?limit=10&miner=%d", miner.Id)) - }() - sweeps = getStreamFromAPI[*index.MainLikelySweepTransaction](fmt.Sprintf("sweeps/%d?limit=5", miner.Id)) - wg.Add(1) - go func() { - defer wg.Done() - shares = getSideBlocksFromAPI(fmt.Sprintf("side_blocks_in_window/%d?from=%d&window=%d&noMiner&noMainStatus&noUncles", miner.Id, tipHeight, wsize)) - }() - wg.Add(1) - go func() { - defer wg.Done() - //get a bit over the expected required - payouts = getSliceFromAPI[*index.Payout](fmt.Sprintf("payouts/%d?from_timestamp=%d", miner.Id, uint64(time.Now().Unix())-(consensus.ChainWindowSize*consensus.TargetBlockTime*(totalWindows+1)))) - }() - wg.Wait() - } else { - sweepC := make(chan *index.MainLikelySweepTransaction) - sweeps = sweepC - close(sweepC) - } - - 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) - - var longDiff, windowDiff types.Difficulty - - wend := tipHeight - currentWindowSize - - foundPayout := cmdutils.NewPositionChart(30*totalWindows, consensus.ChainWindowSize*totalWindows) - for _, p := range payouts { - foundPayout.Add(int(int64(tipHeight)-int64(p.SideHeight)), 1) - } - - for _, share := range shares { - if share.IsUncle() { - - unclesFound.Add(int(int64(tipHeight)-int64(share.SideHeight)), 1) - - uncleWeight, unclePenalty := consensus.ApplyUnclePenalty(types.DifficultyFrom64(share.Difficulty)) - - if i := slices.IndexFunc(shares, func(block *index.SideBlock) bool { - return block.TemplateId == share.UncleOf - }); i != -1 { - if shares[i].SideHeight > wend { - windowDiff = windowDiff.Add64(unclePenalty.Lo) - } - longDiff = longDiff.Add64(unclePenalty.Lo) - } - if share.SideHeight > wend { - windowDiff = windowDiff.Add64(uncleWeight.Lo) - unclesInWindow.Add(int(int64(tipHeight)-int64(share.SideHeight)), 1) - } - longDiff = longDiff.Add64(uncleWeight.Lo) - } else { - sharesFound.Add(int(int64(tipHeight)-toInt64(share.SideHeight)), 1) - if share.SideHeight > wend { - windowDiff = windowDiff.Add64(share.Difficulty) - sharesInWindow.Add(int(int64(tipHeight)-toInt64(share.SideHeight)), 1) - } - longDiff = longDiff.Add64(share.Difficulty) - } - } - - if len(payouts) > 10 { - payouts = payouts[:10] - } - - minerPage := &views.MinerPage{ - Refresh: refresh, - Positions: struct { - Resolution int - ResolutionWindow int - SeparatorIndex int - Blocks *cmdutils.PositionChart - Uncles *cmdutils.PositionChart - BlocksInWindow *cmdutils.PositionChart - UnclesInWindow *cmdutils.PositionChart - Payouts *cmdutils.PositionChart - }{ - Resolution: int(foundPayout.Resolution()), - ResolutionWindow: int(sharesInWindow.Resolution()), - SeparatorIndex: int(consensus.ChainWindowSize*totalWindows - currentWindowSize), - Blocks: sharesFound, - BlocksInWindow: sharesInWindow, - Uncles: unclesFound, - UnclesInWindow: unclesInWindow, - Payouts: foundPayout, - }, - Weight: longDiff.Lo, - WindowWeight: windowDiff.Lo, - Miner: miner, - LastPoolBlock: raw, - LastShares: lastShares, - LastOrphanedShares: lastOrphanedShares, - LastFound: lastFound, - LastPayouts: payouts, - LastSweeps: sweeps, - } - - if windowDiff.Cmp64(0) > 0 { - 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 - - expectedRewardNextBlock := windowDiff.Mul64(poolInfo.MainChain.BaseReward).Div(poolInfo.SideChain.Window.Weight).Lo - 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.Window.Blocks)) - dailyHashRate := types.DifficultyFrom64(poolInfo.SideChain.LastBlock.Difficulty).Mul(longDiff).Div(totalWeight).Div64(consensus.TargetBlockTime).Lo - - hashRate := float64(0) - magnitude := float64(1000) - - if dailyHashRate >= 1000000000 { - hashRate = float64(dailyHashRate) / 1000000000 - magnitude = 1000000000 - } else if dailyHashRate >= 1000000 { - hashRate = float64(dailyHashRate) / 1000000 - magnitude = 1000000 - } else if dailyHashRate >= 1000 { - hashRate = float64(dailyHashRate) / 1000 - magnitude = 1000 - } - - if params.Has("magnitude") { - magnitude = toFloat64(params.Get("magnitude")) - } - if params.Has("hashrate") { - hashRate = toFloat64(params.Get("hashrate")) - - if hashRate > 0 && magnitude > 0 { - dailyHashRate = uint64(hashRate * magnitude) - } - minerPage.HashrateSubmit = true - } - - minerPage.HashrateLocal = hashRate - minerPage.MagnitudeLocal = magnitude - - efforts := make([]float64, len(lastShares)) - for i := len(lastShares) - 1; i >= 0; i-- { - s := lastShares[i] - if i == (len(lastShares) - 1) { - efforts[i] = -1 - continue - } - previous := lastShares[i+1] - - timeDelta := uint64(max(int64(s.Timestamp)-int64(previous.Timestamp), 0)) - - expectedCumDiff := types.DifficultyFrom64(dailyHashRate).Mul64(timeDelta) - - efforts[i] = float64(expectedCumDiff.Mul64(100).Lo) / float64(s.Difficulty) - } - minerPage.LastSharesEfforts = efforts - - renderPage(request, writer, minerPage, poolInfo) - }) - - serveMux.HandleFunc("/miner-options/{miner:[^ ]+}/signed_action", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - - address := mux.Vars(request)["miner"] - miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", address)) - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - - var signedAction *cmdutils.SignedAction - - if err := json.Unmarshal([]byte(params.Get("message")), &signedAction); err != nil || signedAction == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusBadRequest, "Invalid Message", nil)) - return - } - - values := make(url.Values) - values.Set("signature", params.Get("signature")) - values.Set("message", signedAction.String()) - - var jsonErr struct { - Error string `json:"error"` - } - statusCode, buf := getFromAPI("miner_signed_action/" + string(miner.Address.ToBase58()) + "?" + values.Encode()) - _ = json.Unmarshal(buf, &jsonErr) - if statusCode != http.StatusOK { - renderPage(request, writer, views.NewErrorPage(http.StatusBadRequest, "Could not verify message", jsonErr.Error)) - return - } - }) - - serveMux.HandleFunc("/miner-options/{miner:[^ ]+}/{action:set_miner_alias|unset_miner_alias|add_webhook|remove_webhook}", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - - address := mux.Vars(request)["miner"] - miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", address)) - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - - var signedAction *cmdutils.SignedAction - - action := mux.Vars(request)["action"] - switch action { - case "set_miner_alias": - signedAction = cmdutils.SignedActionSetMinerAlias(baseContext.NetServiceAddress, params.Get("alias")) - case "unset_miner_alias": - signedAction = cmdutils.SignedActionUnsetMinerAlias(baseContext.NetServiceAddress) - case "add_webhook": - signedAction = cmdutils.SignedActionAddWebHook(baseContext.NetServiceAddress, params.Get("type"), params.Get("url")) - for _, s := range []string{"side_blocks", "payouts", "found_blocks", "orphaned_blocks", "other"} { - value := "false" - if params.Get(s) == "on" { - value = "true" - } - signedAction.Data = append(signedAction.Data, cmdutils.SignedActionEntry{ - Key: "send_" + s, - Value: value, - }) - } - case "remove_webhook": - signedAction = cmdutils.SignedActionRemoveWebHook(baseContext.NetServiceAddress, params.Get("type"), params.Get("url_hash")) - default: - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Invalid Action", nil)) - return - } - - renderPage(request, writer, &views.MinerOptionsPage{ - Miner: miner, - SignedAction: signedAction, - WebHooks: getSliceFromAPI[*index.MinerWebHook](fmt.Sprintf("miner_webhooks/%s", address)), - }) - }) - - serveMux.HandleFunc("/miner-options/{miner:[^ ]+}", func(writer http.ResponseWriter, request *http.Request) { - //params := request.URL.Query() - - address := mux.Vars(request)["miner"] - miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", address)) - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - - renderPage(request, writer, &views.MinerOptionsPage{ - Miner: miner, - SignedAction: nil, - WebHooks: getSliceFromAPI[*index.MinerWebHook](fmt.Sprintf("miner_webhooks/%s", address)), - }) - }) - - serveMux.HandleFunc("/miner", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - if params.Get("address") == "" { - http.Redirect(writer, request, "/", http.StatusMovedPermanently) - return - } - http.Redirect(writer, request, fmt.Sprintf("/miner/%s", params.Get("address")), http.StatusMovedPermanently) - }) - - serveMux.HandleFunc("/proof/{block:[0-9a-f]+|[0-9]+}/{index:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) { - identifier := utils.DecodeHexBinaryNumber(mux.Vars(request)["block"]) - requestIndex := toUint64(mux.Vars(request)["index"]) - - block := getTypeFromAPI[index.SideBlock](fmt.Sprintf("block_by_id/%s", identifier)) - - if block == nil || !block.MinedMainAtHeight { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Share Was Not Found", nil)) - return - } - - raw := getTypeFromAPI[sidechain.PoolBlock](fmt.Sprintf("block_by_id/%s/light", block.MainId)) - if raw == nil || raw.ShareVersion() == sidechain.ShareVersion_None { - raw = nil - } - - payouts := getSliceFromAPI[index.MainCoinbaseOutput](fmt.Sprintf("block_by_id/%s/coinbase", block.MainId)) - - if raw == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Coinbase Was Not Found", nil)) - return - } - - if uint64(len(payouts)) <= requestIndex { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Payout Was Not Found", nil)) - return - } - - poolInfo := getTypeFromAPI[cmdutils.PoolInfoResult]("pool_info", 5) - lastPoolInfo.Store(poolInfo) - - renderPage(request, writer, &views.ProofPage{ - Output: &payouts[requestIndex], - Block: block, - Raw: raw, - }, poolInfo) - }) - - serveMux.HandleFunc("/payouts/{miner:[0-9]+|4[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+}", func(writer http.ResponseWriter, request *http.Request) { - params := request.URL.Query() - refresh := 0 - if params.Has("refresh") { - writer.Header().Set("refresh", "600") - refresh = 600 - } - - address := mux.Vars(request)["miner"] - if params.Has("address") { - address = params.Get("address") - } - miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?noShares", address)) - - if miner == nil || miner.Address == nil { - renderPage(request, writer, views.NewErrorPage(http.StatusNotFound, "Address Not Found", "You need to have mined at least one share in the past. Come back later :)")) - return - } - - payouts := getStreamFromAPI[*index.Payout](fmt.Sprintf("payouts/%d?search_limit=0", miner.Id)) - renderPage(request, writer, &views.PayoutsPage{ - Miner: miner.Address, - Payouts: payouts, - Refresh: refresh, - }) - }) - - server := &http.Server{ - Addr: "0.0.0.0:8444", - ReadTimeout: time.Second * 2, - Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method != "GET" && request.Method != "HEAD" { - writer.WriteHeader(http.StatusForbidden) - return - } - - writer.Header().Set("content-type", "text/html; charset=utf-8") - - serveMux.ServeHTTP(writer, request) - }), - } - - if *debugListen != "" { - go func() { - if err := http.ListenAndServe(*debugListen, nil); err != nil { - utils.Panic(err) - } - }() - } - - if err := server.ListenAndServe(); err != nil { - utils.Panic(err) - } -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6e1562b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,297 +0,0 @@ -version: "2.2" - -networks: - p2pool-observer: - external: false - -volumes: - p2pool: - external: false - db: - external: false - psql-run: - external: false - -services: - tor: - image: goldy/tor-hidden-service:v0.4.7.12-54c0e54 - tmpfs: - - /tmp - restart: always - environment: - TOR_SOCKS_PORT: 0 - SERVICE1_TOR_SERVICE_HOSTS: 80:site:80,${P2POOL_EXTERNAL_PORT}:p2pool:${P2POOL_PORT} - SERVICE1_TOR_SERVICE_VERSION: '3' - SERVICE1_TOR_SERVICE_KEY: ${TOR_SERVICE_KEY} - TOR_EXTRA_OPTIONS: | - HiddenServiceNonAnonymousMode 1 - HiddenServiceSingleHopMode 1 - depends_on: - - site - - p2pool - networks: - - p2pool-observer - tor-proxy: - image: goldy/tor-hidden-service:v0.4.7.12-54c0e54 - tmpfs: - - /tmp - restart: always - environment: - TOR_SOCKS_PORT: 0.0.0.0:9050 - networks: - - p2pool-observer - site: - build: - context: ./docker/nginx - dockerfile: Dockerfile - args: - - TOR_SERVICE_ADDRESS=${TOR_SERVICE_ADDRESS} - restart: always - depends_on: - - api - - web - - daemon - tmpfs: - - /run - - /var/cache/nginx - - /tmp - networks: - - p2pool-observer - ports: - - ${SITE_PORT}:80 - db: - build: - context: ./docker/postgres - dockerfile: Dockerfile - restart: always - read_only: true - shm_size: 4gb - security_opt: - - no-new-privileges:true - environment: - - POSTGRES_USER=p2pool - - POSTGRES_PASSWORD=p2pool - - POSTGRES_DB=p2pool - command: >- - postgres - -c max_connections=1000 - -c shared_buffers=256MB - -c work_mem=64MB - -c temp_buffers=64MB - -c hash_mem_multiplier=2.0 - -c hash_mem_multiplier=2.0 - -c shared_preload_libraries='pg_stat_statements' - -c track_activity_query_size=2048 - -c pg_stat_statements.track=all - -c pg_stat_statements.save=on - -c track_io_timing=on - networks: - - p2pool-observer - healthcheck: - test: [ "CMD-SHELL", "pg_isready --dbname \"postgres://p2pool:p2pool@db/p2pool\"" ] - interval: 10s - timeout: 5s - retries: 5 - volumes: - - db:/var/lib/postgresql/data:rw - - psql-run:/var/run/postgresql:rw - tmpfs: - # For read-only filesystem, need to create a volume/tmpfs for PostgreSQL to run its much - # needed configuration. The read-only flag does not make volumes and tmpfs read-only. - - /tmp - - /run - - /run/postgresql - - p2pool: - build: - context: ./ - dockerfile: ./docker/golang/Dockerfile - args: - - BUILD_BINARY=p2pool - - GOPROXY=${GOPROXY} - restart: always - security_opt: - - no-new-privileges:true - volumes: - - p2pool:/data:rw - networks: - - p2pool-observer - working_dir: /data - ports: - - ${P2POOL_PORT}:${P2POOL_PORT} - command: >- - /usr/bin/p2pool - -debug-listen 0.0.0.0:6060 - -out-peers ${P2POOL_OUT_PEERS} - -in-peers ${P2POOL_IN_PEERS} - -host ${MONEROD_HOST} - -rpc-port ${MONEROD_RPC_PORT} - -zmq-port ${MONEROD_ZMQ_PORT} - -p2p 0.0.0.0:${P2POOL_PORT} - -p2p-external-port ${P2POOL_EXTERNAL_PORT} - -api-bind '0.0.0.0:3131' - -archive /data/archive.db ${P2POOL_EXTRA_ARGS} - api: - build: - context: ./ - dockerfile: ./docker/golang/Dockerfile - args: - - BUILD_BINARY=api - - GOPROXY=${GOPROXY} - restart: always - environment: - - NET_SERVICE_ADDRESS=${NET_SERVICE_ADDRESS} - - TOR_SERVICE_ADDRESS=${TOR_SERVICE_ADDRESS} - - TRANSACTION_LOOKUP_OTHER=${TRANSACTION_LOOKUP_OTHER} - depends_on: - - db - - p2pool - security_opt: - - no-new-privileges:true - networks: - - p2pool-observer - volumes: - - psql-run:/var/run/postgresql:rw - command: >- - /usr/bin/api - -debug-listen 0.0.0.0:6060 - -host ${MONEROD_HOST} - -rpc-port ${MONEROD_RPC_PORT} - -api-host "http://p2pool:3131" - -db="postgres:///p2pool?host=/var/run/postgresql&user=p2pool&password=p2pool&sslmode=disable" - web: - build: - context: ./ - dockerfile: ./docker/golang/Dockerfile - args: - - BUILD_BINARY=web - - GOPROXY=${GOPROXY} - restart: always - environment: - - TOR_SERVICE_ADDRESS=${TOR_SERVICE_ADDRESS} - - NET_SERVICE_ADDRESS=${NET_SERVICE_ADDRESS} - - API_URL=http://api:8080/api/ - - SITE_TITLE=${SITE_TITLE} - - SITE_IRC_URL=${SITE_IRC_URL} - depends_on: - - api - security_opt: - - no-new-privileges:true - networks: - - p2pool-observer - command: >- - /usr/bin/web - -debug-listen 0.0.0.0:6060 - -host ${MONEROD_HOST} - -rpc-port ${MONEROD_RPC_PORT} - daemon: - build: - context: ./ - dockerfile: ./docker/golang/Dockerfile - args: - - BUILD_BINARY=daemon - - GOPROXY=${GOPROXY} - restart: always - environment: - - TOR_SERVICE_ADDRESS=${TOR_SERVICE_ADDRESS} - - NET_SERVICE_ADDRESS=${NET_SERVICE_ADDRESS} - - TRANSACTION_LOOKUP_OTHER=${TRANSACTION_LOOKUP_OTHER} - depends_on: - - db - - tor-proxy - - p2pool - security_opt: - - no-new-privileges:true - networks: - - p2pool-observer - volumes: - - psql-run:/var/run/postgresql:rw - command: >- - /usr/bin/daemon - -debug-listen 0.0.0.0:6060 - -host ${MONEROD_HOST} - -rpc-port ${MONEROD_RPC_PORT} - -api-host "http://p2pool:3131" - -hook-proxy "tor-proxy:9050" - -db="postgres:///p2pool?host=/var/run/postgresql&user=p2pool&password=p2pool&sslmode=disable" - - pgo-p2pool: - image: golang:1.22-alpine3.19 - restart: always - environment: - - GOPROXY=${GOPROXY} - depends_on: - - p2pool - security_opt: - - no-new-privileges:true - volumes: - - ./cmd/p2pool:/data:rw - networks: - - p2pool-observer - command: >- - go run git.gammaspectra.live/P2Pool/pgo-collector@27345e42a586ccf8b3a2608cc7e91d02a9615f93 - -profile-interval 1h - -profile-duration 60s - -profile-directory /data - -profile-name default.pgo - -endpoint http://p2pool:6060 - pgo-daemon: - image: golang:1.22-alpine3.19 - restart: always - environment: - - GOPROXY=${GOPROXY} - depends_on: - - daemon - security_opt: - - no-new-privileges:true - volumes: - - ./cmd/daemon:/data:rw - networks: - - p2pool-observer - command: >- - go run git.gammaspectra.live/P2Pool/pgo-collector@27345e42a586ccf8b3a2608cc7e91d02a9615f93 - -profile-interval 1h - -profile-duration 60s - -profile-directory /data - -profile-name default.pgo - -endpoint http://daemon:6060 - pgo-web: - image: golang:1.22-alpine3.19 - restart: always - environment: - - GOPROXY=${GOPROXY} - depends_on: - - web - security_opt: - - no-new-privileges:true - volumes: - - ./cmd/web:/data:rw - networks: - - p2pool-observer - command: >- - go run git.gammaspectra.live/P2Pool/pgo-collector@27345e42a586ccf8b3a2608cc7e91d02a9615f93 - -profile-interval 1h - -profile-duration 60s - -profile-directory /data - -profile-name default.pgo - -endpoint http://web:6060 - pgo-api: - image: golang:1.22-alpine3.19 - restart: always - environment: - - GOPROXY=${GOPROXY} - depends_on: - - api - security_opt: - - no-new-privileges:true - volumes: - - ./cmd/api:/data:rw - networks: - - p2pool-observer - command: >- - go run git.gammaspectra.live/P2Pool/pgo-collector@27345e42a586ccf8b3a2608cc7e91d02a9615f93 - -profile-interval 1h - -profile-duration 60s - -profile-directory /data - -profile-name default.pgo - -endpoint http://api:6060 \ No newline at end of file diff --git a/docker/golang/Dockerfile b/docker/golang/Dockerfile deleted file mode 100644 index 0dc6be3..0000000 --- a/docker/golang/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -FROM golang:1.22-alpine3.19 AS builder - - -ENV CFLAGS="-march=native -Ofast" -ENV CXXFLAGS="-march=native -Ofast" -ENV LDFLAGS="-flto" -ENV CGO_CFLAGS="-march=native -Ofast" - -RUN apk update && apk add --no-cache \ - git gcc g++ musl-dev bash autoconf automake cmake make libtool gettext - -RUN git clone --depth 1 --branch v1.2.1 https://github.com/tevador/RandomX.git /tmp/RandomX && cd /tmp/RandomX && \ - mkdir build && cd build && \ - cmake .. -DCMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX:PATH=/usr && \ - make -j$(nproc) && \ - make install && \ - cd ../ && \ - rm -rf /tmp/RandomX - - - -WORKDIR /usr/src/p2pool - -ARG GOPROXY="" -ENV GOPROXY=${GOPROXY} - -COPY go.mod go.sum ./ -RUN go mod download -x && go mod verify - -COPY . . - -ENV GOEXPERIMENT=newinliner - -RUN go run github.com/valyala/quicktemplate/qtc@v1.7.0 - -RUN go build -buildvcs=false -trimpath -v -pgo=auto -o /usr/bin/api git.gammaspectra.live/P2Pool/p2pool-observer/cmd/api -RUN go build -buildvcs=false -trimpath -v -pgo=auto -o /usr/bin/daemon git.gammaspectra.live/P2Pool/p2pool-observer/cmd/daemon -RUN go build -buildvcs=false -trimpath -v -pgo=auto -o /usr/bin/p2pool git.gammaspectra.live/P2Pool/p2pool-observer/cmd/p2pool -RUN go build -buildvcs=false -trimpath -v -pgo=auto -o /usr/bin/web git.gammaspectra.live/P2Pool/p2pool-observer/cmd/web - -FROM alpine:3.19 - -RUN apk update && apk add --no-cache \ - libstdc++ libgcc - -ARG BUILD_BINARY - -COPY --from=builder /usr/bin/${BUILD_BINARY} /usr/bin/${BUILD_BINARY} - -WORKDIR /data \ No newline at end of file diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile deleted file mode 100644 index 3099419..0000000 --- a/docker/nginx/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM nginx:1.25-alpine3.18 - -COPY static /web -COPY snippets/*.conf /etc/nginx/snippets/ -COPY http.conf /etc/nginx/conf.d/http.conf -COPY site.conf /etc/nginx/conf.d/site.conf - -ARG TOR_SERVICE_ADDRESS - -RUN echo "add_header Onion-Location \"http://${TOR_SERVICE_ADDRESS}\$request_uri\" always;" > /etc/nginx/snippets/onion-headers.conf diff --git a/docker/nginx/http.conf b/docker/nginx/http.conf deleted file mode 100644 index 58adf9a..0000000 --- a/docker/nginx/http.conf +++ /dev/null @@ -1 +0,0 @@ -resolver 127.0.0.11 ipv6=off valid=30s; \ No newline at end of file diff --git a/docker/nginx/site.conf b/docker/nginx/site.conf deleted file mode 100644 index 4d224c0..0000000 --- a/docker/nginx/site.conf +++ /dev/null @@ -1,123 +0,0 @@ -upstream api-keepalive { - server api:8080; - keepalive 32; -} - -upstream web-keepalive { - server web:8444; - keepalive 32; -} - -upstream daemon-keepalive { - server daemon:8787; - keepalive 32; -} - -server { - listen 80 fastopen=200 default_server; - - access_log /dev/null; - error_log /dev/null; - - server_name _; - - root /web; - - proxy_connect_timeout 60; - proxy_read_timeout 120; - max_ranges 1; - tcp_nodelay on; - - gzip on; - gzip_types text/html text/css text/xml text/plain text/javascript text/xml application/xml application/x-javascript application/javascript application/json image/svg+xml application/font-woff application/font-woff2 application/font-ttf application/octet-stream application/wasm; - gzip_min_length 1000; - gzip_proxied any; - gzip_comp_level 5; - gzip_disable "MSIE [1-6]\."; - server_tokens off; - - real_ip_header X-Forwarded-For; - set_real_ip_from 0.0.0.0/0; - absolute_redirect off; - - rewrite ^/c/(.*)$ /api/redirect/coinbase/$1 last; - rewrite ^/t/(.*)$ /api/redirect/transaction/$1 last; - rewrite ^/b/(.*)$ /api/redirect/block/$1 last; - rewrite ^/s/(.*)$ /api/redirect/share/$1 last; - rewrite ^/p/(.*)$ /api/redirect/prove/$1 last; - rewrite ^/m/(.*)$ /api/redirect/miner/$1 last; - - try_files $uri $uri/ @web; - include snippets/onion-headers.conf; - - location = / { - proxy_pass http://web-keepalive; - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_set_header Host $http_host; - add_header Allow "GET, POST"; - - include snippets/security-headers.conf; - include snippets/csp.conf; - include snippets/onion-headers.conf; - } - - location = /api { - proxy_pass http://web-keepalive; - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_set_header Host $http_host; - add_header Allow "GET, POST"; - - include snippets/security-headers.conf; - include snippets/csp.conf; - include snippets/onion-headers.conf; - } - - location = /api/events { - proxy_pass http://daemon-keepalive; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_read_timeout 86400; - add_header Cache-Control "no-store"; - include snippets/security-headers.conf; - include snippets/csp.conf; - include snippets/onion-headers.conf; - } - - - location @web { - proxy_pass http://web-keepalive; - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_set_header Host $http_host; - add_header Allow "GET, POST"; - - include snippets/security-headers.conf; - include snippets/csp.conf; - include snippets/onion-headers.conf; - } - - - location ^~ /api/ { - proxy_pass http://api-keepalive; - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_set_header Host $http_host; - add_header Allow "HEAD, OPTIONS, GET, POST"; - - include snippets/security-headers.conf; - include snippets/cors.conf; - include snippets/onion-headers.conf; - } - - - location ~* \.(jpg|jpeg|png|webp|gif|svg|ico|css|js|mjs|xml|woff|ttf|ttc|wasm|data|mem)$ { - add_header Cache-Control "public, max-age=2592000"; # 30 days - gzip on; - sendfile on; - } - - -} \ No newline at end of file diff --git a/docker/nginx/snippets/cors.conf b/docker/nginx/snippets/cors.conf deleted file mode 100644 index a77df38..0000000 --- a/docker/nginx/snippets/cors.conf +++ /dev/null @@ -1,5 +0,0 @@ -add_header Access-Control-Allow-Origin $http_origin always; -add_header Access-Control-Max-Age 1728000 always; -add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS' always; -add_header Access-Control-Allow-Headers 'DNT,Authorization,Origin,Accept,User-Agent,X-Requested-With,If-Modified-Since,If-None-Match,Keep-Alive,Cache-Control,Content-Type,Range' always; -add_header Access-Control-Expose-Headers 'Content-Type, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, Cache-Control' always; \ No newline at end of file diff --git a/docker/nginx/snippets/csp.conf b/docker/nginx/snippets/csp.conf deleted file mode 100644 index c9b4199..0000000 --- a/docker/nginx/snippets/csp.conf +++ /dev/null @@ -1 +0,0 @@ -add_header Content-Security-Policy "default-src 'none'; img-src blob: data: ; object-src 'none'; style-src 'unsafe-inline'; style-src-elem 'unsafe-inline'; style-src-attr 'unsafe-inline'; prefetch-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; navigate-to * 'self'" always; \ No newline at end of file diff --git a/docker/nginx/snippets/security-headers.conf b/docker/nginx/snippets/security-headers.conf deleted file mode 100644 index 58d6d15..0000000 --- a/docker/nginx/snippets/security-headers.conf +++ /dev/null @@ -1,18 +0,0 @@ -add_header X-Frame-Options deny always; -add_header X-DNS-Prefetch-Control off always; -add_header X-Content-Type-Options nosniff always; -add_header X-XSS-Protection "1; mode=block" always; - -add_header Referrer-Policy "no-referrer" always; -add_header Cross-Origin-Opener-Policy "same-origin" always; -add_header Cross-Origin-Embedder-Policy "require-corp" always; -add_header Cross-Origin-Resource-Policy "same-origin" always; - -add_header X-Robots-Tag "nofollow, notranslate" always; -add_header Tk "N" always; -add_header Permissions-Policy "interest-cohort=(), autoplay=(), oversized-images=(), payment=(), fullscreen=(), camera=(), microphone=(), geolocation=(), usb=(), midi=()" always; -add_header Vary 'Origin, Accept-Encoding' always; - -add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; -add_header Expect-CT "max-age=31536000, enforce" always; -add_header Expect-Staple 'max-age=31536000; includeSubDomains; preload' always; diff --git a/docker/nginx/static/robots.txt b/docker/nginx/static/robots.txt deleted file mode 100644 index 69ce278..0000000 --- a/docker/nginx/static/robots.txt +++ /dev/null @@ -1,10 +0,0 @@ -User-agent: * -Disallow: /miner -Disallow: /miners -Disallow: /sweeps -Disallow: /transaction-lookup -Disallow: /proof -Disallow: /api/ - -User-agent: PetalBot -Disallow: / \ No newline at end of file diff --git a/docker/postgres/Dockerfile b/docker/postgres/Dockerfile deleted file mode 100644 index 0a1bd2e..0000000 --- a/docker/postgres/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM postgres:15.6 - - -RUN apt-get update \ - && apt-get install -y --no-install-recommends git ca-certificates make gcc postgresql-server-dev-$PG_MAJOR=$PG_VERSION \ - && rm -rf /var/lib/apt/lists/* - -RUN git clone --branch v1.7 --depth 1 https://github.com/sraoss/pg_ivm.git /usr/src/pg_ivm \ - && cd /usr/src/pg_ivm \ - && make install \ - && cd / \ - && rm -rf /usr/src/pg_ivm diff --git a/go.mod b/go.mod index 01025e7..547df6d 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.gammaspectra.live/P2Pool/p2pool-observer +module git.gammaspectra.live/P2Pool/consensus/v3 go 1.22 diff --git a/go.work b/go.work deleted file mode 100644 index 86abc4e..0000000 --- a/go.work +++ /dev/null @@ -1,20 +0,0 @@ -go 1.22 - -use ( - . - cmd/api - cmd/apitocache - cmd/archivetoarchive - cmd/archivetoindex - cmd/cachetoarchive - cmd/daemon - cmd/httputils - cmd/index - cmd/legacytoarchive - cmd/p2pool - cmd/readcache - cmd/recoverpoolblock - cmd/scansweeps - cmd/utils - cmd/web -) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 7c1740c..0000000 --- a/go.work.sum +++ /dev/null @@ -1,55 +0,0 @@ -github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/gordonklaus/ineffassign v0.0.0-20210522101830-0589229737b2 h1:hC4RAQwLzbDbHsa+CwwGBm1uG2oX9o3Frx9G73duPi8= -github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY= -github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/valyala/fasthttp v1.30.0 h1:nBNzWrgZUUHohyLPU/jTvXdhrcaf2m5k3bWk+3Q049g= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= -go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/monero/address/address.go b/monero/address/address.go index 8d76e3c..771f2bf 100644 --- a/monero/address/address.go +++ b/monero/address/address.go @@ -3,8 +3,8 @@ package address import ( "bytes" "errors" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" "git.gammaspectra.live/P2Pool/moneroutil" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" "slices" ) diff --git a/monero/address/address_test.go b/monero/address/address_test.go index b1b5b22..29a6159 100644 --- a/monero/address/address_test.go +++ b/monero/address/address_test.go @@ -3,10 +3,10 @@ package address import ( "bytes" "encoding/hex" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" "sync/atomic" "testing" ) diff --git a/monero/address/crypto.go b/monero/address/crypto.go index ff68720..e3f23a4 100644 --- a/monero/address/crypto.go +++ b/monero/address/crypto.go @@ -2,11 +2,11 @@ package address import ( "encoding/binary" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + p2poolcrypto "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" "git.gammaspectra.live/P2Pool/moneroutil" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "git.gammaspectra.live/P2Pool/sha3" "strings" ) diff --git a/monero/address/crypto_test.go b/monero/address/crypto_test.go index a1ac17f..01e7e41 100644 --- a/monero/address/crypto_test.go +++ b/monero/address/crypto_test.go @@ -1,8 +1,8 @@ package address import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "os" "path" "runtime" diff --git a/monero/address/interface.go b/monero/address/interface.go index 8bf98c0..3eeb976 100644 --- a/monero/address/interface.go +++ b/monero/address/interface.go @@ -1,7 +1,7 @@ package address import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" ) type Interface interface { diff --git a/monero/address/packed.go b/monero/address/packed.go index 47fb06b..72364ec 100644 --- a/monero/address/packed.go +++ b/monero/address/packed.go @@ -1,8 +1,8 @@ package address import ( + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" "git.gammaspectra.live/P2Pool/moneroutil" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" "unsafe" ) diff --git a/monero/block/block.go b/monero/block/block.go index 1d2b673..ac1ea05 100644 --- a/monero/block/block.go +++ b/monero/block/block.go @@ -5,12 +5,12 @@ import ( "encoding/binary" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" ) diff --git a/monero/block/func.go b/monero/block/func.go index ef64211..139ed93 100644 --- a/monero/block/func.go +++ b/monero/block/func.go @@ -1,7 +1,7 @@ package block import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) type GetDifficultyByHeightFunc func(height uint64) types.Difficulty diff --git a/monero/block/reward.go b/monero/block/reward.go index b1e63eb..5dae1c9 100644 --- a/monero/block/reward.go +++ b/monero/block/reward.go @@ -1,7 +1,7 @@ package block import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" "lukechampine.com/uint128" "math" ) diff --git a/monero/client/client.go b/monero/client/client.go index 3be7452..bfa837c 100644 --- a/monero/client/client.go +++ b/monero/client/client.go @@ -5,11 +5,11 @@ import ( "encoding/hex" "errors" "fmt" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc" "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" "github.com/floatdrop/lru" "sync" "sync/atomic" diff --git a/monero/client/client_test.go b/monero/client/client_test.go index 033af32..cbbe6ce 100644 --- a/monero/client/client_test.go +++ b/monero/client/client_test.go @@ -2,7 +2,7 @@ package client import ( "context" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "os" "testing" ) diff --git a/monero/client/zmq/types.go b/monero/client/zmq/types.go index 1a9e00a..638a3dc 100644 --- a/monero/client/zmq/types.go +++ b/monero/client/zmq/types.go @@ -1,8 +1,8 @@ package zmq import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) type Topic string diff --git a/monero/client/zmq/zmq.go b/monero/client/zmq/zmq.go index c9f0cb4..13b261b 100644 --- a/monero/client/zmq/zmq.go +++ b/monero/client/zmq/zmq.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "slices" "strings" diff --git a/monero/client/zmq/zmq_test.go b/monero/client/zmq/zmq_test.go index daf7dbb..972e20e 100644 --- a/monero/client/zmq/zmq_test.go +++ b/monero/client/zmq/zmq_test.go @@ -3,7 +3,7 @@ package zmq_test import ( "context" "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client/zmq" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client/zmq" "os" "testing" "time" diff --git a/monero/crypto/comm.go b/monero/crypto/comm.go index 711afbe..a38b1b0 100644 --- a/monero/crypto/comm.go +++ b/monero/crypto/comm.go @@ -1,7 +1,7 @@ package crypto import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) // SignatureComm Used in normal message signatures diff --git a/monero/crypto/crypto_test.go b/monero/crypto/crypto_test.go index 4ad96b4..295c7e5 100644 --- a/monero/crypto/crypto_test.go +++ b/monero/crypto/crypto_test.go @@ -2,7 +2,7 @@ package crypto import ( "encoding/hex" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "os" "path" "runtime" diff --git a/monero/crypto/derivations.go b/monero/crypto/derivations.go index b858629..1f82dbf 100644 --- a/monero/crypto/derivations.go +++ b/monero/crypto/derivations.go @@ -2,8 +2,8 @@ package crypto import ( "encoding/binary" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "git.gammaspectra.live/P2Pool/sha3" ) diff --git a/monero/crypto/hash.go b/monero/crypto/hash.go index b6b9b7a..0a052af 100644 --- a/monero/crypto/hash.go +++ b/monero/crypto/hash.go @@ -1,9 +1,9 @@ package crypto import ( + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" "git.gammaspectra.live/P2Pool/moneroutil" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "git.gammaspectra.live/P2Pool/sha3" ) diff --git a/monero/crypto/merkle.go b/monero/crypto/merkle.go index e7e71f7..1d9a79e 100644 --- a/monero/crypto/merkle.go +++ b/monero/crypto/merkle.go @@ -1,8 +1,8 @@ package crypto import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/sha3" ) diff --git a/monero/crypto/pool.go b/monero/crypto/pool.go index a302270..0794256 100644 --- a/monero/crypto/pool.go +++ b/monero/crypto/pool.go @@ -1,8 +1,8 @@ package crypto import ( + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "git.gammaspectra.live/P2Pool/sha3" "runtime" "sync" diff --git a/monero/crypto/private.go b/monero/crypto/private.go index e6ca2d2..fa39302 100644 --- a/monero/crypto/private.go +++ b/monero/crypto/private.go @@ -5,8 +5,8 @@ import ( "database/sql/driver" "encoding/hex" "errors" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" fasthex "github.com/tmthrgd/go-hex" ) diff --git a/monero/crypto/proofs.go b/monero/crypto/proofs.go index d043499..c84ad11 100644 --- a/monero/crypto/proofs.go +++ b/monero/crypto/proofs.go @@ -1,7 +1,7 @@ package crypto import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) var TxProofV2DomainSeparatorHash = Keccak256([]byte("TXPROOF_V2")) // HASH_KEY_TXPROOF_V2 diff --git a/monero/crypto/public.go b/monero/crypto/public.go index d4f972d..5b68141 100644 --- a/monero/crypto/public.go +++ b/monero/crypto/public.go @@ -5,8 +5,8 @@ import ( "database/sql/driver" "encoding/hex" "errors" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" fasthex "github.com/tmthrgd/go-hex" ) diff --git a/monero/crypto/random.go b/monero/crypto/random.go index 0f2dbed..ce0127a 100644 --- a/monero/crypto/random.go +++ b/monero/crypto/random.go @@ -3,8 +3,8 @@ package crypto import ( "crypto/rand" "encoding/binary" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "unsafe" ) diff --git a/monero/crypto/signature.go b/monero/crypto/signature.go index 56b6129..125a53b 100644 --- a/monero/crypto/signature.go +++ b/monero/crypto/signature.go @@ -1,8 +1,8 @@ package crypto import ( + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" ) // Signature Schnorr signature diff --git a/monero/randomx/randomx.go b/monero/randomx/randomx.go index 461d75d..94f78a7 100644 --- a/monero/randomx/randomx.go +++ b/monero/randomx/randomx.go @@ -1,7 +1,7 @@ package randomx import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) type Hasher interface { diff --git a/monero/randomx/randomx_cgo.go b/monero/randomx/randomx_cgo.go index 44788b1..665e426 100644 --- a/monero/randomx/randomx_cgo.go +++ b/monero/randomx/randomx_cgo.go @@ -7,9 +7,9 @@ import ( "crypto/subtle" "encoding/hex" "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/randomx-go-bindings" "runtime" "slices" diff --git a/monero/randomx/randomx_nocgo.go b/monero/randomx/randomx_nocgo.go index 86c13b0..1948d75 100644 --- a/monero/randomx/randomx_nocgo.go +++ b/monero/randomx/randomx_nocgo.go @@ -5,9 +5,9 @@ package randomx import ( "bytes" "crypto/subtle" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/go-randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "runtime" "sync" "unsafe" diff --git a/monero/transaction/coinbase.go b/monero/transaction/coinbase.go index 305c98e..f73516b 100644 --- a/monero/transaction/coinbase.go +++ b/monero/transaction/coinbase.go @@ -5,10 +5,10 @@ import ( "encoding/binary" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" ) type CoinbaseTransaction struct { diff --git a/monero/transaction/extra.go b/monero/transaction/extra.go index 5028afa..e59d934 100644 --- a/monero/transaction/extra.go +++ b/monero/transaction/extra.go @@ -5,9 +5,9 @@ import ( "encoding/binary" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" ) diff --git a/monero/transaction/output.go b/monero/transaction/output.go index b1d9ec0..2384841 100644 --- a/monero/transaction/output.go +++ b/monero/transaction/output.go @@ -4,8 +4,8 @@ import ( "encoding/binary" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" ) diff --git a/p2pool/api/p2poolapi.go b/p2pool/api/p2poolapi.go index d0a39e7..3bfdbf7 100644 --- a/p2pool/api/p2poolapi.go +++ b/p2pool/api/p2poolapi.go @@ -3,12 +3,12 @@ package api import ( "bytes" "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "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" "github.com/floatdrop/lru" "io" "net/http" diff --git a/p2pool/cache/cache.go b/p2pool/cache/cache.go index 20648ac..5e865c6 100644 --- a/p2pool/cache/cache.go +++ b/p2pool/cache/cache.go @@ -1,8 +1,8 @@ package cache import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) type Cache interface { diff --git a/p2pool/cache/legacy/legacy.go b/p2pool/cache/legacy/legacy.go index 7f8a7a4..1c972b1 100644 --- a/p2pool/cache/legacy/legacy.go +++ b/p2pool/cache/legacy/legacy.go @@ -2,9 +2,9 @@ package legacy import ( "encoding/binary" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/cache" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/cache" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" "os" "sync" diff --git a/p2pool/crypto/crypto.go b/p2pool/crypto/crypto.go index 5d30a63..b7e33b6 100644 --- a/p2pool/crypto/crypto.go +++ b/p2pool/crypto/crypto.go @@ -1,8 +1,8 @@ package crypto import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "unsafe" ) diff --git a/p2pool/crypto/crypto_test.go b/p2pool/crypto/crypto_test.go index 140859e..77ac98c 100644 --- a/p2pool/crypto/crypto_test.go +++ b/p2pool/crypto/crypto_test.go @@ -2,9 +2,9 @@ package crypto import ( "encoding/hex" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" "os" "path" "runtime" diff --git a/p2pool/mainchain/mainchain.go b/p2pool/mainchain/mainchain.go index 6ead2b8..899c9fd 100644 --- a/p2pool/mainchain/mainchain.go +++ b/p2pool/mainchain/mainchain.go @@ -4,16 +4,16 @@ import ( "context" "encoding/hex" "fmt" - mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client/zmq" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mempool" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + mainblock "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/mempool" + "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" "github.com/dolthub/swiss" "slices" "sync" diff --git a/p2pool/mempool/mempool.go b/p2pool/mempool/mempool.go index 1ebb70c..34adef2 100644 --- a/p2pool/mempool/mempool.go +++ b/p2pool/mempool/mempool.go @@ -1,9 +1,9 @@ package mempool import ( + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" "lukechampine.com/uint128" "slices" ) diff --git a/p2pool/p2p/challenge.go b/p2pool/p2p/challenge.go index 073e007..0a64dc3 100644 --- a/p2pool/p2p/challenge.go +++ b/p2pool/p2p/challenge.go @@ -3,8 +3,8 @@ package p2p import ( "crypto/rand" "encoding/binary" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "sync/atomic" "unsafe" ) diff --git a/p2pool/p2p/challenge_test.go b/p2pool/p2p/challenge_test.go index 565ee55..fb65b8b 100644 --- a/p2pool/p2p/challenge_test.go +++ b/p2pool/p2p/challenge_test.go @@ -3,7 +3,7 @@ package p2p import ( "crypto/rand" "encoding/hex" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain" "sync/atomic" "testing" ) diff --git a/p2pool/p2p/client.go b/p2pool/p2p/client.go index 9f25137..869840d 100644 --- a/p2pool/p2p/client.go +++ b/p2pool/p2p/client.go @@ -7,10 +7,10 @@ import ( "encoding/hex" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "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" "io" unsafeRandom "math/rand/v2" "net" diff --git a/p2pool/p2p/server.go b/p2pool/p2p/server.go index 1e6a054..74c2cd9 100644 --- a/p2pool/p2p/server.go +++ b/p2pool/p2p/server.go @@ -6,12 +6,12 @@ import ( "encoding/binary" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mainchain" - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/mainchain" + "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" unsafeRandom "math/rand/v2" "net" "net/netip" diff --git a/p2pool/sidechain/blobcache.go b/p2pool/sidechain/blobcache.go index e6f7864..65ac597 100644 --- a/p2pool/sidechain/blobcache.go +++ b/p2pool/sidechain/blobcache.go @@ -3,10 +3,10 @@ package sidechain import ( "bytes" "encoding/binary" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "slices" ) diff --git a/p2pool/sidechain/cache.go b/p2pool/sidechain/cache.go index 60ff931..caa9e32 100644 --- a/p2pool/sidechain/cache.go +++ b/p2pool/sidechain/cache.go @@ -2,11 +2,11 @@ package sidechain import ( "encoding/binary" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/edwards25519" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" "git.gammaspectra.live/P2Pool/sha3" ) diff --git a/p2pool/sidechain/consensus.go b/p2pool/sidechain/consensus.go index e3744b7..79b0037 100644 --- a/p2pool/sidechain/consensus.go +++ b/p2pool/sidechain/consensus.go @@ -3,12 +3,12 @@ package sidechain import ( "errors" "fmt" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/moneroutil" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" "strconv" ) diff --git a/p2pool/sidechain/fake_server.go b/p2pool/sidechain/fake_server.go index 1bb3faf..f0cd59f 100644 --- a/p2pool/sidechain/fake_server.go +++ b/p2pool/sidechain/fake_server.go @@ -2,10 +2,10 @@ package sidechain import ( "context" - mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + mainblock "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" + p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "sync" ) diff --git a/p2pool/sidechain/nilcache.go b/p2pool/sidechain/nilcache.go index d7a5480..7456944 100644 --- a/p2pool/sidechain/nilcache.go +++ b/p2pool/sidechain/nilcache.go @@ -1,9 +1,9 @@ package sidechain import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "git.gammaspectra.live/P2Pool/sha3" ) diff --git a/p2pool/sidechain/poolblock.go b/p2pool/sidechain/poolblock.go index 0555548..230e5cd 100644 --- a/p2pool/sidechain/poolblock.go +++ b/p2pool/sidechain/poolblock.go @@ -6,15 +6,15 @@ import ( "encoding/hex" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + mainblock "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" + p2poolcrypto "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" "slices" "sync/atomic" diff --git a/p2pool/sidechain/poolblock_test.go b/p2pool/sidechain/poolblock_test.go index a8b6706..f9dcf9c 100644 --- a/p2pool/sidechain/poolblock_test.go +++ b/p2pool/sidechain/poolblock_test.go @@ -2,10 +2,10 @@ package sidechain import ( "context" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "os" "testing" ) diff --git a/p2pool/sidechain/share.go b/p2pool/sidechain/share.go index 45b6bc3..102d923 100644 --- a/p2pool/sidechain/share.go +++ b/p2pool/sidechain/share.go @@ -1,9 +1,9 @@ package sidechain import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "slices" "sync" ) diff --git a/p2pool/sidechain/sidechain.go b/p2pool/sidechain/sidechain.go index ae36fd0..57a8377 100644 --- a/p2pool/sidechain/sidechain.go +++ b/p2pool/sidechain/sidechain.go @@ -5,15 +5,15 @@ import ( "context" "errors" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + mainblock "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" + p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/sha3" "github.com/dolthub/swiss" "slices" diff --git a/p2pool/sidechain/sidechain_test.go b/p2pool/sidechain/sidechain_test.go index 805707e..6052972 100644 --- a/p2pool/sidechain/sidechain_test.go +++ b/p2pool/sidechain/sidechain_test.go @@ -2,9 +2,9 @@ package sidechain import ( "encoding/binary" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/client" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/client" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" unsafeRandom "math/rand/v2" "os" diff --git a/p2pool/sidechain/sidedata.go b/p2pool/sidechain/sidedata.go index 2d71fb3..6f0141d 100644 --- a/p2pool/sidechain/sidedata.go +++ b/p2pool/sidechain/sidedata.go @@ -4,11 +4,11 @@ import ( "bytes" "encoding/binary" "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/address" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/address" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + p2pooltypes "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "io" ) diff --git a/p2pool/sidechain/utils.go b/p2pool/sidechain/utils.go index f3b41a6..0ef009e 100644 --- a/p2pool/sidechain/utils.go +++ b/p2pool/sidechain/utils.go @@ -2,13 +2,13 @@ package sidechain import ( "fmt" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" - "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/monero" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/block" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/crypto" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/randomx" + "git.gammaspectra.live/P2Pool/consensus/v3/monero/transaction" + "git.gammaspectra.live/P2Pool/consensus/v3/types" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "git.gammaspectra.live/P2Pool/sha3" "lukechampine.com/uint128" "math" diff --git a/p2pool/types/api.go b/p2pool/types/api.go index 07d2df8..f2a43ae 100644 --- a/p2pool/types/api.go +++ b/p2pool/types/api.go @@ -1,7 +1,7 @@ package types import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/types" ) type P2PoolSideChainStateResult struct { diff --git a/p2pool/types/minerdata.go b/p2pool/types/minerdata.go index 30c7d24..f7133d3 100644 --- a/p2pool/types/minerdata.go +++ b/p2pool/types/minerdata.go @@ -1,8 +1,8 @@ package types import ( - "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mempool" - "git.gammaspectra.live/P2Pool/p2pool-observer/types" + "git.gammaspectra.live/P2Pool/consensus/v3/p2pool/mempool" + "git.gammaspectra.live/P2Pool/consensus/v3/types" "time" ) diff --git a/psql.sh b/psql.sh deleted file mode 100755 index 1d866ac..0000000 --- a/psql.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -exec docker-compose exec --env PGPASSWORD=p2pool db psql --host db --username p2pool p2pool \ No newline at end of file diff --git a/types/difficulty.go b/types/difficulty.go index 5340ca1..2295830 100644 --- a/types/difficulty.go +++ b/types/difficulty.go @@ -5,7 +5,7 @@ import ( "database/sql/driver" "encoding/hex" "errors" - "git.gammaspectra.live/P2Pool/p2pool-observer/utils" + "git.gammaspectra.live/P2Pool/consensus/v3/utils" "github.com/holiman/uint256" fasthex "github.com/tmthrgd/go-hex" "io"