github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/statefetcher/fetcher.go (about) 1 package statefetcher 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "strconv" 8 "strings" 9 10 "github.com/pkg/errors" 11 types "github.com/prysmaticlabs/eth2-types" 12 "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" 13 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 14 "github.com/prysmaticlabs/prysm/beacon-chain/db" 15 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 16 "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" 17 "github.com/prysmaticlabs/prysm/shared/bytesutil" 18 ) 19 20 // StateIdParseError represents an error scenario where a state ID could not be parsed. 21 type StateIdParseError struct { 22 message string 23 } 24 25 // NewStateIdParseError creates a new error instance. 26 func NewStateIdParseError(reason error) StateIdParseError { 27 return StateIdParseError{ 28 message: errors.Wrapf(reason, "could not parse state ID").Error(), 29 } 30 } 31 32 // Error returns the underlying error message. 33 func (e *StateIdParseError) Error() string { 34 return e.message 35 } 36 37 // StateNotFoundError represents an error scenario where a state could not be found. 38 type StateNotFoundError struct { 39 message string 40 } 41 42 // NewStateNotFoundError creates a new error instance. 43 func NewStateNotFoundError(stateRootsSize int) StateNotFoundError { 44 return StateNotFoundError{ 45 message: fmt.Sprintf("state not found in the last %d state roots", stateRootsSize), 46 } 47 } 48 49 // Error returns the underlying error message. 50 func (e *StateNotFoundError) Error() string { 51 return e.message 52 } 53 54 // StateRootNotFoundError represents an error scenario where a state root could not be found. 55 type StateRootNotFoundError struct { 56 message string 57 } 58 59 // NewStateRootNotFoundError creates a new error instance. 60 func NewStateRootNotFoundError(stateRootsSize int) StateNotFoundError { 61 return StateNotFoundError{ 62 message: fmt.Sprintf("state root not found in the last %d state roots", stateRootsSize), 63 } 64 } 65 66 // Error returns the underlying error message. 67 func (e *StateRootNotFoundError) Error() string { 68 return e.message 69 } 70 71 // Fetcher is responsible for retrieving info related with the beacon chain. 72 type Fetcher interface { 73 State(ctx context.Context, stateId []byte) (iface.BeaconState, error) 74 StateRoot(ctx context.Context, stateId []byte) ([]byte, error) 75 } 76 77 // StateProvider is a real implementation of Fetcher. 78 type StateProvider struct { 79 BeaconDB db.ReadOnlyDatabase 80 ChainInfoFetcher blockchain.ChainInfoFetcher 81 GenesisTimeFetcher blockchain.TimeFetcher 82 StateGenService stategen.StateManager 83 } 84 85 // State returns the BeaconState for a given identifier. The identifier can be one of: 86 // - "head" (canonical head in node's view) 87 // - "genesis" 88 // - "finalized" 89 // - "justified" 90 // - <slot> 91 // - <hex encoded state root with '0x' prefix> 92 func (p *StateProvider) State(ctx context.Context, stateId []byte) (iface.BeaconState, error) { 93 var ( 94 s iface.BeaconState 95 err error 96 ) 97 98 stateIdString := strings.ToLower(string(stateId)) 99 switch stateIdString { 100 case "head": 101 s, err = p.ChainInfoFetcher.HeadState(ctx) 102 if err != nil { 103 return nil, errors.Wrap(err, "could not get head state") 104 } 105 case "genesis": 106 s, err = p.BeaconDB.GenesisState(ctx) 107 if err != nil { 108 return nil, errors.Wrap(err, "could not get genesis state") 109 } 110 case "finalized": 111 checkpoint := p.ChainInfoFetcher.FinalizedCheckpt() 112 s, err = p.StateGenService.StateByRoot(ctx, bytesutil.ToBytes32(checkpoint.Root)) 113 if err != nil { 114 return nil, errors.Wrap(err, "could not get finalized state") 115 } 116 case "justified": 117 checkpoint := p.ChainInfoFetcher.CurrentJustifiedCheckpt() 118 s, err = p.StateGenService.StateByRoot(ctx, bytesutil.ToBytes32(checkpoint.Root)) 119 if err != nil { 120 return nil, errors.Wrap(err, "could not get justified state") 121 } 122 default: 123 if len(stateId) == 32 { 124 s, err = p.stateByHex(ctx, stateId) 125 } else { 126 slotNumber, parseErr := strconv.ParseUint(stateIdString, 10, 64) 127 if parseErr != nil { 128 // ID format does not match any valid options. 129 e := NewStateIdParseError(parseErr) 130 return nil, &e 131 } 132 s, err = p.stateBySlot(ctx, types.Slot(slotNumber)) 133 } 134 } 135 136 return s, err 137 } 138 139 // StateRoot returns a beacon state root for a given identifier. The identifier can be one of: 140 // - "head" (canonical head in node's view) 141 // - "genesis" 142 // - "finalized" 143 // - "justified" 144 // - <slot> 145 // - <hex encoded state root with '0x' prefix> 146 func (p *StateProvider) StateRoot(ctx context.Context, stateId []byte) (root []byte, err error) { 147 stateIdString := strings.ToLower(string(stateId)) 148 switch stateIdString { 149 case "head": 150 root, err = p.headStateRoot(ctx) 151 case "genesis": 152 root, err = p.genesisStateRoot(ctx) 153 case "finalized": 154 root, err = p.finalizedStateRoot(ctx) 155 case "justified": 156 root, err = p.justifiedStateRoot(ctx) 157 default: 158 if len(stateId) == 32 { 159 root, err = p.stateRootByHex(ctx, stateId) 160 } else { 161 slotNumber, parseErr := strconv.ParseUint(stateIdString, 10, 64) 162 if parseErr != nil { 163 e := NewStateIdParseError(parseErr) 164 // ID format does not match any valid options. 165 return nil, &e 166 } 167 root, err = p.stateRootBySlot(ctx, types.Slot(slotNumber)) 168 } 169 } 170 171 return root, err 172 } 173 174 func (p *StateProvider) stateByHex(ctx context.Context, stateId []byte) (iface.BeaconState, error) { 175 headState, err := p.ChainInfoFetcher.HeadState(ctx) 176 if err != nil { 177 return nil, errors.Wrap(err, "could not get head state") 178 } 179 for i, root := range headState.StateRoots() { 180 if bytes.Equal(root, stateId) { 181 blockRoot := headState.BlockRoots()[i] 182 return p.StateGenService.StateByRoot(ctx, bytesutil.ToBytes32(blockRoot)) 183 } 184 } 185 186 stateNotFoundErr := NewStateNotFoundError(len(headState.StateRoots())) 187 return nil, &stateNotFoundErr 188 } 189 190 func (p *StateProvider) stateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) { 191 currentSlot := p.GenesisTimeFetcher.CurrentSlot() 192 if slot > currentSlot { 193 return nil, errors.New("slot cannot be in the future") 194 } 195 state, err := p.StateGenService.StateBySlot(ctx, slot) 196 if err != nil { 197 return nil, errors.Wrap(err, "could not get state") 198 } 199 return state, nil 200 } 201 202 func (p *StateProvider) headStateRoot(ctx context.Context) ([]byte, error) { 203 b, err := p.ChainInfoFetcher.HeadBlock(ctx) 204 if err != nil { 205 return nil, errors.Wrap(err, "could not get head block") 206 } 207 if err := helpers.VerifyNilBeaconBlock(b); err != nil { 208 return nil, err 209 } 210 return b.Block().StateRoot(), nil 211 } 212 213 func (p *StateProvider) genesisStateRoot(ctx context.Context) ([]byte, error) { 214 b, err := p.BeaconDB.GenesisBlock(ctx) 215 if err != nil { 216 return nil, errors.Wrap(err, "could not get genesis block") 217 } 218 if err := helpers.VerifyNilBeaconBlock(b); err != nil { 219 return nil, err 220 } 221 return b.Block().StateRoot(), nil 222 } 223 224 func (p *StateProvider) finalizedStateRoot(ctx context.Context) ([]byte, error) { 225 cp, err := p.BeaconDB.FinalizedCheckpoint(ctx) 226 if err != nil { 227 return nil, errors.Wrap(err, "could not get finalized checkpoint") 228 } 229 b, err := p.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root)) 230 if err != nil { 231 return nil, errors.Wrap(err, "could not get finalized block") 232 } 233 if err := helpers.VerifyNilBeaconBlock(b); err != nil { 234 return nil, err 235 } 236 return b.Block().StateRoot(), nil 237 } 238 239 func (p *StateProvider) justifiedStateRoot(ctx context.Context) ([]byte, error) { 240 cp, err := p.BeaconDB.JustifiedCheckpoint(ctx) 241 if err != nil { 242 return nil, errors.Wrap(err, "could not get justified checkpoint") 243 } 244 b, err := p.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root)) 245 if err != nil { 246 return nil, errors.Wrap(err, "could not get justified block") 247 } 248 if err := helpers.VerifyNilBeaconBlock(b); err != nil { 249 return nil, err 250 } 251 return b.Block().StateRoot(), nil 252 } 253 254 func (p *StateProvider) stateRootByHex(ctx context.Context, stateId []byte) ([]byte, error) { 255 var stateRoot [32]byte 256 copy(stateRoot[:], stateId) 257 headState, err := p.ChainInfoFetcher.HeadState(ctx) 258 if err != nil { 259 return nil, errors.Wrap(err, "could not get head state") 260 } 261 for _, root := range headState.StateRoots() { 262 if bytes.Equal(root, stateRoot[:]) { 263 return stateRoot[:], nil 264 } 265 } 266 267 rootNotFoundErr := NewStateRootNotFoundError(len(headState.StateRoots())) 268 return nil, &rootNotFoundErr 269 } 270 271 func (p *StateProvider) stateRootBySlot(ctx context.Context, slot types.Slot) ([]byte, error) { 272 currentSlot := p.GenesisTimeFetcher.CurrentSlot() 273 if slot > currentSlot { 274 return nil, errors.New("slot cannot be in the future") 275 } 276 found, blks, err := p.BeaconDB.BlocksBySlot(ctx, slot) 277 if err != nil { 278 return nil, errors.Wrap(err, "could not get blocks") 279 } 280 if !found { 281 return nil, errors.New("no block exists") 282 } 283 if len(blks) != 1 { 284 return nil, errors.New("multiple blocks exist in same slot") 285 } 286 if blks[0] == nil || blks[0].Block() == nil { 287 return nil, errors.New("nil block") 288 } 289 return blks[0].Block().StateRoot(), nil 290 }