223 lines
5.3 KiB
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}}
|