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"
|
2022-10-28 09:47:14 +00:00
|
|
|
"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
|
|
|
)
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
type PoolBlock struct {
|
2022-10-26 16:46:41 +00:00
|
|
|
Main mainblock.Block
|
|
|
|
|
|
|
|
Side SideData
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func NewShareFromExportedBytes(buf []byte) (*PoolBlock, error) {
|
|
|
|
b := &PoolBlock{}
|
2022-10-26 16:46:41 +00:00
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
if len(buf) < 32 {
|
|
|
|
return nil, errors.New("invalid block data")
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 09:47:14 +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 {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch version {
|
|
|
|
case 1:
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
if _, err = io.ReadFull(reader, b.c.mainId[:]); err != nil {
|
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
if _, err = io.ReadFull(reader, b.c.powHash[:]); err != nil {
|
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
2022-10-28 09:47:14 +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
|
|
|
}
|
2022-10-28 09:47:14 +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
|
|
|
}
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
b.c.mainDifficulty.ReverseBytes()
|
2022-10-26 16:46:41 +00:00
|
|
|
|
|
|
|
if err = binary.Read(reader, binary.BigEndian, &mainDataSize); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
mainData = make([]byte, mainDataSize)
|
|
|
|
if _, err = io.ReadFull(reader, mainData); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err = binary.Read(reader, binary.BigEndian, &sideDataSize); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
sideData = make([]byte, sideDataSize)
|
|
|
|
if _, err = io.ReadFull(reader, sideData); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
/*
|
|
|
|
//Ignore error when unable to read peer
|
|
|
|
_ = func() error {
|
|
|
|
var peerSize uint64
|
2022-10-26 16:46:41 +00:00
|
|
|
|
2022-10-28 09:47:14 +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
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
*/
|
2022-10-26 16:46:41 +00:00
|
|
|
|
|
|
|
case 0:
|
|
|
|
if err = binary.Read(reader, binary.BigEndian, &mainDataSize); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
mainData = make([]byte, mainDataSize)
|
|
|
|
if _, err = io.ReadFull(reader, mainData); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
if sideData, err = io.ReadAll(reader); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
default:
|
2022-10-28 09:47:14 +00:00
|
|
|
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 {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err = b.Side.UnmarshalBinary(sideData); err != nil {
|
2022-10-28 09:47:14 +00:00
|
|
|
return nil, err
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
b.c.templateId = types.HashFromBytes(b.CoinbaseExtra(SideTemplateId))
|
|
|
|
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) CoinbaseExtra(tag CoinbaseExtraTag) []byte {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) MainId() types.Hash {
|
2022-10-28 09:47:14 +00:00
|
|
|
if hash, ok := func() (types.Hash, bool) {
|
|
|
|
b.c.lock.RLock()
|
|
|
|
defer b.c.lock.RUnlock()
|
2022-10-26 16:46:41 +00:00
|
|
|
|
2022-10-28 09:47:14 +00:00
|
|
|
if b.c.mainId != types.ZeroHash {
|
|
|
|
return b.c.mainId, true
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
2022-10-28 09:47:14 +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
|
|
|
}
|
2022-10-28 09:47:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) MainDifficulty() types.Difficulty {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) SideTemplateId(consensus *Consensus) types.Hash {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) PowHash() types.Hash {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) UnmarshalBinary(data []byte) error {
|
2022-10-28 09:47:14 +00:00
|
|
|
reader := bytes.NewReader(data)
|
|
|
|
return b.FromReader(reader)
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) MarshalBinary() ([]byte, error) {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) FromReader(reader readerAndByteReader) (err error) {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +00:00
|
|
|
func (b *PoolBlock) IsProofHigherThanDifficulty() bool {
|
2022-10-28 09:47:14 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +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))
|
2022-10-28 09:47:14 +00:00
|
|
|
|
|
|
|
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()
|
2022-10-28 09:47:14 +00:00
|
|
|
return types.DifficultyFromBytes(powResult[16:])
|
2022-10-26 16:46:41 +00:00
|
|
|
}
|
|
|
|
|
2022-11-01 11:22:00 +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)
|
|
|
|
}
|