github.com/diadata-org/diadata@v1.4.593/pkg/dia/helpers/substrate-helper/gsrpc/registry/retriever/extrinsic_retriever.go (about)

     1  package retriever
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/registry"
     7  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/registry/exec"
     8  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/rpc/chain"
     9  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/rpc/state"
    10  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/types"
    11  	"github.com/diadata-org/diadata/pkg/dia/helpers/substrate-helper/gsrpc/types/block"
    12  )
    13  
    14  //nolint:lll
    15  //go:generate mockery --name ExtrinsicRetriever --structname ExtrinsicRetrieverMock --filename extrinsic_retriever_mock.go --inpackage
    16  
    17  // ExtrinsicRetriever is the interface used for retrieving and decoding extrinsic information
    18  // from a particular block.
    19  type ExtrinsicRetriever interface {
    20  	GetExtrinsics(blockHash types.Hash) ([]*registry.DecodedExtrinsic, error)
    21  }
    22  
    23  // extrinsicRetriever implements the ExtrinsicRetriever interface.
    24  type extrinsicRetriever struct {
    25  	chainRPC chain.Chain
    26  	stateRPC state.State
    27  
    28  	registryFactory registry.Factory
    29  
    30  	chainExecutor             exec.RetryableExecutor[*block.SignedBlock]
    31  	extrinsicDecodingExecutor exec.RetryableExecutor[[]*registry.DecodedExtrinsic]
    32  
    33  	extrinsicDecoder *registry.ExtrinsicDecoder
    34  }
    35  
    36  // NewExtrinsicRetriever creates a new ExtrinsicRetriever.
    37  func NewExtrinsicRetriever(
    38  	chainRPC chain.Chain,
    39  	stateRPC state.State,
    40  	registryFactory registry.Factory,
    41  	chainExecutor exec.RetryableExecutor[*block.SignedBlock],
    42  	extrinsicDecodingExecutor exec.RetryableExecutor[[]*registry.DecodedExtrinsic],
    43  ) (ExtrinsicRetriever, error) {
    44  	retriever := &extrinsicRetriever{
    45  		chainRPC:                  chainRPC,
    46  		stateRPC:                  stateRPC,
    47  		registryFactory:           registryFactory,
    48  		chainExecutor:             chainExecutor,
    49  		extrinsicDecodingExecutor: extrinsicDecodingExecutor,
    50  	}
    51  
    52  	if err := retriever.updateInternalState(nil); err != nil {
    53  		return nil, ErrInternalStateUpdate.Wrap(err)
    54  	}
    55  
    56  	return retriever, nil
    57  }
    58  
    59  // NewDefaultExtrinsicRetriever returns an ExtrinsicRetriever with default values for the factory and executors.
    60  func NewDefaultExtrinsicRetriever(
    61  	chainRPC chain.Chain,
    62  	stateRPC state.State,
    63  	fieldOverrides ...registry.FieldOverride,
    64  ) (ExtrinsicRetriever, error) {
    65  	registryFactory := registry.NewFactory(fieldOverrides...)
    66  
    67  	chainExecutor := exec.NewRetryableExecutor[*block.SignedBlock](exec.WithRetryTimeout(1 * time.Second))
    68  	extrinsicDecodingExecutor := exec.NewRetryableExecutor[[]*registry.DecodedExtrinsic](exec.WithMaxRetryCount(1))
    69  
    70  	return NewExtrinsicRetriever(
    71  		chainRPC,
    72  		stateRPC,
    73  		registryFactory,
    74  		chainExecutor,
    75  		extrinsicDecodingExecutor,
    76  	)
    77  }
    78  
    79  // GetExtrinsics retrieves a generic.SignedBlock and then parses the extrinsics found in it.
    80  //
    81  // Both the block retrieval and the extrinsic parsing are handled via the exec.RetryableExecutor
    82  // in order to ensure retries in case of network errors or parsing errors due to an outdated extrinsic decoder.
    83  func (e *extrinsicRetriever) GetExtrinsics(blockHash types.Hash) ([]*registry.DecodedExtrinsic, error) {
    84  	block, err := e.chainExecutor.ExecWithFallback(
    85  		func() (*block.SignedBlock, error) {
    86  			return e.chainRPC.GetBlock(blockHash)
    87  		},
    88  		func() error {
    89  			return nil
    90  		},
    91  	)
    92  
    93  	if err != nil {
    94  		return nil, ErrBlockRetrieval.Wrap(err)
    95  	}
    96  
    97  	calls, err := e.extrinsicDecodingExecutor.ExecWithFallback(
    98  		func() ([]*registry.DecodedExtrinsic, error) {
    99  			return block.DecodeExtrinsics(e.extrinsicDecoder)
   100  		},
   101  		func() error {
   102  			return e.updateInternalState(&blockHash)
   103  		},
   104  	)
   105  
   106  	if err != nil {
   107  		return nil, ErrExtrinsicDecoding.Wrap(err)
   108  	}
   109  
   110  	return calls, nil
   111  }
   112  
   113  // updateInternalState will retrieve the metadata at the provided blockHash, if provided,
   114  // create an extrinsic decoder based on this metadata and store both.
   115  func (e *extrinsicRetriever) updateInternalState(blockHash *types.Hash) error {
   116  	var (
   117  		meta *types.Metadata
   118  		err  error
   119  	)
   120  
   121  	if blockHash == nil {
   122  		meta, err = e.stateRPC.GetMetadataLatest()
   123  	} else {
   124  		meta, err = e.stateRPC.GetMetadata(*blockHash)
   125  	}
   126  
   127  	if err != nil {
   128  		return ErrMetadataRetrieval.Wrap(err)
   129  	}
   130  
   131  	extrinsicDecoder, err := e.registryFactory.CreateExtrinsicDecoder(meta)
   132  
   133  	if err != nil {
   134  		return ErrExtrinsicDecoderCreation.Wrap(err)
   135  	}
   136  
   137  	e.extrinsicDecoder = extrinsicDecoder
   138  
   139  	return nil
   140  }