243 lines
7.1 KiB
Go
243 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/database"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/api"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
client.SetClientSettings(os.Getenv("MONEROD_RPC_URL"))
|
|
db, err := database.NewDatabase(os.Args[1])
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
defer db.Close()
|
|
api, err := api.New(db, os.Getenv("API_FOLDER"))
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
|
|
//TODO: force-insert section
|
|
|
|
tip := db.GetChainTip()
|
|
|
|
isFresh := tip == nil
|
|
|
|
tipHeight := uint64(1)
|
|
|
|
if tip != nil {
|
|
tipHeight = tip.Height
|
|
}
|
|
|
|
log.Printf("[CHAIN] Last known database tip is %d\n", tipHeight)
|
|
|
|
poolStats, err := api.GetPoolStats()
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
|
|
diskTip := poolStats.PoolStatistics.Height
|
|
|
|
log.Printf("[CHAIN] Last known disk tip is %d\n", diskTip)
|
|
|
|
startFrom := tipHeight
|
|
|
|
if diskTip > tipHeight && !api.BlockExists(tipHeight+1) {
|
|
for i := diskTip; api.BlockExists(i); i-- {
|
|
startFrom = i
|
|
}
|
|
}
|
|
|
|
if isFresh || startFrom != tipHeight {
|
|
block, _, err := api.GetShareEntry(startFrom)
|
|
if err != nil {
|
|
log.Panic(err)
|
|
}
|
|
id := block.Id
|
|
if block, uncles, err := api.GetShareFromRawEntry(id, true); err != nil {
|
|
log.Panicf("[CHAIN] Could not find block %s to insert at height %d. Check disk or uncles\n", id.String(), startFrom)
|
|
} else {
|
|
if err = db.InsertBlock(block, nil); err != nil {
|
|
log.Panic(err)
|
|
}
|
|
for _, uncle := range uncles {
|
|
if err = db.InsertUncleBlock(uncle, nil); err != nil {
|
|
log.Panic(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: handle jumps in blocks (missing data)
|
|
|
|
knownTip := startFrom
|
|
|
|
log.Printf("[CHAIN] Starting tip from height %d\n", knownTip)
|
|
|
|
runs := 0
|
|
|
|
//Fix blocks without height
|
|
for b := range db.GetBlocksByQuery("WHERE miner_main_difficulty = 'ffffffffffffffffffffffffffffffff' ORDER BY main_height ASC;") {
|
|
cacheHeightDifficulty(b.Main.Height)
|
|
|
|
if diff, ok := getHeightDifficulty(b.Main.Height); ok {
|
|
log.Printf("[CHAIN] Filling main difficulty for share %d, main height %d\n", b.Height, b.Main.Height)
|
|
_ = db.SetBlockMainDifficulty(b.Id, diff)
|
|
b = db.GetBlockById(b.Id)
|
|
|
|
if !b.Main.Found && b.IsProofHigherThanDifficulty() {
|
|
log.Printf("[CHAIN] BLOCK FOUND! Main height %d, main id %s\n", b.Main.Height, b.Main.Id.String())
|
|
|
|
if tx, _ := client.GetClient().GetCoinbaseTransaction(b.Coinbase.Id); tx != nil {
|
|
_ = db.SetBlockFound(b.Id, true)
|
|
processFoundBlockWithTransaction(api, b, tx)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//Fix uncle without height
|
|
for u := range db.GetUncleBlocksByQuery("WHERE miner_main_difficulty = 'ffffffffffffffffffffffffffffffff' ORDER BY main_height ASC;") {
|
|
cacheHeightDifficulty(u.Block.Main.Height)
|
|
|
|
if diff, ok := getHeightDifficulty(u.Block.Main.Height); ok {
|
|
log.Printf("[CHAIN] Filling main difficulty for uncle share %d, main height %d\n", u.Block.Height, u.Block.Main.Height)
|
|
_ = db.SetBlockMainDifficulty(u.Block.Id, diff)
|
|
u = db.GetUncleById(u.Block.Id)
|
|
|
|
if !u.Block.Main.Found && u.Block.IsProofHigherThanDifficulty() {
|
|
log.Printf("[CHAIN] BLOCK FOUND! Main height %d, main id %s\n", u.Block.Main.Height, u.Block.Main.Id.String())
|
|
|
|
if tx, _ := client.GetClient().GetCoinbaseTransaction(u.Block.Coinbase.Id); tx != nil {
|
|
_ = db.SetBlockFound(u.Block.Id, true)
|
|
processFoundBlockWithTransaction(api, u, tx)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for {
|
|
runs++
|
|
|
|
diskTip, _, _ := api.GetShareEntry(knownTip)
|
|
|
|
if rawTip, _, _ := api.GetShareFromRawEntry(diskTip.Id, false); rawTip != nil {
|
|
diskTip = rawTip
|
|
}
|
|
|
|
dbTip := db.GetBlockByHeight(knownTip)
|
|
|
|
if dbTip.Id != diskTip.Id { //Reorg has happened, delete old values
|
|
for h := knownTip; h > 0; h-- {
|
|
dbBlock := db.GetBlockByHeight(h)
|
|
diskBlock, _, _ := api.GetShareEntry(h)
|
|
if dbBlock.PreviousId == diskBlock.PreviousId {
|
|
log.Printf("[REORG] Found matching head %s at height %d\n", dbBlock.PreviousId.String(), dbBlock.Height-1)
|
|
deleted, _ := db.DeleteBlockById(dbBlock.Id)
|
|
log.Printf("[REORG] Deleted %d block(s).\n", deleted)
|
|
log.Printf("[REORG] Next tip %s : %d.\n", diskBlock.PreviousId, diskBlock.Height)
|
|
knownTip = dbBlock.Height - 1
|
|
break
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
for h := knownTip + 1; api.BlockExists(h); h++ {
|
|
diskBlock, _, _ := api.GetShareEntry(h)
|
|
if diskBlock == nil {
|
|
break
|
|
}
|
|
id := diskBlock.Id
|
|
|
|
var uncles []*database.UncleBlock
|
|
diskBlock, uncles, err = api.GetShareFromRawEntry(id, true)
|
|
if err != nil {
|
|
log.Printf("[CHAIN] Could not find block %s to insert at height %d. Check disk or uncles\n", id.String(), h)
|
|
break
|
|
}
|
|
|
|
prevBlock := db.GetBlockByHeight(h - 1)
|
|
if diskBlock.PreviousId != prevBlock.Id {
|
|
log.Printf("[CHAIN] Possible reorg occurred, aborting insertion at height %d: prev id %s != id %s\n", h, diskBlock.PreviousId.String(), prevBlock.Id.String())
|
|
break
|
|
}
|
|
|
|
log.Printf("[CHAIN] Inserting block %s at height %d\n", diskBlock.Id.String(), diskBlock.Height)
|
|
|
|
cacheHeightDifficulty(diskBlock.Main.Height)
|
|
|
|
diff, ok := getHeightDifficulty(diskBlock.Main.Height)
|
|
|
|
if ok {
|
|
err = db.InsertBlock(diskBlock, &diff)
|
|
} else {
|
|
err = db.InsertBlock(diskBlock, nil)
|
|
}
|
|
|
|
if err == nil {
|
|
for _, uncle := range uncles {
|
|
log.Printf("[CHAIN] Inserting uncle %s @ %s at %d", uncle.Block.Main.Id.String(), diskBlock.Id.String(), diskBlock.Height)
|
|
|
|
diff, ok := getHeightDifficulty(uncle.Block.Main.Height)
|
|
|
|
if ok {
|
|
err = db.InsertUncleBlock(uncle, &diff)
|
|
} else {
|
|
err = db.InsertUncleBlock(uncle, nil)
|
|
}
|
|
|
|
if uncle.Block.Main.Found {
|
|
log.Printf("[CHAIN] BLOCK FOUND! (uncle) Main height %d, main id %s", uncle.Block.Main.Height, uncle.Block.Main.Id.String())
|
|
|
|
if b, _ := api.GetRawBlock(uncle.Block.Id); b != nil {
|
|
processFoundBlockWithTransaction(api, uncle, b.Main.Coinbase)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
knownTip = diskBlock.Height
|
|
}
|
|
|
|
if diskBlock.Main.Found {
|
|
log.Printf("[CHAIN] BLOCK FOUND! Main height %d, main id %s", diskBlock.Main.Height, diskBlock.Main.Id.String())
|
|
|
|
if b, _ := api.GetRawBlock(diskBlock.Id); b != nil {
|
|
processFoundBlockWithTransaction(api, diskBlock, b.Main.Coinbase)
|
|
}
|
|
}
|
|
}
|
|
|
|
if runs%10 == 0 { //Every 10 seconds or so
|
|
for foundBlock := range db.GetAllFound(10) {
|
|
//Scan last 10 found blocks and set status accordingly if found/not found
|
|
|
|
// Look between +1 block and +4 blocks
|
|
if (diskTip.Main.Height-1) > foundBlock.GetBlock().Main.Height && (diskTip.Main.Height-5) < foundBlock.GetBlock().Main.Height || db.GetCoinbaseTransaction(foundBlock.GetBlock()) == nil {
|
|
if tx, _ := client.GetClient().GetCoinbaseTransaction(foundBlock.GetBlock().Coinbase.Id); tx == nil {
|
|
// If more than two minutes have passed before we get utxo, remove from found
|
|
log.Printf("[CHAIN] Block that was found at main height %d, cannot find output, marking not found\n", foundBlock.GetBlock().Main.Height)
|
|
_ = db.SetBlockFound(foundBlock.GetBlock().Id, false)
|
|
} else {
|
|
processFoundBlockWithTransaction(api, foundBlock, tx)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if isFresh {
|
|
//TODO: Do migration tasks
|
|
isFresh = false
|
|
}
|
|
|
|
time.Sleep(time.Second * 1)
|
|
}
|
|
}
|