consensus/p2pool/sidechain/poolblock.go

318 lines
7.3 KiB
Go
Raw Normal View History

2022-10-26 16:46:41 +00:00
package sidechain
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"git.gammaspectra.live/P2Pool/moneroutil"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
mainblock "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/transaction"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"github.com/holiman/uint256"
"io"
"math/bits"
"sync"
)
type CoinbaseExtraTag int
const SideExtraNonceSize = 4
const SideExtraNonceMaxSize = SideExtraNonceSize + 10
const (
SideCoinbasePublicKey = transaction.TxExtraTagPubKey
SideExtraNonce = transaction.TxExtraTagNonce
SideTemplateId = transaction.TxExtraTagMergeMining
2022-10-26 16:46:41 +00:00
)
type PoolBlock struct {
2022-10-26 16:46:41 +00:00
Main mainblock.Block
Side SideData
c struct {
lock sync.RWMutex
mainId types.Hash
mainDifficulty types.Difficulty
templateId types.Hash
powHash types.Hash
2022-10-26 16:46:41 +00:00
}
}
func NewShareFromExportedBytes(buf []byte) (*PoolBlock, error) {
b := &PoolBlock{}
2022-10-26 16:46:41 +00:00
if len(buf) < 32 {
return nil, errors.New("invalid block data")
2022-10-26 16:46:41 +00:00
}
reader := bytes.NewReader(buf)
2022-10-26 16:46:41 +00:00
var (
err error
version uint64
mainDataSize uint64
mainData []byte
sideDataSize uint64
sideData []byte
)
if err = binary.Read(reader, binary.BigEndian, &version); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
switch version {
case 1:
if _, err = io.ReadFull(reader, b.c.mainId[:]); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if _, err = io.ReadFull(reader, b.c.powHash[:]); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if err = binary.Read(reader, binary.BigEndian, &b.c.mainDifficulty.Hi); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if err = binary.Read(reader, binary.BigEndian, &b.c.mainDifficulty.Lo); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
b.c.mainDifficulty.ReverseBytes()
2022-10-26 16:46:41 +00:00
if err = binary.Read(reader, binary.BigEndian, &mainDataSize); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
mainData = make([]byte, mainDataSize)
if _, err = io.ReadFull(reader, mainData); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if err = binary.Read(reader, binary.BigEndian, &sideDataSize); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
sideData = make([]byte, sideDataSize)
if _, err = io.ReadFull(reader, sideData); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
/*
//Ignore error when unable to read peer
_ = func() error {
var peerSize uint64
2022-10-26 16:46:41 +00:00
if err = binary.Read(reader, binary.BigEndian, &peerSize); err != nil {
return err
}
b.Extra.Peer = make([]byte, peerSize)
if _, err = io.ReadFull(reader, b.Extra.Peer); err != nil {
return err
}
2022-10-26 16:46:41 +00:00
return nil
}()
*/
2022-10-26 16:46:41 +00:00
case 0:
if err = binary.Read(reader, binary.BigEndian, &mainDataSize); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
mainData = make([]byte, mainDataSize)
if _, err = io.ReadFull(reader, mainData); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if sideData, err = io.ReadAll(reader); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
default:
return nil, fmt.Errorf("unknown block version %d", version)
2022-10-26 16:46:41 +00:00
}
if err = b.Main.UnmarshalBinary(mainData); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
if err = b.Side.UnmarshalBinary(sideData); err != nil {
return nil, err
2022-10-26 16:46:41 +00:00
}
b.c.templateId = types.HashFromBytes(b.CoinbaseExtra(SideTemplateId))
return b, nil
}
func (b *PoolBlock) CoinbaseExtra(tag CoinbaseExtraTag) []byte {
switch tag {
case SideExtraNonce:
if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil {
if len(t.Data) < SideExtraNonceSize || len(t.Data) > SideExtraNonceMaxSize {
return nil
}
return t.Data
}
case SideTemplateId:
if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil {
if len(t.Data) != types.HashSize {
return nil
}
return t.Data
}
case SideCoinbasePublicKey:
if t := b.Main.Coinbase.Extra.GetTag(uint8(tag)); t != nil {
if len(t.Data) != types.HashSize {
return nil
}
return t.Data
}
2022-10-26 16:46:41 +00:00
}
return nil
}
func (b *PoolBlock) MainId() types.Hash {
if hash, ok := func() (types.Hash, bool) {
b.c.lock.RLock()
defer b.c.lock.RUnlock()
2022-10-26 16:46:41 +00:00
if b.c.mainId != types.ZeroHash {
return b.c.mainId, true
2022-10-26 16:46:41 +00:00
}
return types.ZeroHash, false
}(); ok {
return hash
} else {
b.c.lock.Lock()
defer b.c.lock.Unlock()
if b.c.mainId == types.ZeroHash { //check again for race
b.c.mainId = b.Main.Id()
}
return b.c.mainId
2022-10-26 16:46:41 +00:00
}
}
func (b *PoolBlock) MainDifficulty() types.Difficulty {
if difficulty, ok := func() (types.Difficulty, bool) {
b.c.lock.RLock()
defer b.c.lock.RUnlock()
if b.c.mainDifficulty != types.ZeroDifficulty {
return b.c.mainDifficulty, true
}
return types.ZeroDifficulty, false
}(); ok {
return difficulty
} else {
b.c.lock.Lock()
defer b.c.lock.Unlock()
if b.c.mainDifficulty == types.ZeroDifficulty { //check again for race
b.c.mainDifficulty = b.Main.Difficulty()
}
return b.c.mainDifficulty
}
}
func (b *PoolBlock) SideTemplateId(consensus *Consensus) types.Hash {
if hash, ok := func() (types.Hash, bool) {
b.c.lock.RLock()
defer b.c.lock.RUnlock()
if b.c.templateId != types.ZeroHash {
return b.c.templateId, true
}
return types.ZeroHash, false
}(); ok {
return hash
} else {
b.c.lock.Lock()
defer b.c.lock.Unlock()
if b.c.templateId == types.ZeroHash { //check again for race
b.c.templateId = consensus.CalculateSideTemplateId(&b.Main, &b.Side)
}
return b.c.templateId
}
}
func (b *PoolBlock) PowHash() types.Hash {
if hash, ok := func() (types.Hash, bool) {
b.c.lock.RLock()
defer b.c.lock.RUnlock()
if b.c.powHash != types.ZeroHash {
return b.c.powHash, true
}
return types.ZeroHash, false
}(); ok {
return hash
} else {
b.c.lock.Lock()
defer b.c.lock.Unlock()
if b.c.powHash == types.ZeroHash { //check again for race
b.c.powHash = b.Main.PowHash()
}
return b.c.powHash
}
}
func (b *PoolBlock) UnmarshalBinary(data []byte) error {
reader := bytes.NewReader(data)
return b.FromReader(reader)
}
func (b *PoolBlock) MarshalBinary() ([]byte, error) {
if mainData, err := b.Main.MarshalBinary(); err != nil {
return nil, err
} else if sideData, err := b.Side.MarshalBinary(); err != nil {
return nil, err
} else {
data := make([]byte, 0, len(mainData)+len(sideData))
data = append(data, mainData...)
data = append(data, sideData...)
return data, nil
}
}
func (b *PoolBlock) FromReader(reader readerAndByteReader) (err error) {
if err = b.Main.FromReader(reader); err != nil {
return err
}
if err = b.Side.FromReader(reader); err != nil {
return err
}
2022-10-26 16:46:41 +00:00
return nil
}
func (b *PoolBlock) IsProofHigherThanDifficulty() bool {
if mainDifficulty := b.MainDifficulty(); mainDifficulty == types.ZeroDifficulty {
//TODO: err
return false
} else {
return b.GetProofDifficulty().Cmp(mainDifficulty) >= 0
}
2022-10-26 16:46:41 +00:00
}
func (b *PoolBlock) GetProofDifficulty() types.Difficulty {
2022-10-26 16:46:41 +00:00
base := uint256.NewInt(0).SetBytes32(bytes.Repeat([]byte{0xff}, 32))
powHash := b.PowHash()
pow := uint256.NewInt(0).SetBytes32(powHash[:])
2022-10-26 16:46:41 +00:00
pow = &uint256.Int{bits.ReverseBytes64(pow[3]), bits.ReverseBytes64(pow[2]), bits.ReverseBytes64(pow[1]), bits.ReverseBytes64(pow[0])}
if pow.Eq(uint256.NewInt(0)) {
return types.Difficulty{}
}
powResult := uint256.NewInt(0).Div(base, pow).Bytes32()
return types.DifficultyFromBytes(powResult[16:])
2022-10-26 16:46:41 +00:00
}
func (b *PoolBlock) GetAddress() *address.Address {
2022-10-26 16:46:41 +00:00
return address.FromRawAddress(moneroutil.MainNetwork, b.Side.PublicSpendKey, b.Side.PublicViewKey)
}