Added archive, and utilities to export cache/legacy to archive

This commit is contained in:
DataHoarder 2023-03-12 15:00:38 +01:00
parent 8ee16e6d53
commit 4550fdffd0
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
11 changed files with 514 additions and 42 deletions

View file

@ -0,0 +1,86 @@
package main
import (
"flag"
"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"
"log"
"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 {
log.Panic(err)
}
cache, err := legacy.NewCache(*inputFile)
if err != nil {
log.Panic(err)
}
defer cache.Close()
archiveCache, err := archive.NewCache(*outputArchive, consensus)
if err != nil {
log.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 {
log.Printf("ERROR: 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)
}
}

View file

@ -0,0 +1,117 @@
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"
"log"
"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 {
log.Panic(err)
}
archiveCache, err := archive.NewCache(*outputArchive, consensus)
if err != nil {
log.Panic(err)
}
defer archiveCache.Close()
processed := make(map[types.Hash]bool)
totalStored := 0
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 {
log.Panic(err)
} else {
if block, err := sidechain.NewShareFromExportedBytes(hexBuf, consensus.NetworkType); err != nil {
log.Panic(err)
} else {
return block
}
}
}
return nil
}
var storeBlock func(k types.Hash, b *sidechain.PoolBlock)
storeBlock = func(k types.Hash, b *sidechain.PoolBlock) {
if b == nil || processed[k] {
return
}
if parent := loadBlock(b.Side.Parent); parent != nil {
b.FillTransactionParentIndices(parent)
storeBlock(b.Side.Parent, parent)
}
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:])
if dir, err := os.ReadDir(dPath); err != nil {
log.Panic(err)
} else {
for _, e := range dir {
h, _ := hex.DecodeString(e.Name())
id := types.HashFromBytes(h)
storeBlock(id, loadBlock(id))
}
}
}
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 {
log.Panic(err)
} else {
for _, e := range dir {
fPath := path.Join(dPath, e.Name())
log.Printf("Processing %s", fPath)
if buf, err := os.ReadFile(path.Join(dPath, e.Name())); err != nil {
log.Panic(err)
} else {
if hexBuf, err := hex.DecodeString(string(buf)); err != nil {
log.Panic(err)
} else {
if block, err := sidechain.NewShareFromExportedBytes(hexBuf, consensus.NetworkType); err != nil {
log.Panic(err)
} else {
block.Depth.Store(math.MaxUint64)
archiveCache.Store(block)
totalStored++
}
}
}
}
}
}
log.Printf("total stored %d", totalStored)
}

View file

