github.com/amazechain/amc@v0.1.3/internal/consensus/apoa/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 apoa 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "github.com/amazechain/amc/common/block" 23 "github.com/amazechain/amc/common/hexutil" 24 "github.com/amazechain/amc/common/types" 25 "github.com/amazechain/amc/internal/avm/common" 26 mvm_types "github.com/amazechain/amc/internal/avm/types" 27 "github.com/amazechain/amc/internal/consensus" 28 "github.com/amazechain/amc/modules/rpc/jsonrpc" 29 "github.com/holiman/uint256" 30 ) 31 32 // API is a user facing jsonrpc API to allow controlling the signer and voting 33 // mechanisms of the proof-of-authority scheme. 34 type API struct { 35 chain consensus.ChainReader 36 apoa *Apoa 37 } 38 39 // GetSnapshot retrieves the state snapshot at a given block. 40 func (api *API) GetSnapshot(number *jsonrpc.BlockNumber) (*Snapshot, error) { 41 // Retrieve the requested block number (or current if none requested) 42 var header block.IHeader 43 if number == nil || *number == jsonrpc.LatestBlockNumber { 44 header = api.chain.CurrentBlock().Header() 45 } else { 46 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 47 } 48 // Ensure we have an actually valid block and return its snapshot 49 if header == nil { 50 return nil, errUnknownBlock 51 } 52 return api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 53 } 54 55 // GetSnapshotAtHash retrieves the state snapshot at a given block. 56 func (api *API) GetSnapshotAtHash(hash types.Hash) (*Snapshot, error) { 57 header, _ := api.chain.GetHeaderByHash(hash) 58 if header == nil { 59 return nil, errUnknownBlock 60 } 61 return api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 62 } 63 64 // GetSigners retrieves the list of authorized signers at the specified block. 65 func (api *API) GetSigners(number *jsonrpc.BlockNumber) ([]common.Address, error) { 66 // Retrieve the requested block number (or current if none requested) 67 var header block.IHeader 68 if number == nil || *number == jsonrpc.LatestBlockNumber { 69 header = api.chain.CurrentBlock().Header() 70 } else { 71 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 72 } 73 // Ensure we have an actually valid block and return the signers from its snapshot 74 if header == nil { 75 return nil, errUnknownBlock 76 } 77 snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 78 if err != nil { 79 return nil, err 80 } 81 82 signers := snap.signers() 83 ethSigners := make([]common.Address, len(signers)) 84 for i, signer := range signers { 85 ethSigners[i] = *mvm_types.FromAmcAddress(&signer) 86 } 87 return ethSigners, nil 88 } 89 90 // GetSignersAtHash retrieves the list of authorized signers at the specified block. 91 func (api *API) GetSignersAtHash(hash types.Hash) ([]common.Address, error) { 92 header, _ := api.chain.GetHeaderByHash(hash) 93 if header == nil { 94 return nil, errUnknownBlock 95 } 96 snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 97 if err != nil { 98 return nil, err 99 } 100 signers := snap.signers() 101 ethSigners := make([]common.Address, len(signers)) 102 for i, signer := range signers { 103 ethSigners[i] = *mvm_types.FromAmcAddress(&signer) 104 } 105 return ethSigners, nil 106 } 107 108 // Proposals returns the current proposals the node tries to uphold and vote on. 109 func (api *API) Proposals() map[common.Address]bool { 110 api.apoa.lock.RLock() 111 defer api.apoa.lock.RUnlock() 112 113 proposals := make(map[common.Address]bool) 114 for address, auth := range api.apoa.proposals { 115 proposals[*mvm_types.FromAmcAddress(&address)] = auth 116 } 117 return proposals 118 } 119 120 // Propose injects a new authorization proposal that the signer will attempt to 121 // push through. 122 func (api *API) Propose(address common.Address, auth bool) { 123 api.apoa.lock.Lock() 124 defer api.apoa.lock.Unlock() 125 126 api.apoa.proposals[*mvm_types.ToAmcAddress(&address)] = auth 127 } 128 129 // Discard drops a currently running proposal, stopping the signer from casting 130 // further votes (either for or against). 131 func (api *API) Discard(address common.Address) { 132 api.apoa.lock.Lock() 133 defer api.apoa.lock.Unlock() 134 135 delete(api.apoa.proposals, *mvm_types.ToAmcAddress(&address)) 136 } 137 138 type status struct { 139 InturnPercent float64 `json:"inturnPercent"` 140 SigningStatus map[types.Address]int `json:"sealerActivity"` 141 NumBlocks uint64 `json:"numBlocks"` 142 } 143 144 // Status returns the status of the last N blocks, 145 // - the number of active signers, 146 // - the number of signers, 147 // - the percentage of in-turn blocks 148 func (api *API) Status() (*status, error) { 149 var ( 150 numBlocks = uint64(64) 151 header = api.chain.CurrentBlock().Header() 152 diff = uint64(0) 153 optimals = 0 154 ) 155 snap, err := api.apoa.snapshot(api.chain, header.Number64().Uint64(), header.Hash(), nil) 156 if err != nil { 157 return nil, err 158 } 159 var ( 160 signers = snap.signers() 161 end = header.Number64().Uint64() 162 start = end - numBlocks 163 ) 164 if numBlocks > end { 165 start = 1 166 numBlocks = end - start 167 } 168 signStatus := make(map[types.Address]int) 169 for _, s := range signers { 170 signStatus[s] = 0 171 } 172 for n := start; n < end; n++ { 173 h := api.chain.GetHeaderByNumber(uint256.NewInt(n)) 174 block := api.chain.GetBlock(h.Hash(), n) 175 if h == nil { 176 return nil, fmt.Errorf("missing block %d", n) 177 } 178 if block.Difficulty().Cmp(diffInTurn) == 0 { 179 optimals++ 180 } 181 diff += block.Difficulty().Uint64() 182 sealer, err := api.apoa.Author(h) 183 if err != nil { 184 return nil, err 185 } 186 signStatus[sealer]++ 187 } 188 return &status{ 189 InturnPercent: float64(100*optimals) / float64(numBlocks), 190 SigningStatus: signStatus, 191 NumBlocks: numBlocks, 192 }, nil 193 } 194 195 type blockNumberOrHashOrRLP struct { 196 *jsonrpc.BlockNumberOrHash 197 RLP hexutil.Bytes `json:"rlp,omitempty"` 198 } 199 200 func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error { 201 bnOrHash := new(jsonrpc.BlockNumberOrHash) 202 // Try to unmarshal bNrOrHash 203 if err := bnOrHash.UnmarshalJSON(data); err == nil { 204 sb.BlockNumberOrHash = bnOrHash 205 return nil 206 } 207 // Try to unmarshal RLP 208 var input string 209 if err := json.Unmarshal(data, &input); err != nil { 210 return err 211 } 212 blob, err := hexutil.Decode(input) 213 if err != nil { 214 return err 215 } 216 sb.RLP = blob 217 return nil 218 } 219 220 // GetSigner returns the signer for a specific apoa block. 221 // Can be called with either a blocknumber, blockhash or an rlp encoded blob. 222 // The RLP encoded blob can either be a block or a header. 223 func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (types.Address, error) { 224 if len(rlpOrBlockNr.RLP) == 0 { 225 blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash 226 var header block.IHeader 227 if blockNrOrHash == nil { 228 header = api.chain.CurrentBlock().Header() 229 } else if hash, ok := blockNrOrHash.Hash(); ok { 230 header, _ = api.chain.GetHeaderByHash(hash) 231 } else if number, ok := blockNrOrHash.Number(); ok { 232 header = api.chain.GetHeaderByNumber(uint256.NewInt(uint64(number.Int64()))) 233 } 234 if header == nil { 235 return types.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String()) 236 } 237 return api.apoa.Author(header) 238 } 239 240 return types.Address{}, fmt.Errorf("do not support rlp") 241 }