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}}