@ -27,6 +27,7 @@ func main() {
moneroRpcPort := flag.Uint("rpc-port", 18081, "monerod RPC API port number")
moneroZmqPort := flag.Uint("zmq-port", 18083, "monerod ZMQ pub port number")
p2pListen := flag.String("p2p", fmt.Sprintf("0.0.0.0:%d", currentConsensus.DefaultPort()), "IP:port for p2p server to listen on.")
createArchive := flag.String("archive", "", "If specified, create an archive store of sidechain blocks.")
addPeers := flag.String("addpeers", "", "Comma-separated list of IP:port of other p2pool nodes to connect to")
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")
consensusConfigFile := flag.String("config", "", "Name of the p2pool config file")
@ -87,6 +88,10 @@ func main() {
settings["cache"] = "p2pool.cache"
}
if *createArchive != "" {
settings["archive"] = *createArchive
}
if p2pool, err := p2pool2.NewP2Pool(currentConsensus, settings); err != nil {
log.Fatalf("Could not start p2pool: %s", err)
} else {

View file

@ -50,7 +50,7 @@ func main() {
calculatedBlockId := block.SideTemplateId(consensus)
if expectedBlockId != calculatedBlockId {
log.Printf("ERROR: block height %d ,template id %s, expected %s", block.Side.Height, calculatedBlockId, expectedBlockId)
log.Printf("ERROR: block height %d, template id %s, expected %s", block.Side.Height, calculatedBlockId, expectedBlockId)
} else {
blob, err := block.MarshalBinary()
if err != nil {

5
go.mod
View file

@ -10,14 +10,15 @@ require (
git.gammaspectra.live/P2Pool/randomx-go-bindings v0.0.0-20221027134633-11f5607e6752
github.com/ake-persson/mapslice-json v0.0.0-20210720081907-22c8edf57807
github.com/floatdrop/lru v1.3.0
github.com/go-faster/xor v0.3.0
github.com/go-faster/xor v1.0.0
github.com/go-zeromq/zmq4 v0.15.0
github.com/gorilla/mux v1.8.0
github.com/holiman/uint256 v1.2.1
github.com/jxskiss/base62 v1.1.0
github.com/lib/pq v1.10.7
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.1
github.com/tyler-sommer/stick v1.0.4
go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.7.0
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0
golang.org/x/net v0.8.0

14
go.sum
View file

@ -19,8 +19,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/floatdrop/lru v1.3.0 h1:83abtaKjXcWrPmtzTAk2Ggq8DUKqI29YzrTrB8+vu0c=
github.com/floatdrop/lru v1.3.0/go.mod h1:83zlXKA06Bm32JImNINCiTr0ldadvdAjUe5jSwIaw0s=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
github.com/go-faster/xor v0.3.0 h1:tc0bdVe31Wj999e5rEj7K3DhHyQNp2VydYyLFj3YSN8=
github.com/go-faster/xor v0.3.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ=
github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38=
github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ=
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.15.0 h1:SLqukpmLTx0JsLaOaCCjwy5eBdfJ+ouJX/677HoFbJM=
@ -44,13 +44,19 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tyler-sommer/stick v1.0.4 h1:kuHyr9FFBBPA5dWqHWNFiCdHBqj8Mr/xuCxx7uAbzIk=
github.com/tyler-sommer/stick v1.0.4/go.mod h1:rjBy3zi6GwoxExa6OSRPPPaLqUEKNsBxTeWckhIX1us=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

207
p2pool/cache/archive/archive.go vendored Normal file
View file

@ -0,0 +1,207 @@
package archive
import (
"encoding/binary"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
bolt "go.etcd.io/bbolt"
"golang.org/x/exp/slices"
"log"
"math"
"time"
"unsafe"
)
// EpochSize Maximum amount of blocks without a full one
const EpochSize = 256
type Cache struct {
db *bolt.DB
consensus *sidechain.Consensus
}
var blocksByMainId = []byte("blocksByMainId")
var refByTemplateId = []byte("refByTemplateId")
var refBySideHeight = []byte("refBySideHeight")
var refByMainHeight = []byte("refByMainHeight")
func NewCache(path string, consensus *sidechain.Consensus) (*Cache, error) {
if db, err := bolt.Open(path, 0666, &bolt.Options{Timeout: time.Second * 5}); err != nil {
return nil, err
} else {
if err = db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists(blocksByMainId); err != nil {
return err
} else if _, err := tx.CreateBucketIfNotExists(refByTemplateId); err != nil {
return err
} else if _, err := tx.CreateBucketIfNotExists(refBySideHeight); err != nil {
return err
} else if _, err := tx.CreateBucketIfNotExists(refByMainHeight); err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return &Cache{
db: db,
consensus: consensus,
}, nil
}
}
type multiRecord []types.Hash
func multiRecordFromBytes(b []byte) multiRecord {
if len(b) == 0 || (len(b)%types.HashSize) != 0 {
return nil
}
return slices.Clone(unsafe.Slice((*types.Hash)(unsafe.Pointer(&b[0])), len(b)/types.HashSize))
}
func (r multiRecord) Contains(hash types.Hash) bool {
return slices.Contains(r, hash)
}
func (r multiRecord) Bytes() []byte {
if len(r) == 0 {
return nil
}
return slices.Clone(unsafe.Slice((*byte)(unsafe.Pointer(&r[0])), len(r)*types.HashSize))
}
func (c *Cache) Store(block *sidechain.PoolBlock) {
sideId := block.SideTemplateId(c.consensus)
mainId := block.MainId()
var sideHeight, mainHeight [8]byte
binary.LittleEndian.PutUint64(sideHeight[:], block.Side.Height)
binary.LittleEndian.PutUint64(mainHeight[:], block.Main.Coinbase.GenHeight)
if c.existsByMainId(mainId) {
return
}
var storePruned, storeCompact bool
fullBlockTemplateHeight := block.Side.Height - (block.Side.Height % EpochSize)
// store full blocks on epoch
if block.Side.Height != fullBlockTemplateHeight {
if len(block.Main.Transactions) == len(block.Main.TransactionParentIndices) && c.existsByTemplateId(block.Side.Parent) {
storeCompact = true
}
if block.Depth.Load() == math.MaxUint64 {
//fallback
if c.existsBySideChainHeightRange(block.Side.Height-c.consensus.ChainWindowSize-1, block.Side.Height-1) {
storePruned = true
}
} else if block.Depth.Load() < c.consensus.ChainWindowSize {
storePruned = true
}
}
if blob, err := block.MarshalBinaryFlags(storePruned, storeCompact); err == nil {
log.Printf("[Archive Cache] Store block id = %s, template id = %s, height = %d, sidechain height %d, pruned = %t, compact = %t, blob size = %d bytes", mainId.String(), sideId.String(), block.Main.Coinbase.GenHeight, block.Side.Height, storePruned, storeCompact, len(blob))
if err = c.db.Update(func(tx *bolt.Tx) error {
b1 := tx.Bucket(blocksByMainId)
if err = b1.Put(mainId[:], blob); err != nil {
return err
}
b2 := tx.Bucket(refByTemplateId)
if records := multiRecordFromBytes(b2.Get(sideId[:])); !records.Contains(mainId) {
records = append(records, mainId)
if err = b2.Put(sideId[:], records.Bytes()); err != nil {
return err
}
}
b3 := tx.Bucket(refBySideHeight)
if records := multiRecordFromBytes(b3.Get(sideHeight[:])); !records.Contains(mainId) {
records = append(records, mainId)
if err = b3.Put(sideHeight[:], records.Bytes()); err != nil {
return err
}
}
b4 := tx.Bucket(refByMainHeight)
if records := multiRecordFromBytes(b4.Get(mainHeight[:])); !records.Contains(mainId) {
records = append(records, mainId)
if err = b4.Put(mainHeight[:], records.Bytes()); err != nil {
return err
}
}
return nil
}); err != nil {
log.Printf("[Archive Cache] bolt error: %s", err)
}
}
}
func (c *Cache) RemoveByMainId(id types.Hash) {
//TODO
}
func (c *Cache) RemoveByTemplateId(id types.Hash) {
//TODO
}
func (c *Cache) existsByMainId(id types.Hash) (result bool) {
_ = c.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(blocksByMainId)
if b.Get(id[:]) != nil {
result = true
}
return nil
})
return result
}
func (c *Cache) LoadByMainId(id types.Hash) *sidechain.PoolBlock {
//TODO
return nil
}
func (c *Cache) existsByTemplateId(id types.Hash) (result bool) {
_ = c.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(refByTemplateId)
if b.Get(id[:]) != nil {
result = true
}
return nil
})
return result
}
func (c *Cache) LoadByTemplateId(id types.Hash) []*sidechain.PoolBlock {
//TODO
return nil
}
func (c *Cache) existsBySideChainHeightRange(startHeight, endHeight uint64) (result bool) {
_ = c.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(refBySideHeight)
var sideHeight [8]byte
for h := startHeight; h <= endHeight; h++ {
binary.LittleEndian.PutUint64(sideHeight[:], h)
if b.Get(sideHeight[:]) == nil {
return nil
}
}
result = true
return nil
})
return result
}
func (c *Cache) LoadBySideChainHeight(height uint64) []*sidechain.PoolBlock {
//TODO
return nil
}
func (c *Cache) LoadByMainChainHeight(height uint64) []*sidechain.PoolBlock {
//TODO
return nil
}
func (c *Cache) Close() {
_ = c.db.Close()
}

