From d59a78c25d3f4a7779dd9eb06552a79b169b149a Mon Sep 17 00:00:00 2001 From: WeebDataHoarder <57538841+WeebDataHoarder@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:18:42 +0100 Subject: [PATCH] Check miner data on client block response and block broadcast --- p2pool/mainchain/mainchain.go | 118 ++++++++++++++++++++++------ p2pool/p2p/client.go | 39 ++++++++- p2pool/p2p/server.go | 24 ++++-- p2pool/p2pool.go | 144 +++++++++------------------------- p2pool/sidechain/blobcache.go | 34 +++++--- p2pool/sidechain/sidechain.go | 2 + p2pool/types/minerdata.go | 19 +++++ 7 files changed, 230 insertions(+), 150 deletions(-) create mode 100644 p2pool/types/minerdata.go diff --git a/p2pool/mainchain/mainchain.go b/p2pool/mainchain/mainchain.go index 44b6cb6..d4e5916 100644 --- a/p2pool/mainchain/mainchain.go +++ b/p2pool/mainchain/mainchain.go @@ -2,6 +2,7 @@ package mainchain import ( "context" + "encoding/hex" "fmt" mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" "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/transaction" "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" "golang.org/x/exp/slices" @@ -31,7 +33,7 @@ type MainChain struct { mainchainByHash map[types.Hash]*sidechain.ChainMain tip atomic.Pointer[sidechain.ChainMain] - tipMinerData atomic.Pointer[MinerData] + tipMinerData atomic.Pointer[p2pooltypes.MinerData] medianTimestamp atomic.Uint64 } @@ -64,15 +66,94 @@ func (c *MainChain) Listen() error { for { select { - case <-ctx.Done(): + case <-s.ErrC: return ctx.Err() - case err := <-s.ErrC: - //TODO: retry connection - return err 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: - 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() } +func (c *MainChain) GetMinerDataTip() *p2pooltypes.MinerData { + return c.tipMinerData.Load() +} + func (c *MainChain) updateTip() { if minerData := c.tipMinerData.Load(); minerData != nil { if d := c.GetChainMainByHash(minerData.PrevId); d != nil { @@ -336,7 +421,7 @@ func (c *MainChain) DownloadBlockHeaders(currentHeight uint64) error { return nil } -func (c *MainChain) HandleMinerData(minerData *MinerData) { +func (c *MainChain) HandleMinerData(minerData *p2pooltypes.MinerData) { var missingHeights []uint64 func() { c.lock.Lock() @@ -364,10 +449,6 @@ func (c *MainChain) HandleMinerData(minerData *MinerData) { } else { existingPrevMainData.Id = prevMainData.Id - // timestamp and reward is unknown here - existingPrevMainData.Timestamp = 0 - existingPrevMainData.Reward = 0 - prevMainData = existingPrevMainData } @@ -429,16 +510,3 @@ func (c *MainChain) getBlockHeader(height uint64) error { 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 -} diff --git a/p2pool/p2p/client.go b/p2pool/p2p/client.go index 3af3908..f6013a1 100644 --- a/p2pool/p2p/client.go +++ b/p2pool/p2p/client.go @@ -394,14 +394,22 @@ func (c *Client) OnConnection() { isChainTipBlockRequest := false if c.chainTipBlockRequest.Swap(false) { isChainTipBlockRequest = true - //peerHeight := block.Main.Coinbase.GenHeight - //ourHeight := + if expectedBlockId == types.ZeroHash { + 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 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 { c.Ban(DefaultBanTime, fmt.Errorf("expected block id = %s, got %s", expectedBlockId, types.ZeroHash.String())) + return } c.SendPeerListRequest() @@ -469,7 +477,32 @@ func (c *Client) OnConnection() { //TODO: ban here, but sort blocks properly, maybe a queue to re-try? return } 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) if missingBlocks, err = c.Owner.SideChain().AddPoolBlockExternal(block); err != nil { diff --git a/p2pool/p2p/server.go b/p2pool/p2p/server.go index e1d2ba2..131319f 100644 --- a/p2pool/p2p/server.go +++ b/p2pool/p2p/server.go @@ -5,7 +5,9 @@ import ( "crypto/rand" "encoding/binary" "errors" + "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/utils" "golang.org/x/exp/slices" "log" @@ -18,8 +20,16 @@ import ( "unsafe" ) +type P2PoolInterface interface { + sidechain.ConsensusProvider + SideChain() *sidechain.SideChain + MainChain() *mainchain.MainChain + GetChainMainTip() *sidechain.ChainMain + GetMinerDataTip() *p2pooltypes.MinerData +} + type Server struct { - sidechain *sidechain.SideChain + p2pool P2PoolInterface peerId uint64 versionInformation PeerVersionInformation @@ -47,7 +57,7 @@ type Server struct { 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)))) _, err := rand.Read(peerId) if err != nil { @@ -60,7 +70,7 @@ func NewServer(sidechain *sidechain.SideChain, listenAddress string, externalLis } s := &Server{ - sidechain: sidechain, + p2pool: p2pool, listenAddress: addrPort, externalListenPort: externalListenPort, peerId: binary.LittleEndian.Uint64(peerId), @@ -316,7 +326,11 @@ func (s *Server) PeerId() uint64 { } 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() { @@ -384,5 +398,5 @@ func (s *Server) Broadcast(block *sidechain.PoolBlock) { } func (s *Server) Consensus() *sidechain.Consensus { - return s.sidechain.Consensus() + return s.p2pool.Consensus() } diff --git a/p2pool/p2pool.go b/p2pool/p2pool.go index 9e94238..59f7a47 100644 --- a/p2pool/p2pool.go +++ b/p2pool/p2pool.go @@ -3,7 +3,6 @@ package p2pool import ( "context" "encoding/binary" - "encoding/hex" "errors" "fmt" "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/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" "log" "strconv" @@ -95,7 +95,7 @@ func NewP2Pool(consensus *sidechain.Consensus, settings map[string]string) (*P2P 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 } @@ -114,8 +114,13 @@ 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 { return &block.Header{ Timestamp: chainMain.Timestamp, @@ -124,7 +129,7 @@ func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header { Difficulty: chainMain.Difficulty, Id: chainMain.Id, } - } else { + } else if lowerThanTip { if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil { return nil } else { @@ -146,11 +151,14 @@ func (p *P2Pool) GetMinimalBlockHeaderByHeight(height uint64) *block.Header { 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 { + } else if lowerThanTip { //TODO cache if header, err := p.ClientRPC().GetBlockHeaderByHeight(height, p.ctx); err != nil { return types.ZeroDifficulty @@ -173,6 +181,8 @@ func (p *P2Pool) GetDifficultyByHeight(height uint64) types.Difficulty { return blockHeader.Difficulty } } + + return types.ZeroDifficulty } // GetMinimalBlockHeaderByHash Only Id / Height / Timestamp are assured @@ -221,6 +231,14 @@ 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 } @@ -241,106 +259,11 @@ func (p *P2Pool) Run() (err error) { return err } - if zmqStream, err := p.ClientZMQ().Listen(p.ctx); err != nil { - return err - } else { - 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(), - }) - } - } - }() - - } + go func() { + if p.mainchain.Listen() != nil { + p.Close() + } + }() p.started.Store(true) @@ -405,7 +328,7 @@ func (p *P2Pool) getMinerData() error { prevId, _ := types.HashFromString(minerData.PrevId) seedHash, _ := types.HashFromString(minerData.SeedHash) diff, _ := types.DifficultyFromString(minerData.Difficulty) - p.mainchain.HandleMinerData(&mainchain.MinerData{ + p.mainchain.HandleMinerData(&p2pooltypes.MinerData{ MajorVersion: minerData.MajorVersion, Height: minerData.Height, PrevId: prevId, @@ -463,5 +386,16 @@ func (p *P2Pool) Started() bool { } 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) } diff --git a/p2pool/sidechain/blobcache.go b/p2pool/sidechain/blobcache.go index dcf95d0..45568a0 100644 --- a/p2pool/sidechain/blobcache.go +++ b/p2pool/sidechain/blobcache.go @@ -13,10 +13,10 @@ import ( const BlockSaveEpochSize = 32 const ( - BlockSaveOptionTemplate = 1 << 0 - //BlockSaveOptionDeterministicPrivateKey = 1 << 1 - BlockSaveOptionDeterministicBlobs = 1 << 2 - BlockSaveOptionUncles = 1 << 3 + BlockSaveOptionTemplate = 1 << 0 + BlockSaveOptionDeterministicPrivateKeySeed = 1 << 1 + BlockSaveOptionDeterministicBlobs = 1 << 2 + BlockSaveOptionUncles = 1 << 3 BlockSaveFieldSizeInBits = 8 @@ -60,9 +60,6 @@ func (c *SideChain) saveBlock(block *PoolBlock) { c.sidechainLock.RLock() defer c.sidechainLock.RUnlock() - //only store keys when not deterministic - //isDeterministicPrivateKey := c.isPoolBlockTransactionKeyIsDeterministic(block) - calculatedOutputs := c.calculateOutputs(block) calcBlob, _ := calculatedOutputs.MarshalBinary() blockBlob, _ := block.Main.Coinbase.Outputs.MarshalBinary() @@ -76,13 +73,26 @@ func (c *SideChain) saveBlock(block *PoolBlock) { 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) var blockFlags uint64 - /*if isDeterministicPrivateKey { - blockFlags |= BlockSaveOptionDeterministicPrivateKey - }*/ + if isDeterministicPrivateKeySeed { + blockFlags |= BlockSaveOptionDeterministicPrivateKeySeed + } if !storeBlob { blockFlags |= BlockSaveOptionDeterministicBlobs @@ -163,8 +173,8 @@ func (c *SideChain) saveBlock(block *PoolBlock) { blob = binary.AppendUvarint(blob, minerAddressOffset) } - // private key, if needed - if true /*(blockFlags & BlockSaveOptionDeterministicPrivateKey) == 0*/ { + // private key seed, if needed + if (blockFlags&BlockSaveOptionDeterministicPrivateKeySeed) == 0 || (block.ShareVersion() > ShareVersion_V1 && (blockFlags&BlockSaveOptionTemplate) != 0) { blob = append(blob, block.Side.CoinbasePrivateKeySeed[:]...) //public may be needed on invalid - TODO check //blob = append(blob, block.CoinbaseExtra(SideCoinbasePublicKey)...) diff --git a/p2pool/sidechain/sidechain.go b/p2pool/sidechain/sidechain.go index bf34bf6..b4c66a9 100644 --- a/p2pool/sidechain/sidechain.go +++ b/p2pool/sidechain/sidechain.go @@ -12,6 +12,7 @@ import ( "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" "golang.org/x/exp/slices" @@ -42,6 +43,7 @@ type P2PoolInterface interface { UpdateBlockFound(data *ChainMain, block *PoolBlock) SubmitBlock(block *mainblock.Block) GetChainMainTip() *ChainMain + GetMinerDataTip() *p2pooltypes.MinerData } type ChainMain struct { diff --git a/p2pool/types/minerdata.go b/p2pool/types/minerdata.go new file mode 100644 index 0000000..b496d46 --- /dev/null +++ b/p2pool/types/minerdata.go @@ -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 +}