github.com/codingfuture/orig-energi3@v0.8.4/energi/api/masternode.go (about) 1 // Copyright 2019 The Energi Core Authors 2 // This file is part of the Energi Core library. 3 // 4 // The Energi Core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Energi Core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 24 "github.com/ethereum/go-ethereum/accounts/abi/bind" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/p2p/enode" 30 31 energi_abi "energi.world/core/gen3/energi/abi" 32 energi_common "energi.world/core/gen3/energi/common" 33 energi_params "energi.world/core/gen3/energi/params" 34 ) 35 36 const ( 37 mntokenCallGas uint64 = energi_params.MasternodeCallGas 38 masternodeCallGas uint64 = energi_params.MasternodeCallGas 39 ) 40 41 type MasternodeAPI struct { 42 backend Backend 43 nodesCache *energi_common.CacheStorage 44 statsCache *energi_common.CacheStorage 45 } 46 47 func NewMasternodeAPI(b Backend) *MasternodeAPI { 48 r := &MasternodeAPI{ 49 backend: b, 50 nodesCache: energi_common.NewCacheStorage(), 51 statsCache: energi_common.NewCacheStorage(), 52 } 53 b.OnSyncedHeadUpdates(func() { 54 r.ListMasternodes() 55 r.Stats() 56 }) 57 return r 58 } 59 60 type MasternodeStats struct { 61 Active uint64 62 Total uint64 63 ActiveCollateral *hexutil.Big 64 TotalCollateral *hexutil.Big 65 MaxOfAllTimes *hexutil.Big 66 } 67 68 func (m *MasternodeAPI) token( 69 password *string, 70 dst common.Address, 71 ) (session *energi_abi.IMasternodeTokenSession, err error) { 72 contract, err := energi_abi.NewIMasternodeToken( 73 energi_params.Energi_MasternodeToken, m.backend.(bind.ContractBackend)) 74 if err != nil { 75 return nil, err 76 } 77 78 session = &energi_abi.IMasternodeTokenSession{ 79 Contract: contract, 80 CallOpts: bind.CallOpts{ 81 Pending: true, 82 From: dst, 83 GasLimit: energi_params.UnlimitedGas, 84 }, 85 TransactOpts: bind.TransactOpts{ 86 From: dst, 87 Signer: createSignerCallback(m.backend, password), 88 Value: common.Big0, 89 GasLimit: mntokenCallGas, 90 }, 91 } 92 return 93 } 94 95 func (m *MasternodeAPI) CollateralBalance( 96 dst common.Address, 97 ) (ret struct { 98 Balance *hexutil.Big 99 LastBlock *hexutil.Big 100 }, err error) { 101 token, err := energi_abi.NewIMasternodeTokenCaller( 102 energi_params.Energi_MasternodeToken, m.backend.(bind.ContractCaller)) 103 if err != nil { 104 log.Error("Failed", "err", err) 105 return ret, err 106 } 107 108 res, err := token.BalanceInfo( 109 &bind.CallOpts{ 110 From: dst, 111 GasLimit: energi_params.UnlimitedGas, 112 }, 113 dst, 114 ) 115 if err != nil { 116 log.Error("Failed", "err", err) 117 return ret, err 118 } 119 120 ret.Balance = (*hexutil.Big)(res.Balance) 121 ret.LastBlock = (*hexutil.Big)(res.LastBlock) 122 return ret, nil 123 } 124 125 func (m *MasternodeAPI) DepositCollateral( 126 dst common.Address, 127 amount *hexutil.Big, 128 password *string, 129 ) (txhash common.Hash, err error) { 130 registry, err := masternodeRegistry(password, dst, m.backend) 131 if err != nil { 132 return 133 } 134 135 limits, err := registry.CollateralLimits() 136 if err != nil { 137 return 138 } 139 140 if err = m.validateAmount("Deposit", amount.ToInt(), limits.Min); err != nil { 141 return 142 } 143 144 token, err := m.token(password, dst) 145 if err != nil { 146 return 147 } 148 149 balance, err := token.BalanceOf(dst) 150 if err != nil { 151 // Possible new masternode detected. 152 log.Warn("Fetching masternode collateral failed: %v", err) 153 } 154 155 newTotalAmount := new(big.Int).Add(balance, amount.ToInt()) 156 157 // Expected total amount should not be more than the maximum collateral value allowed. 158 if newTotalAmount.Cmp(limits.Max) > 0 { 159 err = fmt.Errorf("Total expected deposits should not exceed max allowed deposit") 160 return 161 } 162 163 token.TransactOpts.Value = amount.ToInt() 164 tx, err := token.DepositCollateral() 165 166 if tx != nil { 167 txhash = tx.Hash() 168 log.Info("Note: please wait until the collateral TX gets into a block!", "tx", txhash.Hex()) 169 } 170 171 return 172 } 173 174 func (m *MasternodeAPI) WithdrawCollateral( 175 dst common.Address, 176 amount *hexutil.Big, 177 password *string, 178 ) (txhash common.Hash, err error) { 179 registry, err := masternodeRegistry(password, dst, m.backend) 180 if err != nil { 181 return 182 } 183 184 limits, err := registry.CollateralLimits() 185 if err != nil { 186 return 187 } 188 189 if err = m.validateAmount("Withdrawal", amount.ToInt(), limits.Min); err != nil { 190 return 191 } 192 193 token, err := m.token(password, dst) 194 if err != nil { 195 return 196 } 197 198 balance, err := token.BalanceOf(dst) 199 if err != nil { 200 err = fmt.Errorf("Fetching masternode collateral failed: %v", err) 201 return 202 } 203 204 // Amount to withdraw should be less than or equal to the collateral amount. 205 if amount.ToInt().Cmp(balance) > 0 { 206 err = fmt.Errorf("Withdrawal amount is greater than the collateral balance amount") 207 return 208 } 209 210 tx, err := token.WithdrawCollateral(amount.ToInt()) 211 212 if tx != nil { 213 txhash = tx.Hash() 214 log.Info("Note: please wait until the collateral TX gets into a block!", "tx", txhash.Hex()) 215 } 216 217 return 218 } 219 220 func (m *MasternodeAPI) validateAmount(validateType string, amount, minColl *big.Int) error { 221 // Amount to should be greater than zero. 222 if amount.Cmp(common.Big0) < 1 { 223 return fmt.Errorf("%v amount should be greater than zero", validateType) 224 } 225 226 // Amount should be a multiple of the minimum collateral amount allowed. 227 if new(big.Int).Mod(amount, minColl).Cmp(common.Big0) != 0 { 228 return fmt.Errorf("%v amount should be a multiple of the minimum collateral amount", validateType) 229 } 230 231 return nil 232 } 233 234 type MNInfo struct { 235 Masternode common.Address 236 Owner common.Address 237 Enode string 238 Collateral *hexutil.Big 239 AnnouncedBlock uint64 240 IsActive bool 241 IsAlive bool 242 SWFeatures *hexutil.Big 243 SWVersion string 244 } 245 246 func (m *MasternodeAPI) ListMasternodes() (res []MNInfo, err error) { 247 data, err := m.nodesCache.Get(m.backend, m.listMasternodes) 248 if err != nil || data == nil { 249 log.Error("ListMasternodes failed", "err", err) 250 return 251 } 252 253 res = data.([]MNInfo) 254 255 return 256 } 257 258 func (m *MasternodeAPI) listMasternodes(num *big.Int) (interface{}, error) { 259 registry, err := energi_abi.NewIMasternodeRegistryV2Caller( 260 energi_params.Energi_MasternodeRegistry, m.backend.(bind.ContractCaller)) 261 if err != nil { 262 log.Error("Failed", "err", err) 263 return nil, err 264 } 265 266 call_opts := &bind.CallOpts{ 267 BlockNumber: num, 268 GasLimit: energi_params.UnlimitedGas, 269 } 270 masternodes, err := registry.Enumerate(call_opts) 271 if err != nil { 272 log.Error("Failed", "err", err) 273 return nil, err 274 } 275 276 res := make([]MNInfo, 0, len(masternodes)) 277 for _, mn := range masternodes { 278 mninfo, err := registry.Info(call_opts, mn) 279 if err != nil { 280 log.Warn("Info error", "mn", mn, "err", err) 281 continue 282 } 283 284 isActive, err := registry.IsActive(call_opts, mn) 285 if err != nil { 286 log.Warn("IsActive error", "mn", mn, "err", err) 287 continue 288 } 289 290 canHeartbeat, err := registry.CanHeartbeat(call_opts, mn) 291 if err != nil { 292 log.Warn("CanHeartbeat error", "mn", mn, "err", err) 293 continue 294 } 295 296 res = append(res, MNInfo{ 297 Masternode: mn, 298 Owner: mninfo.Owner, 299 Enode: m.enode(mninfo.Ipv4address, mninfo.Enode), 300 Collateral: (*hexutil.Big)(mninfo.Collateral), 301 AnnouncedBlock: mninfo.AnnouncedBlock.Uint64(), 302 IsActive: isActive, 303 IsAlive: isActive && !canHeartbeat, 304 SWFeatures: (*hexutil.Big)(mninfo.SwFeatures), 305 SWVersion: energi_common.SWVersionIntToString(mninfo.SwFeatures), 306 }) 307 } 308 309 return res, err 310 } 311 312 func (m *MasternodeAPI) MasternodeInfo(owner_or_mn common.Address) (res MNInfo, err error) { 313 Mns, err := m.ListMasternodes() 314 if err != nil { 315 log.Error("Failed at m.ListMasternodes", "err", err) 316 return 317 } 318 319 for _, node := range Mns { 320 if node.Masternode == owner_or_mn || node.Owner == owner_or_mn { 321 res = node 322 break 323 } 324 } 325 326 return 327 } 328 329 func (m *MasternodeAPI) Stats() (res *MasternodeStats, err error) { 330 data, err := m.statsCache.Get(m.backend, m.stats) 331 332 if err != nil || data == nil { 333 log.Error("Stats failed", "err", err) 334 return 335 } 336 337 res = data.(*MasternodeStats) 338 return 339 } 340 341 func (m *MasternodeAPI) stats(num *big.Int) (interface{}, error) { 342 registry, err := energi_abi.NewIMasternodeRegistryV2Caller( 343 energi_params.Energi_MasternodeRegistry, m.backend.(bind.ContractCaller)) 344 if err != nil { 345 log.Error("Failed", "err", err) 346 return nil, err 347 } 348 349 call_opts := &bind.CallOpts{ 350 BlockNumber: num, 351 GasLimit: energi_params.UnlimitedGas, 352 } 353 count, err := registry.Count(call_opts) 354 if err != nil { 355 log.Error("Failed", "err", err) 356 return nil, err 357 } 358 359 res := &MasternodeStats{} 360 res.Active = count.Active.Uint64() 361 res.Total = count.Total.Uint64() 362 res.ActiveCollateral = (*hexutil.Big)(count.ActiveCollateral) 363 res.TotalCollateral = (*hexutil.Big)(count.TotalCollateral) 364 res.MaxOfAllTimes = (*hexutil.Big)(count.MaxOfAllTimes) 365 366 return res, nil 367 } 368 369 func (m *MasternodeAPI) enode(ipv4address uint32, pubkey [2][32]byte) string { 370 cfg := m.backend.ChainConfig() 371 res := energi_common.MastenodeEnode(ipv4address, pubkey, cfg) 372 373 if res == nil { 374 return "" 375 } 376 377 return res.String() 378 } 379 380 func masternodeRegistry( 381 password *string, 382 dst common.Address, 383 backend Backend, 384 ) (session *energi_abi.IMasternodeRegistryV2Session, err error) { 385 contract, err := energi_abi.NewIMasternodeRegistryV2( 386 energi_params.Energi_MasternodeRegistry, backend.(bind.ContractBackend)) 387 if err != nil { 388 return nil, err 389 } 390 391 session = &energi_abi.IMasternodeRegistryV2Session{ 392 Contract: contract, 393 CallOpts: bind.CallOpts{ 394 From: dst, 395 GasLimit: energi_params.UnlimitedGas, 396 }, 397 TransactOpts: bind.TransactOpts{ 398 From: dst, 399 Signer: createSignerCallback(backend, password), 400 Value: common.Big0, 401 GasLimit: masternodeCallGas, 402 }, 403 } 404 return 405 } 406 407 func (m *MasternodeAPI) Announce( 408 owner common.Address, 409 enode_url string, 410 password *string, 411 ) (txhash common.Hash, err error) { 412 registry, err := masternodeRegistry(password, owner, m.backend) 413 if err != nil { 414 return 415 } 416 417 var ( 418 ipv4address uint32 419 pubkey [2][32]byte 420 ) 421 422 //--- 423 res, err := enode.ParseV4(enode_url) 424 if err != nil { 425 return 426 } 427 428 //--- 429 ip := res.IP().To4() 430 if ip == nil { 431 err = errors.New("Invalid IPv4") 432 return 433 } 434 435 if ip[0] == byte(127) || ip[0] == byte(10) || 436 (ip[0] == byte(192) && ip[1] == byte(168)) || 437 (ip[0] == byte(172) && (ip[1]&0xF0) == byte(16)) { 438 err = errors.New("Wrong enode IP") 439 return 440 } 441 442 cfg := m.backend.ChainConfig() 443 444 if res.UDP() != int(cfg.ChainID.Int64()) || res.TCP() != int(cfg.ChainID.Int64()) { 445 err = errors.New("Wrong enode port") 446 return 447 } 448 449 ipv4address = uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) 450 451 //--- 452 pk := crypto.CompressPubkey(res.Pubkey()) 453 if len(pk) != 33 { 454 log.Error("Wrong public key length", "pklen", len(pk)) 455 err = errors.New("Wrong public key") 456 return 457 } 458 459 copy(pubkey[0][:], pk[:32]) 460 copy(pubkey[1][:], pk[32:33]) 461 462 //--- 463 masternode := crypto.PubkeyToAddress(*res.Pubkey()) 464 465 //--- 466 tx, err := registry.Announce(masternode, ipv4address, pubkey) 467 468 if tx != nil { 469 txhash = tx.Hash() 470 log.Info("Note: please wait until the TX gets into a block!", "tx", txhash.Hex()) 471 } 472 473 return 474 } 475 476 func (m *MasternodeAPI) Denounce( 477 owner common.Address, 478 password *string, 479 ) (txhash common.Hash, err error) { 480 registry, err := masternodeRegistry(password, owner, m.backend) 481 if err != nil { 482 return 483 } 484 485 ownerinfo, err := registry.OwnerInfo(owner) 486 if err != nil { 487 log.Error("Not found", "owner", owner) 488 return 489 } 490 491 tx, err := registry.Denounce(ownerinfo.Masternode) 492 493 if tx != nil { 494 txhash = tx.Hash() 495 log.Info("Note: please wait until the TX gets into a block!", "tx", txhash.Hex()) 496 } 497 498 return 499 }