Check miner data on client block response and block broadcast
This commit is contained in:
parent
968b07ae0b
commit
d59a78c25d
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
144
p2pool/p2pool.go
144
p2pool/p2pool.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)...)
|
||||||
|
|
|
@ -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
19
p2pool/types/minerdata.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue