github.com/amazechain/amc@v0.1.3/internal/consensus/apos/api.go (about) 1 // Copyright 2022 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package apos 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "github.com/amazechain/amc/contracts/deposit" 25 "github.com/amazechain/amc/modules/rawdb" 26 "github.com/amazechain/amc/turbo/rpchelper" 27 "github.com/holiman/uint256" 28 "github.com/ledgerwatch/erigon-lib/kv" 29 30 "github.com/amazechain/amc/common/block" 31 "github.com/amazechain/amc/common/hexutil" 32 "github.com/amazechain/amc/common/types" 33 "github.com/amazechain/amc/internal/avm/common" 34 mvm_types "github.com/amazechain/amc/internal/avm/types" 35 "github.com/amazechain/amc/internal/consensus" 36 "github.com/amazechain/amc/modules/rpc/jsonrpc" 37 ) 38 39 const maxSearchBlock = 1000 40 41 type MinedBlock struct { 42 BlockNumber *uint256.Int `json:"blockNumber"` 43 Timestamp uint64 `json:"timestamp"` 44 Reward *uint256.Int `json:"reward"` 45 } 46 type MinedBlockResponse struct { 47 MinedBlocks []MinedBlock `json:"minedBlocks"` 48 CurrentBlockNumber *uint256.Int `json:"currentBlockNumber"` 49 } 50 51 type VerifiedBlockResponse struct { 52 MinedBlocks []MinedBlock `json:"minedBlocks"` 53 Total *uint256.Int `json:"totalBlocks"` 54 } 55 56 // API is a user facing jsonrpc API to allow controlling the signer and voting 57 // mechanisms of the proof-of-authority scheme. 58 type API struct { 59 chain consensus.ChainReader 60 apos *APos 61 } 62 63 // GetSnapshot retrieves the state snapshot at a given block. 64 func (api *API) GetSnapshot(number *jsonrpc.BlockNumber) (*Snapshot, error) { 65 // Retrieve the requested block number (or current if none requested) 66 var header block.IHeader 67 if number == nil || *number == jsonrpc.LatestBlockNumber { 68 header = api.chain.CurrentBlock().Header() 69 } else { 70 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 71 } 72 // Ensure we have an actually valid block and return its snapshot 73 if header == nil { 74 return nil, errUnknownBlock 75 } 76 return api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 77 } 78 79 // GetSnapshotAtHash retrieves the state snapshot at a given block. 80 func (api *API) GetSnapshotAtHash(hash types.Hash) (*Snapshot, error) { 81 header, _ := api.chain.GetHeaderByHash(hash) 82 if header == nil { 83 return nil, errUnknownBlock 84 } 85 return api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 86 } 87 88 // GetSigners retrieves the list of authorized signers at the specified block. 89 func (api *API) GetSigners(number *jsonrpc.BlockNumber) ([]common.Address, error) { 90 // Retrieve the requested block number (or current if none requested) 91 var header block.IHeader 92 if number == nil || *number == jsonrpc.LatestBlockNumber { 93 header = api.chain.CurrentBlock().Header() 94 } else { 95 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 96 } 97 // Ensure we have an actually valid block and return the signers from its snapshot 98 if header == nil { 99 return nil, errUnknownBlock 100 } 101 snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 102 if err != nil { 103 return nil, err 104 } 105 106 signers := snap.signers() 107 ethSigners := make([]common.Address, len(signers)) 108 for i, signer := range signers { 109 ethSigners[i] = *mvm_types.FromAmcAddress(&signer) 110 } 111 return ethSigners, nil 112 } 113 114 // GetSignersAtHash retrieves the list of authorized signers at the specified block. 115 func (api *API) GetSignersAtHash(hash types.Hash) ([]common.Address, error) { 116 header, _ := api.chain.GetHeaderByHash(hash) 117 if header == nil { 118 return nil, errUnknownBlock 119 } 120 snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 121 if err != nil { 122 return nil, err 123 } 124 signers := snap.signers() 125 ethSigners := make([]common.Address, len(signers)) 126 for i, signer := range signers { 127 ethSigners[i] = *mvm_types.FromAmcAddress(&signer) 128 } 129 return ethSigners, nil 130 } 131 132 // Proposals returns the current proposals the node tries to uphold and vote on. 133 func (api *API) Proposals() map[common.Address]bool { 134 api.apos.lock.RLock() 135 defer api.apos.lock.RUnlock() 136 137 proposals := make(map[common.Address]bool) 138 for address, auth := range api.apos.proposals { 139 proposals[*mvm_types.FromAmcAddress(&address)] = auth 140 } 141 return proposals 142 } 143 144 // Propose injects a new authorization proposal that the signer will attempt to 145 // push through. 146 func (api *API) Propose(address common.Address, auth bool) { 147 api.apos.lock.Lock() 148 defer api.apos.lock.Unlock() 149 150 api.apos.proposals[*mvm_types.ToAmcAddress(&address)] = auth 151 } 152 153 // Discard drops a currently running proposal, stopping the signer from casting 154 // further votes (either for or against). 155 func (api *API) Discard(address common.Address) { 156 api.apos.lock.Lock() 157 defer api.apos.lock.Unlock() 158 159 delete(api.apos.proposals, *mvm_types.ToAmcAddress(&address)) 160 } 161 162 type status struct { 163 InturnPercent float64 `json:"inturnPercent"` 164 SigningStatus map[types.Address]int `json:"sealerActivity"` 165 NumBlocks uint64 `json:"numBlocks"` 166 } 167 168 // Status returns the status of the last N blocks, 169 // - the number of active signers, 170 // - the number of signers, 171 // - the percentage of in-turn blocks 172 func (api *API) Status() (*status, error) { 173 var ( 174 numBlocks = uint64(64) 175 header = api.chain.CurrentBlock().Header() 176 diff = uint64(0) 177 optimals = 0 178 ) 179 snap, err := api.apos.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 180 if err != nil { 181 return nil, err 182 } 183 var ( 184 signers = snap.signers() 185 end = header.Number64().Uint64() 186 start = end - numBlocks 187 ) 188 if numBlocks > end { 189 start = 1 190 numBlocks = end - start 191 } 192 signStatus := make(map[types.Address]int) 193 for _, s := range signers { 194 signStatus[s] = 0 195 } 196 for n := start; n < end; n++ { 197 h := api.chain.GetHeaderByNumber(uint256.NewInt(n)) 198 block := api.chain.GetBlock(h.Hash(), n) 199 if h == nil { 200 return nil, fmt.Errorf("missing block %d", n) 201 } 202 if block.Difficulty().Cmp(diffInTurn) == 0 { 203 optimals++ 204 } 205 diff += block.Difficulty().Uint64() 206 sealer, err := api.apos.Author(h) 207 if err != nil { 208 return nil, err 209 } 210 signStatus[sealer]++ 211 } 212 return &status{ 213 InturnPercent: float64(100*optimals) / float64(numBlocks), 214 SigningStatus: signStatus, 215 NumBlocks: numBlocks, 216 }, nil 217 } 218 219 type blockNumberOrHashOrRLP struct { 220 *jsonrpc.BlockNumberOrHash 221 RLP hexutil.Bytes `json:"rlp,omitempty"` 222 } 223 224 func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error { 225 bnOrHash := new(jsonrpc.BlockNumberOrHash) 226 // Try to unmarshal bNrOrHash 227 if err := bnOrHash.UnmarshalJSON(data); err == nil { 228 sb.BlockNumberOrHash = bnOrHash 229 return nil 230 } 231 // Try to unmarshal RLP 232 var input string 233 if err := json.Unmarshal(data, &input); err != nil { 234 return err 235 } 236 blob, err := hexutil.Decode(input) 237 if err != nil { 238 return err 239 } 240 sb.RLP = blob 241 return nil 242 } 243 244 // GetSigner returns the signer for a specific Apos block. 245 // Can be called with either a blocknumber, blockhash or an rlp encoded blob. 246 // The RLP encoded blob can either be a block or a header. 247 func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (types.Address, error) { 248 if len(rlpOrBlockNr.RLP) == 0 { 249 blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash 250 var header block.IHeader 251 if blockNrOrHash == nil { 252 header = api.chain.CurrentBlock().Header() 253 } else if hash, ok := blockNrOrHash.Hash(); ok { 254 header, _ = api.chain.GetHeaderByHash(hash) 255 } else if number, ok := blockNrOrHash.Number(); ok { 256 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 257 } 258 if header == nil { 259 return types.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String()) 260 } 261 return api.apos.Author(header) 262 } 263 264 return types.Address{}, fmt.Errorf("do not support rlp") 265 } 266 267 // GetRewards 268 func (api *API) GetRewards(address common.Address, from jsonrpc.BlockNumberOrHash, to jsonrpc.BlockNumberOrHash) (resp *RewardResponse, err error) { 269 270 var ( 271 resolvedFromBlock *uint256.Int 272 resolvedToBlock *uint256.Int 273 ) 274 275 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 276 resolvedFromBlock, _, err = rpchelper.GetCanonicalBlockNumber(from, tx) 277 resolvedToBlock, _, err = rpchelper.GetCanonicalBlockNumber(to, tx) 278 return nil 279 }) 280 281 if err != nil { 282 return nil, err 283 } 284 285 rewardService := newReward(api.apos.chainConfig) 286 resp, err = rewardService.GetRewards(*mvm_types.ToAmcAddress(&address), resolvedFromBlock, resolvedToBlock, api.chain.GetBlockByNumber) 287 288 return resp, err 289 } 290 291 // GetRewards 292 func (api *API) GetDepositInfo(address common.Address) (*deposit.Info, error) { 293 294 addr := *mvm_types.ToAmcAddress(&address) 295 296 info := new(deposit.Info) 297 var err error 298 299 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 300 info = deposit.GetDepositInfo(tx, addr) 301 return nil 302 }) 303 304 return info, err 305 } 306 307 // GetRewards todo:needs check 308 func (api *API) GetBlockRewards(blockNr jsonrpc.BlockNumberOrHash) (resp []*block.Reward, err error) { 309 var ( 310 resolvedBlockNr *uint256.Int 311 hash types.Hash 312 ) 313 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 314 resolvedBlockNr, hash, err = rpchelper.GetCanonicalBlockNumber(blockNr, tx) 315 if err != nil { 316 return err 317 } 318 //header := rawdb.ReadHeader(tx, hash, resolvedBlockNr.Uint64()) 319 body := rawdb.ReadBlock(tx, hash, resolvedBlockNr.Uint64()) 320 if body == nil { 321 err = errors.New("cannot find block body") 322 return err 323 } 324 resp = body.Body().Reward() 325 return nil 326 }) 327 return resp, err 328 } 329 330 // getHeader search header by BlockNumberOrHash 331 func (api *API) getHeader(from jsonrpc.BlockNumberOrHash) (currentHeader block.IHeader) { 332 // 333 if blockNr, ok := from.Number(); ok { 334 if blockNr == jsonrpc.LatestBlockNumber || blockNr == jsonrpc.PendingBlockNumber { 335 currentHeader = api.chain.CurrentBlock().Header() 336 } else if blockNr < jsonrpc.EarliestBlockNumber { 337 currentHeader = api.chain.GetHeaderByNumber(uint256.NewInt(0)) 338 } else { 339 currentHeader = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(blockNr.Int64()))) 340 } 341 } else if hash, ok := from.Hash(); ok { 342 currentHeader, _ = api.chain.GetHeaderByHash(hash) 343 } else { 344 currentHeader = api.chain.CurrentBlock().Header() 345 } 346 return 347 } 348 349 // GetTasks 350 func (api *API) GetMinedBlock(address common.Address, from jsonrpc.BlockNumberOrHash, wantCount uint64) (*MinedBlockResponse, error) { 351 352 addr := *mvm_types.ToAmcAddress(&address) 353 var ( 354 err error 355 searchCount int 356 findCount uint64 357 currentHeader block.IHeader 358 currentBlock block.IBlock 359 depositInfo *deposit.Info 360 ) 361 362 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 363 depositInfo = deposit.GetDepositInfo(tx, addr) 364 return nil 365 }) 366 if depositInfo == nil { 367 return nil, fmt.Errorf("address do not have depositInfo") 368 } 369 // 370 currentHeader = api.getHeader(from) 371 if currentHeader == nil { 372 return nil, err 373 } 374 375 // 376 currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64()) 377 searchCount = 0 378 findCount = 0 379 minedBlocks := make([]MinedBlock, 0) 380 381 for i := currentHeader.Number64().Uint64(); i >= 0; i-- { 382 verifier := currentBlock.Body().Verifier() 383 for _, verify := range verifier { 384 if addr == verify.Address { 385 minedBlocks = append(minedBlocks, MinedBlock{ 386 BlockNumber: currentBlock.Number64(), 387 Timestamp: currentBlock.Time(), 388 Reward: depositInfo.RewardPerBlock, 389 }) 390 findCount++ 391 if findCount >= wantCount { 392 goto Finish 393 } 394 } 395 } 396 searchCount++ 397 if searchCount >= maxSearchBlock { 398 break 399 } 400 currentHeader = api.chain.GetHeaderByNumber(new(uint256.Int).SubUint64(currentBlock.Number64(), 1)) 401 if currentHeader == nil { 402 break 403 } 404 currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64()) 405 } 406 407 Finish: 408 return &MinedBlockResponse{ 409 MinedBlocks: minedBlocks, 410 CurrentBlockNumber: currentBlock.Number64(), 411 }, nil 412 } 413 414 // VerifiedBlock 415 func (api *API) VerifiedBlock(address common.Address, from jsonrpc.BlockNumberOrHash, wantCount uint64, to *jsonrpc.BlockNumber) (*VerifiedBlockResponse, error) { 416 417 addr := *mvm_types.ToAmcAddress(&address) 418 var ( 419 err error 420 searchCount int 421 findCount uint64 422 currentHeader block.IHeader 423 currentBlock block.IBlock 424 depositInfo *deposit.Info 425 ) 426 427 if to.Int64() <= 0 { 428 return nil, fmt.Errorf("'To' block number must be greater than 0") 429 } 430 431 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 432 depositInfo = deposit.GetDepositInfo(tx, addr) 433 return nil 434 }) 435 if depositInfo == nil { 436 return nil, fmt.Errorf("address do not have depositInfo") 437 } 438 // 439 currentHeader = api.getHeader(from) 440 if currentHeader == nil { 441 return nil, err 442 } 443 444 // 445 currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64()) 446 searchCount = 0 447 findCount = 0 448 minedBlocks := make([]MinedBlock, 0) 449 450 for i := currentHeader.Number64().Uint64(); i >= 0; i-- { 451 verifier := currentBlock.Body().Verifier() 452 for _, verify := range verifier { 453 if addr == verify.Address { 454 minedBlocks = append(minedBlocks, MinedBlock{ 455 BlockNumber: currentBlock.Number64(), 456 Timestamp: currentBlock.Time(), 457 Reward: depositInfo.RewardPerBlock, 458 }) 459 findCount++ 460 } 461 } 462 if findCount >= wantCount { 463 break 464 } 465 if uint64(*to) == currentBlock.Number64().Uint64() { 466 break 467 } 468 searchCount++ 469 if searchCount >= int(api.apos.config.RewardEpoch) { 470 break 471 } 472 currentHeader = api.chain.GetHeaderByNumber(new(uint256.Int).SubUint64(currentBlock.Number64(), 1)) 473 if currentHeader == nil { 474 break 475 } 476 currentBlock = api.chain.GetBlock(currentHeader.Hash(), currentHeader.Number64().Uint64()) 477 } 478 479 return &VerifiedBlockResponse{ 480 MinedBlocks: minedBlocks, 481 Total: uint256.NewInt(findCount), 482 }, nil 483 } 484 485 func (api *API) GetAccountRewardUnpaid(address types.Address) (val *uint256.Int, err error) { 486 rewardService := newReward(api.apos.chainConfig) 487 488 api.apos.db.View(context.Background(), func(tx kv.Tx) error { 489 val, err = rewardService.getAccountRewardUnpaid(tx, address) 490 return err 491 }) 492 return 493 }