19
p2pool/cache/cache.go vendored
View file

@ -7,10 +7,11 @@ import (
type Cache interface {
Store(block *sidechain.PoolBlock)
Close()
}
type Loadee interface {
Consensus() *sidechain.Consensus
sidechain.ConsensusProvider
AddCachedBlock(block *sidechain.PoolBlock)
}
@ -20,6 +21,18 @@ type HeapCache interface {
}
type AddressableCache interface {
Remove(hash types.Hash)
Load(hash types.Hash) *sidechain.PoolBlock
Cache
RemoveByMainId(id types.Hash)
RemoveByTemplateId(id types.Hash)
LoadByMainId(id types.Hash) *sidechain.PoolBlock
// LoadByTemplateId returns a slice of loaded blocks. If more than one, these might have colliding nonce / extra nonce values
LoadByTemplateId(id types.Hash) []*sidechain.PoolBlock
LoadBySideChainHeight(height uint64) []*sidechain.PoolBlock
LoadByMainChainHeight(height uint64) []*sidechain.PoolBlock
}
type IndexedCache interface {
AddressableCache
}

View file

@ -10,6 +10,7 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/client/zmq"
"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/p2p"
@ -27,6 +28,7 @@ type P2Pool struct {
consensus *sidechain.Consensus
sidechain *sidechain.SideChain
mainchain *mainchain.MainChain
archive cache.AddressableCache
cache cache.HeapCache
server *p2p.Server
@ -56,6 +58,13 @@ func (p *P2Pool) RemoveBlob(key []byte) (err error) {
func (p *P2Pool) Close() {
p.ctxCancel()
_ = p.zmqClient.Close()
if p.cache != nil {
p.cache.Close()
}
if p.archive != nil {
p.archive.Close()
}
}
func NewP2Pool(consensus *sidechain.Consensus, settings map[string]string) (*P2Pool, error) {
@ -83,6 +92,12 @@ func NewP2Pool(consensus *sidechain.Consensus, settings map[string]string) (*P2P
return nil, errors.New("could not create MainChain")
}
if archivePath, ok := settings["archive"]; ok {
if pool.archive, err = archive.NewCache(archivePath, pool.consensus); err != nil {
return nil, fmt.Errorf("could not create cache: %w", err)
}
}
if cachePath, ok := settings["cache"]; ok {
if pool.cache, err = legacy.NewCache(cachePath); err != nil {
return nil, fmt.Errorf("could not create cache: %w", err)
@ -408,6 +423,9 @@ 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() {

View file

@ -12,6 +12,7 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"golang.org/x/exp/slices"
"io"
"log"
"sync"
@ -177,6 +178,48 @@ func NewShareFromExportedBytes(buf []byte, networkType NetworkType) (*PoolBlock,
return b, nil
}
func (b *PoolBlock) FillParentIndicesFromTransaction(parent *PoolBlock) error {
if len(b.Main.TransactionParentIndices) > 0 && len(b.Main.TransactionParentIndices) == len(b.Main.Transactions) {
if slices.Index(b.Main.Transactions, types.ZeroHash) != -1 { //only do this when zero hashes exist
if parent != nil && types.HashFromBytes(parent.CoinbaseExtra(SideTemplateId)) == b.Side.Parent {
for i, parentIndex := range b.Main.TransactionParentIndices {
if parentIndex != 0 {
// p2pool stores coinbase transaction hash as well, decrease
actualIndex := parentIndex - 1
if actualIndex > uint64(len(parent.Main.Transactions)) {
return errors.New("index of parent transaction out of bounds")
}
b.Main.Transactions[i] = parent.Main.Transactions[actualIndex]
}
}
}
}
}
return nil
}
func (b *PoolBlock) FillTransactionParentIndices(parent *PoolBlock) bool {
if len(b.Main.Transactions) != len(b.Main.TransactionParentIndices) {
if parent != nil && types.HashFromBytes(parent.CoinbaseExtra(SideTemplateId)) == b.Side.Parent {
b.Main.TransactionParentIndices = make([]uint64, len(b.Main.Transactions))
//do not fail if not found
for i, txHash := range b.Main.Transactions {
if parentIndex := slices.Index(parent.Main.Transactions, txHash); parentIndex != -1 {
//increase as p2pool stores tx hash as well
b.Main.TransactionParentIndices[i] = uint64(parentIndex + 1)
}
}
return true
}
return false
}
return true
}
func (b *PoolBlock) ShareVersion() ShareVersion {
// P2Pool forks to v2 at 2023-03-18 21:00 UTC
// Different miners can have different timestamps,

View file

@ -113,23 +113,13 @@ func (c *SideChain) PreprocessBlock(block *PoolBlock) (missingBlocks []types.Has
}
if len(block.Main.TransactionParentIndices) > 0 && len(block.Main.TransactionParentIndices) == len(block.Main.Transactions) {
if slices.Index(block.Main.Transactions, types.ZeroHash) != -1 { //only do this when zero hashes exist
parent := c.getParent(block)
if parent == nil {
missingBlocks = append(missingBlocks, block.Side.Parent)
return missingBlocks, errors.New("parent does not exist in compact block")
}
for i, parentIndex := range block.Main.TransactionParentIndices {
if parentIndex != 0 {
// p2pool stores coinbase transaction hash as well, decrease
actualIndex := parentIndex - 1
if actualIndex > uint64(len(parent.Main.Transactions)) {
return nil, errors.New("index of parent transaction out of bounds")
}
block.Main.Transactions[i] = parent.Main.Transactions[actualIndex]
}
}
parent := c.getParent(block)
if parent == nil {
missingBlocks = append(missingBlocks, block.Side.Parent)
return missingBlocks, errors.New("parent does not exist in compact block")
}
if err = block.FillParentIndicesFromTransaction(parent); err != nil {
return nil, err
}
} else {
// fill if not received from network
@ -140,21 +130,7 @@ func (c *SideChain) PreprocessBlock(block *PoolBlock) (missingBlocks []types.Has
}
func (c *SideChain) fillPoolBlockTransactionParentIndices(block *PoolBlock) {
if len(block.Main.Transactions) != len(block.Main.TransactionParentIndices) {
parent := c.getParent(block)
if parent != nil {
block.Main.TransactionParentIndices = make([]uint64, len(block.Main.Transactions))
//do not fail if not found
for i, txHash := range block.Main.Transactions {
if parentIndex := slices.Index(parent.Main.Transactions, txHash); parentIndex != -1 {
//increase as p2pool stores tx hash as well
block.Main.TransactionParentIndices[i] = uint64(parentIndex + 1)
}
}
}
}
block.FillTransactionParentIndices(c.getParent(block))
}
func (c *SideChain) isPoolBlockTransactionKeyIsDeterministic(block *PoolBlock) bool {