git.gammaspectra.live/P2Pool/consensus@v0.0.0-20240403173234-a039820b20c9/p2pool/sidechain/consensus.go (about)

     1  package sidechain
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"git.gammaspectra.live/P2Pool/consensus/monero"
     7  	"git.gammaspectra.live/P2Pool/consensus/monero/crypto"
     8  	"git.gammaspectra.live/P2Pool/consensus/monero/randomx"
     9  	"git.gammaspectra.live/P2Pool/consensus/types"
    10  	"git.gammaspectra.live/P2Pool/consensus/utils"
    11  	"git.gammaspectra.live/P2Pool/moneroutil"
    12  	"strconv"
    13  )
    14  
    15  type NetworkType int
    16  
    17  const (
    18  	NetworkInvalid NetworkType = iota
    19  	NetworkMainnet
    20  	NetworkTestnet
    21  	NetworkStagenet
    22  )
    23  
    24  const (
    25  	UncleBlockDepth = 3
    26  )
    27  
    28  type ConsensusProvider interface {
    29  	Consensus() *Consensus
    30  }
    31  
    32  func (n NetworkType) String() string {
    33  	switch n {
    34  	case NetworkInvalid:
    35  		return "invalid"
    36  	case NetworkMainnet:
    37  		return "mainnet"
    38  	case NetworkTestnet:
    39  		return "testnet"
    40  	case NetworkStagenet:
    41  		return "stagenet"
    42  	}
    43  	return ""
    44  }
    45  
    46  func (n NetworkType) AddressNetwork() (uint8, error) {
    47  	switch n {
    48  	case NetworkInvalid:
    49  		return 0, errors.New("invalid network")
    50  	case NetworkMainnet:
    51  		return moneroutil.MainNetwork, nil
    52  	case NetworkTestnet:
    53  		return moneroutil.TestNetwork, nil
    54  	case NetworkStagenet:
    55  		return moneroutil.StageNetwork, nil
    56  	}
    57  	return 0, errors.New("unknown network")
    58  }
    59  
    60  func (n NetworkType) MarshalJSON() ([]byte, error) {
    61  	return []byte("\"" + n.String() + "\""), nil
    62  }
    63  
    64  func (n *NetworkType) UnmarshalJSON(b []byte) error {
    65  	var s string
    66  	if err := utils.UnmarshalJSON(b, &s); err != nil {
    67  		return err
    68  	}
    69  
    70  	switch s {
    71  	case "invalid":
    72  		*n = NetworkInvalid
    73  	case "", "mainnet": //special case for config.json
    74  		*n = NetworkMainnet
    75  	case "testnet":
    76  		*n = NetworkTestnet
    77  	case "stagenet":
    78  		*n = NetworkStagenet
    79  
    80  	default:
    81  		return fmt.Errorf("unknown network type %s", s)
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  type Consensus struct {
    88  	NetworkType       NetworkType `json:"network_type"`
    89  	PoolName          string      `json:"name"`
    90  	PoolPassword      string      `json:"password"`
    91  	TargetBlockTime   uint64      `json:"block_time"`
    92  	MinimumDifficulty uint64      `json:"min_diff"`
    93  	ChainWindowSize   uint64      `json:"pplns_window"`
    94  	UnclePenalty      uint64      `json:"uncle_penalty"`
    95  
    96  	// HardFork optional hardfork information for p2pool
    97  	// If empty it will be filled with the default hardfork list to the corresponding NetworkType
    98  	HardForks []HardFork `json:"hard_forks,omitempty"`
    99  
   100  	hasher randomx.Hasher
   101  
   102  	Id types.Hash `json:"id"`
   103  }
   104  
   105  const SmallestMinimumDifficulty = 100000
   106  const LargestMinimumDifficulty = 1000000000
   107  
   108  func NewConsensus(networkType NetworkType, poolName, poolPassword string, targetBlockTime, minimumDifficulty, chainWindowSize, unclePenalty uint64) *Consensus {
   109  	c := &Consensus{
   110  		NetworkType:       networkType,
   111  		PoolName:          poolName,
   112  		PoolPassword:      poolPassword,
   113  		TargetBlockTime:   targetBlockTime,
   114  		MinimumDifficulty: minimumDifficulty,
   115  		ChainWindowSize:   chainWindowSize,
   116  		UnclePenalty:      unclePenalty,
   117  	}
   118  
   119  	if !c.verify() {
   120  		return nil
   121  	}
   122  	return c
   123  }
   124  
   125  func NewConsensusFromJSON(data []byte) (*Consensus, error) {
   126  	var c Consensus
   127  	if err := utils.UnmarshalJSON(data, &c); err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	if !c.verify() {
   132  		return nil, errors.New("could not verify")
   133  	}
   134  
   135  	return &c, nil
   136  }
   137  
   138  func (c *Consensus) verify() bool {
   139  
   140  	if c.PoolName == "default" {
   141  		//p2pool changed consensus config to use default instead of original value
   142  		c.PoolName = ConsensusDefault.PoolName
   143  	}
   144  
   145  	if len(c.PoolName) > 128 {
   146  		return false
   147  	}
   148  
   149  	if len(c.PoolPassword) > 128 {
   150  		return false
   151  	}
   152  
   153  	if c.TargetBlockTime < 1 || c.TargetBlockTime > monero.BlockTime {
   154  		return false
   155  	}
   156  
   157  	if c.NetworkType == NetworkMainnet && c.MinimumDifficulty < SmallestMinimumDifficulty || c.MinimumDifficulty > LargestMinimumDifficulty {
   158  		return false
   159  	}
   160  
   161  	if c.ChainWindowSize < 60 || c.ChainWindowSize > 2160 {
   162  		return false
   163  	}
   164  
   165  	if c.UnclePenalty < 1 || c.UnclePenalty > 99 {
   166  		return false
   167  	}
   168  
   169  	var emptyHash types.Hash
   170  	c.Id = c.CalculateId()
   171  	if c.Id == emptyHash {
   172  		return false
   173  	}
   174  
   175  	if len(c.HardForks) == 0 {
   176  		switch c.NetworkType {
   177  		case NetworkMainnet:
   178  			c.HardForks = p2poolMainNetHardForks
   179  		case NetworkTestnet:
   180  			c.HardForks = p2poolTestNetHardForks
   181  		case NetworkStagenet:
   182  			c.HardForks = p2poolStageNetHardForks
   183  		default:
   184  			utils.Panicf("invalid network type for determining hardfork")
   185  		}
   186  	}
   187  
   188  	return true
   189  }
   190  
   191  func (c *Consensus) CalculateSideTemplateId(share *PoolBlock) (result types.Hash) {
   192  	return c.CalculateSideTemplateIdPreAllocated(share, make([]byte, 0, max(share.Main.BufferLength(), share.Side.BufferLength())))
   193  }
   194  
   195  func (c *Consensus) CalculateSideTemplateIdPreAllocated(share *PoolBlock, buf []byte) (result types.Hash) {
   196  	h := crypto.GetKeccak256Hasher()
   197  	defer crypto.PutKeccak256Hasher(h)
   198  
   199  	buf, _ = share.Main.SideChainHashingBlob(buf, true)
   200  	_, _ = h.Write(buf)
   201  	buf, _ = share.Side.AppendBinary(buf[:0], share.ShareVersion())
   202  	_, _ = h.Write(buf)
   203  
   204  	_, _ = h.Write(c.Id[:])
   205  	crypto.HashFastSum(h, result[:])
   206  	return result
   207  }
   208  
   209  func (c *Consensus) CalculateSideChainIdFromBlobs(mainBlob, sideBlob []byte) (result types.Hash) {
   210  	h := crypto.GetKeccak256Hasher()
   211  	defer crypto.PutKeccak256Hasher(h)
   212  
   213  	_, _ = h.Write(mainBlob)
   214  	_, _ = h.Write(sideBlob)
   215  
   216  	_, _ = h.Write(c.Id[:])
   217  	crypto.HashFastSum(h, result[:])
   218  	return result
   219  }
   220  
   221  func (c *Consensus) IsDefault() bool {
   222  	return c.Id == ConsensusDefault.Id
   223  }
   224  
   225  func (c *Consensus) IsMini() bool {
   226  	return c.Id == ConsensusMini.Id
   227  }
   228  
   229  func (c *Consensus) DefaultPort() uint16 {
   230  	if c.IsMini() {
   231  		return 37888
   232  	}
   233  	return 37889
   234  }
   235  
   236  func (c *Consensus) SeedNode() string {
   237  	if c.IsMini() {
   238  		return "seeds-mini.p2pool.io"
   239  	} else if c.IsDefault() {
   240  		return "seeds.p2pool.io"
   241  	}
   242  	return ""
   243  }
   244  
   245  func (c *Consensus) InitHasher(n int, flags ...randomx.Flag) error {
   246  	if c.hasher != nil {
   247  		c.hasher.Close()
   248  	}
   249  	var err error
   250  	c.hasher, err = randomx.NewRandomX(n, flags...)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	return nil
   255  }
   256  
   257  func (c *Consensus) GetHasher() randomx.Hasher {
   258  	if c.hasher == nil {
   259  		panic("hasher has not been initialized in consensus")
   260  	}
   261  	return c.hasher
   262  }
   263  
   264  func (c *Consensus) CalculateId() types.Hash {
   265  	var buf []byte
   266  	buf = append(buf, c.NetworkType.String()...)
   267  	buf = append(buf, 0)
   268  	buf = append(buf, c.PoolName...)
   269  	buf = append(buf, 0)
   270  	buf = append(buf, c.PoolPassword...)
   271  	buf = append(buf, 0)
   272  	buf = append(buf, strconv.FormatUint(c.TargetBlockTime, 10)...)
   273  	buf = append(buf, 0)
   274  	buf = append(buf, strconv.FormatUint(c.MinimumDifficulty, 10)...)
   275  	buf = append(buf, 0)
   276  	buf = append(buf, strconv.FormatUint(c.ChainWindowSize, 10)...)
   277  	buf = append(buf, 0)
   278  	buf = append(buf, strconv.FormatUint(c.UnclePenalty, 10)...)
   279  	buf = append(buf, 0)
   280  
   281  	return randomx.ConsensusHash(buf)
   282  }
   283  
   284  // ApplyUnclePenalty Applies UnclePenalty efficiently
   285  func (c *Consensus) ApplyUnclePenalty(weight types.Difficulty) (uncleWeight, unclePenalty types.Difficulty) {
   286  	unclePenalty = weight.Mul64(c.UnclePenalty).Div64(100)
   287  	uncleWeight = weight.Sub(unclePenalty)
   288  	return
   289  }
   290  
   291  var ConsensusDefault = &Consensus{NetworkType: NetworkMainnet, PoolName: "mainnet test 2", TargetBlockTime: 10, MinimumDifficulty: 100000, ChainWindowSize: 2160, UnclePenalty: 20, HardForks: p2poolMainNetHardForks, 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}}
   292  var ConsensusMini = &Consensus{NetworkType: NetworkMainnet, PoolName: "mini", TargetBlockTime: 10, MinimumDifficulty: 100000, ChainWindowSize: 2160, UnclePenalty: 20, HardForks: p2poolMainNetHardForks, 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}}