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  }