consensus/p2pool/sidechain/consensus.go

223 lines
5.3 KiB
Go

package sidechain
import (
"encoding/json"
"errors"
"fmt"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"strconv"
)
type NetworkType int
const (
NetworkInvalid NetworkType = iota
NetworkMainnet
NetworkTestnet
NetworkStagenet
)
const (
PPLNSWindow = 2160
BlockTime = 10
UnclePenalty = 20
UncleBlockDepth = 3
)
type ConsensusProvider interface {
Consensus() *Consensus
}
func (n NetworkType) String() string {
switch n {
case NetworkInvalid:
return "invalid"
case NetworkMainnet:
return "mainnet"
case NetworkTestnet:
return "testnet"
case NetworkStagenet:
return "stagenet"
}
return ""
}
func (n NetworkType) MarshalJSON() ([]byte, error) {
return json.Marshal(n.String())
}
func (n *NetworkType) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
switch s {
case "invalid":
*n = NetworkInvalid
case "", "mainnet": //special case for config.json
*n = NetworkMainnet
case "testnet":
*n = NetworkTestnet
case "stagenet":
*n = NetworkStagenet
default:
return fmt.Errorf("unknown network type %s", s)
}
return nil
}
type Consensus struct {
NetworkType NetworkType `json:"network_type"`
PoolName string `json:"name"`
PoolPassword string `json:"password"`
TargetBlockTime uint64 `json:"block_time"`
MinimumDifficulty uint64 `json:"min_diff"`
ChainWindowSize uint64 `json:"pplns_window"`
UnclePenalty uint64 `json:"uncle_penalty"`
id types.Hash
}
const SmallestMinimumDifficulty = 100000
const LargestMinimumDifficulty = 1000000000
func NewConsensus(networkType NetworkType, poolName, poolPassword string, targetBlockTime, minimumDifficulty, chainWindowSize, unclePenalty uint64) *Consensus {
c := &Consensus{
NetworkType: networkType,
PoolName: poolName,
PoolPassword: poolPassword,
TargetBlockTime: targetBlockTime,
MinimumDifficulty: minimumDifficulty,
ChainWindowSize: chainWindowSize,
UnclePenalty: unclePenalty,
}
if !c.verify() {
return nil
}
return c
}
func NewConsensusFromJSON(data []byte) (*Consensus, error) {
var c Consensus
if err := json.Unmarshal(data, &c); err != nil {
return nil, err
}
if !c.verify() {
return nil, errors.New("could not verify")
}
return &c, nil
}
func (c *Consensus) verify() bool {
if len(c.PoolName) > 128 {
return false
}
if len(c.PoolPassword) > 128 {
return false
}
if c.TargetBlockTime < 1 || c.TargetBlockTime > monero.BlockTime {
return false
}
if c.NetworkType == NetworkMainnet && c.MinimumDifficulty < SmallestMinimumDifficulty || c.MinimumDifficulty > LargestMinimumDifficulty {
return false
}
if c.ChainWindowSize < 60 || c.ChainWindowSize > 2160 {
return false
}
if c.UnclePenalty < 1 || c.UnclePenalty > 99 {
return false
}
var emptyHash types.Hash
c.id = c.CalculateId()
if c.id == emptyHash {
return false
}
return true
}
func (c *Consensus) CalculateSideTemplateId(share *PoolBlock) types.Hash {
mainData, _ := share.Main.SideChainHashingBlob()
sideData, _ := share.Side.MarshalBinary(share.ShareVersion())
return c.CalculateSideChainIdFromBlobs(mainData, sideData)
}
func (c *Consensus) CalculateSideChainIdFromBlobs(mainBlob, sideBlob []byte) types.Hash {
//TODO: handle extra nonce
return crypto.PooledKeccak256(mainBlob, sideBlob, c.id[:])
}
func (c *Consensus) Id() types.Hash {
var h types.Hash
if c.id == h {
//this data race is fine
c.id = c.CalculateId()
return c.id
}
return c.id
}
func (c *Consensus) IsDefault() bool {
return c.id == ConsensusDefault.id
}
func (c *Consensus) IsMini() bool {
return c.id == ConsensusMini.id
}
func (c *Consensus) DefaultPort() uint16 {
if c.IsMini() {
return 37888
}
return 37889
}
func (c *Consensus) SeedNode() string {
if c.IsMini() {
return "seeds-mini.p2pool.io"
} else if c.IsDefault() {
return "seeds.p2pool.io"
}
return ""
}
func (c *Consensus) CalculateId() types.Hash {
var buf []byte
buf = append(buf, c.NetworkType.String()...)
buf = append(buf, 0)
buf = append(buf, c.PoolName...)
buf = append(buf, 0)
buf = append(buf, c.PoolPassword...)
buf = append(buf, 0)
buf = append(buf, strconv.FormatUint(c.TargetBlockTime, 10)...)
buf = append(buf, 0)
buf = append(buf, strconv.FormatUint(c.MinimumDifficulty, 10)...)
buf = append(buf, 0)
buf = append(buf, strconv.FormatUint(c.ChainWindowSize, 10)...)
buf = append(buf, 0)
buf = append(buf, strconv.FormatUint(c.UnclePenalty, 10)...)
buf = append(buf, 0)
return randomx.ConsensusHash(buf)
}
var ConsensusDefault = &Consensus{NetworkType: NetworkMainnet, PoolName: "mainnet test 2", TargetBlockTime: 10, MinimumDifficulty: 100000, ChainWindowSize: 2160, UnclePenalty: 20, id: types.Hash{34, 175, 126, 231, 181, 11, 104, 146, 227, 153, 218, 107, 44, 108, 68, 39, 178, 81, 4, 212, 169, 4, 142, 0, 177, 110, 157, 240, 68, 7, 249, 24}}
var ConsensusMini = &Consensus{NetworkType: NetworkMainnet, PoolName: "mini", TargetBlockTime: 10, MinimumDifficulty: 100000, ChainWindowSize: 2160, UnclePenalty: 20, id: types.Hash{57, 130, 201, 26, 149, 174, 199, 250, 66, 80, 189, 18, 108, 216, 194, 220, 136, 23, 63, 24, 64, 113, 221, 44, 219, 86, 39, 163, 53, 24, 126, 196}}