github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/contract_reader.go (about)

     1  package environment
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/cadence/runtime"
     7  	"github.com/onflow/cadence/runtime/ast"
     8  	"github.com/onflow/cadence/runtime/common"
     9  
    10  	"github.com/onflow/flow-go/fvm/errors"
    11  	"github.com/onflow/flow-go/fvm/tracing"
    12  	"github.com/onflow/flow-go/model/flow"
    13  	"github.com/onflow/flow-go/module/trace"
    14  )
    15  
    16  // ContractReader provide read access to contracts.
    17  type ContractReader struct {
    18  	tracer tracing.TracerSpan
    19  	meter  Meter
    20  
    21  	accounts Accounts
    22  }
    23  
    24  func NewContractReader(
    25  	tracer tracing.TracerSpan,
    26  	meter Meter,
    27  	accounts Accounts,
    28  ) *ContractReader {
    29  	return &ContractReader{
    30  		tracer:   tracer,
    31  		meter:    meter,
    32  		accounts: accounts,
    33  	}
    34  }
    35  
    36  func (reader *ContractReader) GetAccountContractNames(
    37  	runtimeAddress common.Address,
    38  ) (
    39  	[]string,
    40  	error,
    41  ) {
    42  	defer reader.tracer.StartChildSpan(
    43  		trace.FVMEnvGetAccountContractNames).End()
    44  
    45  	err := reader.meter.MeterComputation(
    46  		ComputationKindGetAccountContractNames,
    47  		1)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("get account contract names failed: %w", err)
    50  	}
    51  
    52  	address := flow.ConvertAddress(runtimeAddress)
    53  
    54  	return reader.accounts.GetContractNames(address)
    55  }
    56  
    57  func (reader *ContractReader) ResolveLocation(
    58  	identifiers []runtime.Identifier,
    59  	location runtime.Location,
    60  ) (
    61  	[]runtime.ResolvedLocation,
    62  	error,
    63  ) {
    64  	defer reader.tracer.StartExtensiveTracingChildSpan(
    65  		trace.FVMEnvResolveLocation).End()
    66  
    67  	err := reader.meter.MeterComputation(ComputationKindResolveLocation, 1)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("resolve location failed: %w", err)
    70  	}
    71  
    72  	addressLocation, isAddress := location.(common.AddressLocation)
    73  
    74  	// if the location is not an address location, e.g. an identifier location
    75  	// (`import Crypto`), then return a single resolved location which declares
    76  	// all identifiers.
    77  	if !isAddress {
    78  		return []runtime.ResolvedLocation{
    79  			{
    80  				Location:    location,
    81  				Identifiers: identifiers,
    82  			},
    83  		}, nil
    84  	}
    85  
    86  	// if the location is an address,
    87  	// and no specific identifiers where requested in the import statement,
    88  	// then fetch all identifiers at this address
    89  	if len(identifiers) == 0 {
    90  		address := flow.ConvertAddress(addressLocation.Address)
    91  
    92  		contractNames, err := reader.accounts.GetContractNames(address)
    93  		if err != nil {
    94  			return nil, fmt.Errorf("resolving location failed: %w", err)
    95  		}
    96  
    97  		// if there are no contractNames deployed,
    98  		// then return no resolved locations
    99  		if len(contractNames) == 0 {
   100  			return nil, nil
   101  		}
   102  
   103  		identifiers = make([]ast.Identifier, len(contractNames))
   104  
   105  		for i := range identifiers {
   106  			identifiers[i] = runtime.Identifier{
   107  				Identifier: contractNames[i],
   108  			}
   109  		}
   110  	}
   111  
   112  	// return one resolved location per identifier.
   113  	// each resolved location is an address contract location
   114  	resolvedLocations := make([]runtime.ResolvedLocation, len(identifiers))
   115  	for i := range resolvedLocations {
   116  		identifier := identifiers[i]
   117  		resolvedLocations[i] = runtime.ResolvedLocation{
   118  			Location: common.AddressLocation{
   119  				Address: addressLocation.Address,
   120  				Name:    identifier.Identifier,
   121  			},
   122  			Identifiers: []runtime.Identifier{identifier},
   123  		}
   124  	}
   125  
   126  	return resolvedLocations, nil
   127  }
   128  
   129  func (reader *ContractReader) getCode(
   130  	location common.AddressLocation,
   131  ) (
   132  	[]byte,
   133  	error,
   134  ) {
   135  	defer reader.tracer.StartChildSpan(trace.FVMEnvGetCode).End()
   136  
   137  	err := reader.meter.MeterComputation(ComputationKindGetCode, 1)
   138  	if err != nil {
   139  		return nil, fmt.Errorf("get code failed: %w", err)
   140  	}
   141  
   142  	add, err := reader.accounts.GetContract(location.Name, flow.ConvertAddress(location.Address))
   143  	if err != nil {
   144  		return nil, fmt.Errorf("get code failed: %w", err)
   145  	}
   146  
   147  	return add, nil
   148  }
   149  
   150  func (reader *ContractReader) GetCode(
   151  	location runtime.Location,
   152  ) (
   153  	[]byte,
   154  	error,
   155  ) {
   156  	contractLocation, ok := location.(common.AddressLocation)
   157  	if !ok {
   158  		return nil, errors.NewInvalidLocationErrorf(
   159  			location,
   160  			"expecting an AddressLocation, but other location types are passed")
   161  	}
   162  
   163  	return reader.getCode(contractLocation)
   164  }
   165  
   166  func (reader *ContractReader) GetAccountContractCode(
   167  	location common.AddressLocation,
   168  ) (
   169  	[]byte,
   170  	error,
   171  ) {
   172  	defer reader.tracer.StartChildSpan(
   173  		trace.FVMEnvGetAccountContractCode).End()
   174  
   175  	err := reader.meter.MeterComputation(
   176  		ComputationKindGetAccountContractCode,
   177  		1)
   178  	if err != nil {
   179  		return nil, fmt.Errorf("get account contract code failed: %w", err)
   180  	}
   181  
   182  	code, err := reader.getCode(location)
   183  	if err != nil {
   184  		return nil, fmt.Errorf("get account contract code failed: %w", err)
   185  	}
   186  
   187  	return code, nil
   188  }