Check miner data on client block response and block broadcast

This commit is contained in:
DataHoarder 2023-03-09 16:18:42 +01:00
parent 968b07ae0b
commit d59a78c25d
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
7 changed files with 230 additions and 150 deletions

View file

@ -2,6 +2,7 @@ package mainchain
import ( import (
"context" "context"
"encoding/hex"
"fmt" "fmt"
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" 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"
@ -9,6 +10,7 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" "git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" "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/types"
"git.gammaspectra.live/P2Pool/p2pool-observer/utils" "git.gammaspectra.live/P2Pool/p2pool-observer/utils"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
@ -31,7 +33,7 @@ type MainChain struct {
mainchainByHash map[types.Hash]*sidechain.ChainMain mainchainByHash map[types.Hash]*sidechain.ChainMain
tip atomic.Pointer[sidechain.ChainMain] tip atomic.Pointer[sidechain.ChainMain]
tipMinerData atomic.Pointer[MinerData] tipMinerData atomic.Pointer[p2pooltypes.MinerData]
medianTimestamp atomic.Uint64 medianTimestamp atomic.Uint64
} }
@ -64,15 +66,94 @@ func (c *MainChain) Listen() error {
for { for {
select { select {
case <-ctx.Done(): case <-s.ErrC:
return ctx.Err() return ctx.Err()
case err := <-s.ErrC:
//TODO: retry connection
return err
case fullChainMain := <-s.FullChainMainC: case fullChainMain := <-s.FullChainMainC:
log.Print(fullChainMain) if len(fullChainMain.MinerTx.Inputs) < 1 {
continue
}
d := &sidechain.ChainMain{
Difficulty: types.ZeroDifficulty,
Height: fullChainMain.MinerTx.Inputs[0].Gen.Height,
Timestamp: uint64(fullChainMain.Timestamp),
Reward: 0,
Id: types.ZeroHash,
}
for _, o := range fullChainMain.MinerTx.Outputs {
d.Reward += o.Amount
}
outputs := make(transaction.Outputs, 0, len(fullChainMain.MinerTx.Outputs))
var totalReward uint64
for i, o := range fullChainMain.MinerTx.Outputs {
if o.ToKey != nil {
outputs = append(outputs, transaction.Output{
Index: uint64(i),
Reward: o.Amount,
Type: transaction.TxOutToKey,
EphemeralPublicKey: o.ToKey.Key,
ViewTag: 0,
})
} else if o.ToTaggedKey != nil {
tk, _ := hex.DecodeString(o.ToTaggedKey.ViewTag)
outputs = append(outputs, transaction.Output{
Index: uint64(i),
Reward: o.Amount,
Type: transaction.TxOutToTaggedKey,
EphemeralPublicKey: o.ToTaggedKey.Key,
ViewTag: tk[0],
})
} else {
//error
break
}
totalReward += o.Amount
}
if len(outputs) != len(fullChainMain.MinerTx.Outputs) {
continue
}
extraDataRaw, _ := hex.DecodeString(fullChainMain.MinerTx.Extra)
extraTags := transaction.ExtraTags{}
if err = extraTags.UnmarshalBinary(extraDataRaw); err != nil {
continue
}
blockData := &mainblock.Block{
MajorVersion: uint8(fullChainMain.MajorVersion),
MinorVersion: uint8(fullChainMain.MinorVersion),
Timestamp: uint64(fullChainMain.Timestamp),
PreviousId: fullChainMain.PrevID,
Nonce: uint32(fullChainMain.Nonce),
Coinbase: &transaction.CoinbaseTransaction{
Version: uint8(fullChainMain.MinerTx.Version),
UnlockTime: uint64(fullChainMain.MinerTx.UnlockTime),
InputCount: uint8(len(fullChainMain.MinerTx.Inputs)),
InputType: transaction.TxInGen,
GenHeight: fullChainMain.MinerTx.Inputs[0].Gen.Height,
Outputs: outputs,
OutputsBlobSize: 0,
TotalReward: totalReward,
Extra: extraTags,
ExtraBaseRCT: 0,
},
Transactions: fullChainMain.TxHashes,
TransactionParentIndices: nil,
}
c.HandleMainBlock(blockData)
case fullMinerData := <-s.FullMinerDataC: case fullMinerData := <-s.FullMinerDataC:
log.Print(fullMinerData) c.HandleMinerData(&p2pooltypes.MinerData{
MajorVersion: fullMinerData.MajorVersion,
Height: fullMinerData.Height,
PrevId: fullMinerData.PrevId,
SeedHash: fullMinerData.SeedHash,
Difficulty: fullMinerData.Difficulty,
MedianWeight: fullMinerData.MedianWeight,
AlreadyGeneratedCoins: fullMinerData.AlreadyGeneratedCoins,
MedianTimestamp: fullMinerData.MedianTimestamp,
TimeReceived: time.Now(),
})
} }
} }
} }
@ -249,6 +330,10 @@ func (c *MainChain) GetChainMainTip() *sidechain.ChainMain {
return c.tip.Load() return c.tip.Load()
} }
func (c *MainChain) GetMinerDataTip() *p2pooltypes.MinerData {
return c.tipMinerData.Load()
}
func (c *MainChain) updateTip() { func (c *MainChain) updateTip() {
if minerData := c.tipMinerData.Load(); minerData != nil { if minerData := c.tipMinerData.Load(); minerData != nil {
if d := c.GetChainMainByHash(minerData.PrevId); d != nil { if d := c.GetChainMainByHash(minerData.PrevId); d != nil {
@ -336,7 +421,7 @@ func (c *MainChain) DownloadBlockHeaders(currentHeight uint64) error {
return nil return nil
} }
func (c *MainChain) HandleMinerData(minerData *MinerData) { func (c *MainChain) HandleMinerData(minerData *p2pooltypes.MinerData) {
var missingHeights []uint64 var missingHeights []uint64
func() { func() {
c.lock.Lock() c.lock.Lock()
@ -364,10 +449,6 @@ func (c *MainChain) HandleMinerData(minerData *MinerData) {
} else { } else {
existingPrevMainData.Id = prevMainData.Id existingPrevMainData.Id = prevMainData.Id
// timestamp and reward is unknown here
existingPrevMainData.Timestamp = 0
existingPrevMainData.Reward = 0
prevMainData = existingPrevMainData prevMainData = existingPrevMainData
} }
@ -429,16 +510,3 @@ func (c *MainChain) getBlockHeader(height uint64) error {
return nil return nil
} }
type MinerData struct {
MajorVersion uint8
Height uint64
PrevId types.Hash
SeedHash types.Hash
Difficulty types.Difficulty
MedianWeight uint64
AlreadyGeneratedCoins uint64
MedianTimestamp uint64
//TxBacklog any
TimeReceived time.Time
}

View file

@ -394,14 +394,22 @@ func (c *Client) OnConnection() {
isChainTipBlockRequest := false isChainTipBlockRequest := false
if c.chainTipBlockRequest.Swap(false) { if c.chainTipBlockRequest.Swap(false) {
isChainTipBlockRequest = true isChainTipBlockRequest = true
//peerHeight := block.Main.Coinbase.GenHeight if expectedBlockId == types.ZeroHash {
//ourHeight := peerHeight := block.Main.Coinbase.GenHeight
ourHeight := c.Owner.MainChain().GetMinerDataTip().Height
if (peerHeight + 2) < ourHeight {
c.Ban(DefaultBanTime, fmt.Errorf("mining on top of a stale block (mainchain peer height %d, expected >= %d)", peerHeight, ourHeight))
return
}
}
//TODO: stale block //TODO: stale block
log.Printf("[P2PClient] Peer %s tip is at id = %s, height = %d, main height = %d", c.AddressPort.String(), types.HashFromBytes(block.CoinbaseExtra(sidechain.SideTemplateId)), block.Side.Height, block.Main.Coinbase.GenHeight) log.Printf("[P2PClient] Peer %s tip is at id = %s, height = %d, main height = %d", c.AddressPort.String(), types.HashFromBytes(block.CoinbaseExtra(sidechain.SideTemplateId)), block.Side.Height, block.Main.Coinbase.GenHeight)
if expectedBlockId != types.ZeroHash { if expectedBlockId != types.ZeroHash {
c.Ban(DefaultBanTime, fmt.Errorf("expected block id = %s, got %s", expectedBlockId, types.ZeroHash.String())) c.Ban(DefaultBanTime, fmt.Errorf("expected block id = %s, got %s", expectedBlockId, types.ZeroHash.String()))
return
} }
c.SendPeerListRequest() c.SendPeerListRequest()
@ -469,7 +477,32 @@ func (c *Client) OnConnection() {
//TODO: ban here, but sort blocks properly, maybe a queue to re-try? //TODO: ban here, but sort blocks properly, maybe a queue to re-try?
return return
} else { } else {
//TODO: investigate different monero block mining ourMinerData := c.Owner.MainChain().GetMinerDataTip()
if block.Main.PreviousId != ourMinerData.PrevId {
// This peer is mining on top of a different Monero block, investigate it
peerHeight := block.Main.Coinbase.GenHeight
ourHeight := ourMinerData.Height
if peerHeight < ourHeight {
if (ourHeight - peerHeight) < 5 {
elapsedTime := time.Now().Sub(ourMinerData.TimeReceived)
if (ourHeight-peerHeight) > 1 || elapsedTime > (time.Second*10) {
log.Printf("[P2PClient] Peer %s broadcasted a stale block (%d ms late, mainchain height %d, expected >= %d), ignoring it", c.AddressPort.String(), elapsedTime.Milliseconds(), peerHeight, ourHeight)
}
} else {
c.Ban(DefaultBanTime, fmt.Errorf("broadcasted an unreasonably stale block (mainchain height %d, expected >= %d)", peerHeight, ourHeight))
return
}
} else if peerHeight > ourHeight {
if peerHeight >= (ourHeight + 2) {
log.Printf("[P2PClient] Peer %s is ahead on mainchain (mainchain height %d, your height %d). Is monerod stuck or lagging?", c.AddressPort.String(), peerHeight, ourHeight)
}
} else {
log.Printf("[P2PClient] Peer %s is mining on an alternative mainchain tip (mainchain height %d, previous_id = %s)", c.AddressPort.String(), peerHeight, block.Main.PreviousId)
}
}
block.WantBroadcast.Store(true) block.WantBroadcast.Store(true)
if missingBlocks, err = c.Owner.SideChain().AddPoolBlockExternal(block); err != nil { if missingBlocks, err = c.Owner.SideChain().AddPoolBlockExternal(block); err != nil {

View file

@ -5,7 +5,9 @@ import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"errors" "errors"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mainchain"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types"
"git.gammaspectra.live/P2Pool/p2pool-observer/utils" "git.gammaspectra.live/P2Pool/p2pool-observer/utils"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"log" "log"
@ -18,8 +20,16 @@ import (
"unsafe" "unsafe"
) )
type P2PoolInterface interface {
sidechain.ConsensusProvider
SideChain() *sidechain.SideChain
MainChain() *mainchain.MainChain
GetChainMainTip() *sidechain.ChainMain
GetMinerDataTip() *p2pooltypes.MinerData
}
type Server struct { type Server struct {
sidechain *sidechain.SideChain p2pool P2PoolInterface
peerId uint64 peerId uint64
versionInformation PeerVersionInformation versionInformation PeerVersionInformation
@ -47,7 +57,7 @@ type Server struct {
ctx context.Context ctx context.Context
} }
func NewServer(sidechain *sidechain.SideChain, listenAddress string, externalListenPort uint16, maxOutgoingPeers, maxIncomingPeers uint32, ctx context.Context) (*Server, error) { func NewServer(p2pool P2PoolInterface, listenAddress string, externalListenPort uint16, maxOutgoingPeers, maxIncomingPeers uint32, ctx context.Context) (*Server, error) {
peerId := make([]byte, int(unsafe.Sizeof(uint64(0)))) peerId := make([]byte, int(unsafe.Sizeof(uint64(0))))
_, err := rand.Read(peerId) _, err := rand.Read(peerId)
if err != nil { if err != nil {
@ -60,7 +70,7 @@ func NewServer(sidechain *sidechain.SideChain, listenAddress string, externalLis
} }
s := &Server{ s := &Server{
sidechain: sidechain, p2pool: p2pool,
listenAddress: addrPort, listenAddress: addrPort,
externalListenPort: externalListenPort, externalListenPort: externalListenPort,
peerId: binary.LittleEndian.Uint64(peerId), peerId: binary.LittleEndian.Uint64(peerId),
@ -316,7 +326,11 @@ func (s *Server) PeerId() uint64 {
} }
func (s *Server) SideChain() *sidechain.SideChain { func (s *Server) SideChain() *sidechain.SideChain {
return s.sidechain return s.p2pool.SideChain()
}
func (s *Server) MainChain() *mainchain.MainChain {
return s.p2pool.MainChain()
} }
func (s *Server) updateClients() { func (s *Server) updateClients() {
@ -384,5 +398,5 @@ func (s *Server) Broadcast(block *sidechain.PoolBlock) {
} }
func (s *Server) Consensus() *sidechain.Consensus { func (s *Server) Consensus() *sidechain.Consensus {
return s.sidechain.Consensus() return s.p2pool.Consensus()
} }

View file

@ -3,7 +3,6 @@ package p2pool
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
@ -13,6 +12,7 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mainchain" "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/mainchain"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/p2p" "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/p2p"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" "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/types"
"log" "log"
"strconv" "strconv"
@ -95,7 +95,7 @@ func NewP2Pool(consensus *sidechain.Consensus, settings map[string]string) (*P2P
externalListenPort, _ = strconv.ParseUint(externalPort, 10, 0) externalListenPort, _ = strconv.ParseUint(externalPort, 10, 0)
} }
if pool.server, err = p2p.NewServer(pool.sidechain, listenAddress, uint16(externalListenPort), uint32(maxOutgoingPeers), uint32(maxIncomingPeers), pool.ctx); err != nil { if pool.server, err = p2p.NewServer(pool, listenAddress, uint16(externalListenPort), uint32(maxOutgoingPeers), uint32(maxIncomingPeers), pool.ctx); err != nil {
return nil, err return nil, err
} }
@ -114,8 +114,13 @@ func (p *P2Pool) GetChainMainTip() *sidechain.ChainMain {
return p.mainchain.GetChainMainTip() return p.mainchain.GetChainMainTip()
} }
func (p *P2Pool) GetMinerDataTip() *p2pooltypes.MinerData {
return p.mainchain.GetMinerDataTip()
}
// GetMinimalBlockHeaderByHeight Only Id / Height / Timestamp are assured // GetMinimalBlockHeaderByHeight Only Id / Height / Timestamp are assured
func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header { 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 { if chainMain := p.mainchain.GetChainMainByHeight(height); chainMain != nil && chainMain.Id != types.ZeroHash {
return &block.Header{ return &block.Header{
Timestamp: chainMain.Timestamp, Timestamp: chainMain.Timestamp,
@ -124,7 +129,7 @@ func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header {
Difficulty: chainMain.Difficulty, Difficulty: chainMain.Difficulty,
Id: chainMain.Id, Id: chainMain.Id,
} }
} else { } else if lowerThanTip {
if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil { if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil {
return nil return nil
} else { } else {
@ -146,11 +151,14 @@ func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header {
return blockHeader return blockHeader
} }
} }
return nil
} }
func (p *P2Pool) GetDifficultyByHeight(height uint64) types.Difficulty { 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 { if chainMain := p.mainchain.GetChainMainByHeight(height); chainMain != nil && chainMain.Difficulty != types.ZeroDifficulty {
return chainMain.Difficulty return chainMain.Difficulty
} else { } else if lowerThanTip {
//TODO cache //TODO cache
if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil { if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil {
return types.ZeroDifficulty return types.ZeroDifficulty
@ -173,6 +181,8 @@ func (p *P2Pool) GetDifficultyByHeight(height uint64) types.Difficulty {
return blockHeader.Difficulty return blockHeader.Difficulty
} }
} }
return types.ZeroDifficulty
} }
// GetMinimalBlockHeaderByHash Only Id / Height / Timestamp are assured // GetMinimalBlockHeaderByHash Only Id / Height / Timestamp are assured
@ -221,6 +231,14 @@ func (p *P2Pool) Context() context.Context {
return p.ctx 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 { func (p *P2Pool) Server() *p2p.Server {
return p.server return p.server
} }
@ -241,106 +259,11 @@ func (p *P2Pool) Run() (err error) {
return err return err
} }
if zmqStream, err := p.ClientZMQ().Listen(p.ctx); err != nil { go func() {
return err if p.mainchain.Listen() != nil {
} else { p.Close()
go func() { }
for { }()
select {
case <-zmqStream.ErrC:
p.Close()
return
case fullChainMain := <-zmqStream.FullChainMainC:
if len(fullChainMain.MinerTx.Inputs) < 1 {
continue
}
d := &sidechain.ChainMain{
Difficulty: types.ZeroDifficulty,
Height: fullChainMain.MinerTx.Inputs[0].Gen.Height,
Timestamp: uint64(fullChainMain.Timestamp),
Reward: 0,
Id: types.ZeroHash,
}
for _, o := range fullChainMain.MinerTx.Outputs {
d.Reward += o.Amount
}
outputs := make(transaction.Outputs, 0, len(fullChainMain.MinerTx.Outputs))
var totalReward uint64
for i, o := range fullChainMain.MinerTx.Outputs {
if o.ToKey != nil {
outputs = append(outputs, transaction.Output{
Index: uint64(i),
Reward: o.Amount,
Type: transaction.TxOutToKey,
EphemeralPublicKey: o.ToKey.Key,
ViewTag: 0,
})
} else if o.ToTaggedKey != nil {
tk, _ := hex.DecodeString(o.ToTaggedKey.ViewTag)
outputs = append(outputs, transaction.Output{
Index: uint64(i),
Reward: o.Amount,
Type: transaction.TxOutToTaggedKey,
EphemeralPublicKey: o.ToTaggedKey.Key,
ViewTag: tk[0],
})
} else {
//error
break
}
totalReward += o.Amount
}
if len(outputs) != len(fullChainMain.MinerTx.Outputs) {
continue
}
extraDataRaw, _ := hex.DecodeString(fullChainMain.MinerTx.Extra)
extraTags := transaction.ExtraTags{}
if err = extraTags.UnmarshalBinary(extraDataRaw); err != nil {
continue
}
blockData := &block.Block{
MajorVersion: uint8(fullChainMain.MajorVersion),
MinorVersion: uint8(fullChainMain.MinorVersion),
Timestamp: uint64(fullChainMain.Timestamp),
PreviousId: fullChainMain.PrevID,
Nonce: uint32(fullChainMain.Nonce),
Coinbase: &transaction.CoinbaseTransaction{
Version: uint8(fullChainMain.MinerTx.Version),
UnlockTime: uint64(fullChainMain.MinerTx.UnlockTime),
InputCount: uint8(len(fullChainMain.MinerTx.Inputs)),
InputType: transaction.TxInGen,
GenHeight: fullChainMain.MinerTx.Inputs[0].Gen.Height,
Outputs: outputs,
OutputsBlobSize: 0,
TotalReward: totalReward,
Extra: extraTags,
ExtraBaseRCT: 0,
},
Transactions: fullChainMain.TxHashes,
TransactionParentIndices: nil,
}
p.mainchain.HandleMainBlock(blockData)
case fullMinerData := <-zmqStream.FullMinerDataC:
p.mainchain.HandleMinerData(&mainchain.MinerData{
MajorVersion: fullMinerData.MajorVersion,
Height: fullMinerData.Height,
PrevId: fullMinerData.PrevId,
SeedHash: fullMinerData.SeedHash,
Difficulty: fullMinerData.Difficulty,
MedianWeight: fullMinerData.MedianWeight,
AlreadyGeneratedCoins: fullMinerData.AlreadyGeneratedCoins,
MedianTimestamp: fullMinerData.MedianTimestamp,
TimeReceived: time.Now(),
})
}
}
}()
}
p.started.Store(true) p.started.Store(true)
@ -405,7 +328,7 @@ func (p *P2Pool) getMinerData() error {
prevId, _ := types.HashFromString(minerData.PrevId) prevId, _ := types.HashFromString(minerData.PrevId)
seedHash, _ := types.HashFromString(minerData.SeedHash) seedHash, _ := types.HashFromString(minerData.SeedHash)
diff, _ := types.DifficultyFromString(minerData.Difficulty) diff, _ := types.DifficultyFromString(minerData.Difficulty)
p.mainchain.HandleMinerData(&mainchain.MinerData{ p.mainchain.HandleMinerData(&p2pooltypes.MinerData{
MajorVersion: minerData.MajorVersion, MajorVersion: minerData.MajorVersion,
Height: minerData.Height, Height: minerData.Height,
PrevId: prevId, PrevId: prevId,
@ -463,5 +386,16 @@ func (p *P2Pool) Started() bool {
} }
func (p *P2Pool) Broadcast(block *sidechain.PoolBlock) { func (p *P2Pool) Broadcast(block *sidechain.PoolBlock) {
minerData := p.GetMinerDataTip()
if (block.Main.Coinbase.GenHeight)+2 < minerData.Height {
log.Printf("[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) {
log.Printf("[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.server.Broadcast(block)
} }

View file

@ -13,10 +13,10 @@ import (
const BlockSaveEpochSize = 32 const BlockSaveEpochSize = 32
const ( const (
BlockSaveOptionTemplate = 1 << 0 BlockSaveOptionTemplate = 1 << 0
//BlockSaveOptionDeterministicPrivateKey = 1 << 1 BlockSaveOptionDeterministicPrivateKeySeed = 1 << 1
BlockSaveOptionDeterministicBlobs = 1 << 2 BlockSaveOptionDeterministicBlobs = 1 << 2
BlockSaveOptionUncles = 1 << 3 BlockSaveOptionUncles = 1 << 3
BlockSaveFieldSizeInBits = 8 BlockSaveFieldSizeInBits = 8
@ -60,9 +60,6 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
c.sidechainLock.RLock() c.sidechainLock.RLock()
defer c.sidechainLock.RUnlock() defer c.sidechainLock.RUnlock()
//only store keys when not deterministic
//isDeterministicPrivateKey := c.isPoolBlockTransactionKeyIsDeterministic(block)
calculatedOutputs := c.calculateOutputs(block) calculatedOutputs := c.calculateOutputs(block)
calcBlob, _ := calculatedOutputs.MarshalBinary() calcBlob, _ := calculatedOutputs.MarshalBinary()
blockBlob, _ := block.Main.Coinbase.Outputs.MarshalBinary() blockBlob, _ := block.Main.Coinbase.Outputs.MarshalBinary()
@ -76,13 +73,26 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
parent := c.getParent(block) parent := c.getParent(block)
//only store keys when not deterministic
isDeterministicPrivateKeySeed := parent != nil && c.isPoolBlockTransactionKeyIsDeterministic(block)
if isDeterministicPrivateKeySeed && block.ShareVersion() > ShareVersion_V1 {
expectedSeed := parent.Side.CoinbasePrivateKeySeed
if parent.Main.PreviousId != block.Main.PreviousId {
expectedSeed = parent.CalculateTransactionPrivateKeySeed()
}
if block.Side.CoinbasePrivateKeySeed != expectedSeed {
isDeterministicPrivateKeySeed = false
}
}
blob := make([]byte, 0, 4096*2) blob := make([]byte, 0, 4096*2)
var blockFlags uint64 var blockFlags uint64
/*if isDeterministicPrivateKey { if isDeterministicPrivateKeySeed {
blockFlags |= BlockSaveOptionDeterministicPrivateKey blockFlags |= BlockSaveOptionDeterministicPrivateKeySeed
}*/ }
if !storeBlob { if !storeBlob {
blockFlags |= BlockSaveOptionDeterministicBlobs blockFlags |= BlockSaveOptionDeterministicBlobs
@ -163,8 +173,8 @@ func (c *SideChain) saveBlock(block *PoolBlock) {
blob = binary.AppendUvarint(blob, minerAddressOffset) blob = binary.AppendUvarint(blob, minerAddressOffset)
} }
// private key, if needed // private key seed, if needed
if true /*(blockFlags & BlockSaveOptionDeterministicPrivateKey) == 0*/ { if (blockFlags&BlockSaveOptionDeterministicPrivateKeySeed) == 0 || (block.ShareVersion() > ShareVersion_V1 && (blockFlags&BlockSaveOptionTemplate) != 0) {
blob = append(blob, block.Side.CoinbasePrivateKeySeed[:]...) blob = append(blob, block.Side.CoinbasePrivateKeySeed[:]...)
//public may be needed on invalid - TODO check //public may be needed on invalid - TODO check
//blob = append(blob, block.CoinbaseExtra(SideCoinbasePublicKey)...) //blob = append(blob, block.CoinbaseExtra(SideCoinbasePublicKey)...)

View file

@ -12,6 +12,7 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto" "git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction" "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/types"
"git.gammaspectra.live/P2Pool/p2pool-observer/utils" "git.gammaspectra.live/P2Pool/p2pool-observer/utils"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
@ -42,6 +43,7 @@ type P2PoolInterface interface {
UpdateBlockFound(data *ChainMain, block *PoolBlock) UpdateBlockFound(data *ChainMain, block *PoolBlock)
SubmitBlock(block *mainblock.Block) SubmitBlock(block *mainblock.Block)
GetChainMainTip() *ChainMain GetChainMainTip() *ChainMain
GetMinerDataTip() *p2pooltypes.MinerData
} }
type ChainMain struct { type ChainMain struct {

19
p2pool/types/minerdata.go Normal file
View file

@ -0,0 +1,19 @@
package types
import (
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"time"
)
type MinerData struct {
MajorVersion uint8
Height uint64
PrevId types.Hash
SeedHash types.Hash
Difficulty types.Difficulty
MedianWeight uint64
AlreadyGeneratedCoins uint64
MedianTimestamp uint64
//TxBacklog any
TimeReceived time.Time
}