github.com/koko1123/flow-go-1@v0.29.6/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/koko1123/flow-go-1/fvm/errors"
    11  	"github.com/koko1123/flow-go-1/fvm/tracing"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  	"github.com/koko1123/flow-go-1/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  	address runtime.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  	a := flow.Address(address)
    53  
    54  	freezeError := reader.accounts.CheckAccountNotFrozen(a)
    55  	if freezeError != nil {
    56  		return nil, fmt.Errorf(
    57  			"get account contract names failed: %w",
    58  			freezeError)
    59  	}
    60  
    61  	return reader.accounts.GetContractNames(a)
    62  }
    63  
    64  func (reader *ContractReader) ResolveLocation(
    65  	identifiers []runtime.Identifier,
    66  	location runtime.Location,
    67  ) (
    68  	[]runtime.ResolvedLocation,
    69  	error,
    70  ) {
    71  	defer reader.tracer.StartExtensiveTracingChildSpan(
    72  		trace.FVMEnvResolveLocation).End()
    73  
    74  	err := reader.meter.MeterComputation(ComputationKindResolveLocation, 1)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("resolve location failed: %w", err)
    77  	}
    78  
    79  	addressLocation, isAddress := location.(common.AddressLocation)
    80  
    81  	// if the location is not an address location, e.g. an identifier location
    82  	// (`import Crypto`), then return a single resolved location which declares
    83  	// all identifiers.
    84  	if !isAddress {
    85  		return []runtime.ResolvedLocation{
    86  			{
    87  				Location:    location,
    88  				Identifiers: identifiers,
    89  			},
    90  		}, nil
    91  	}
    92  
    93  	// if the location is an address,
    94  	// and no specific identifiers where requested in the import statement,
    95  	// then fetch all identifiers at this address
    96  	if len(identifiers) == 0 {
    97  		address := flow.Address(addressLocation.Address)
    98  
    99  		err := reader.accounts.CheckAccountNotFrozen(address)
   100  		if err != nil {
   101  			return nil, fmt.Errorf(
   102  				"resolving location's account frozen check failed: %w",
   103  				err)
   104  		}
   105  
   106  		contractNames, err := reader.accounts.GetContractNames(address)
   107  		if err != nil {
   108  			return nil, fmt.Errorf("resolving location failed: %w", err)
   109  		}
   110  
   111  		// if there are no contractNames deployed,
   112  		// then return no resolved locations
   113  		if len(contractNames) == 0 {
   114  			return nil, nil
   115  		}
   116  
   117  		identifiers = make([]ast.Identifier, len(contractNames))
   118  
   119  		for i := range identifiers {
   120  			identifiers[i] = runtime.Identifier{
   121  				Identifier: contractNames[i],
   122  			}
   123  		}
   124  	}
   125  
   126  	// return one resolved location per identifier.
   127  	// each resolved location is an address contract location
   128  	resolvedLocations := make([]runtime.ResolvedLocation, len(identifiers))
   129  	for i := range resolvedLocations {
   130  		identifier := identifiers[i]
   131  		resolvedLocations[i] = runtime.ResolvedLocation{
   132  			Location: common.AddressLocation{
   133  				Address: addressLocation.Address,
   134  				Name:    identifier.Identifier,
   135  			},
   136  			Identifiers: []runtime.Identifier{identifier},
   137  		}
   138  	}
   139  
   140  	return resolvedLocations, nil
   141  }
   142  
   143  func (reader *ContractReader) GetCode(
   144  	location runtime.Location,
   145  ) (
   146  	[]byte,
   147  	error,
   148  ) {
   149  	defer reader.tracer.StartChildSpan(trace.FVMEnvGetCode).End()
   150  
   151  	err := reader.meter.MeterComputation(ComputationKindGetCode, 1)
   152  	if err != nil {
   153  		return nil, fmt.Errorf("get code failed: %w", err)
   154  	}
   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  	address := flow.Address(contractLocation.Address)
   164  
   165  	err = reader.accounts.CheckAccountNotFrozen(address)
   166  	if err != nil {
   167  		return nil, fmt.Errorf("get code failed: %w", err)
   168  	}
   169  
   170  	add, err := reader.accounts.GetContract(contractLocation.Name, address)
   171  	if err != nil {
   172  		return nil, fmt.Errorf("get code failed: %w", err)
   173  	}
   174  
   175  	return add, nil
   176  }
   177  
   178  func (reader *ContractReader) GetAccountContractCode(
   179  	address runtime.Address,
   180  	name string,
   181  ) (
   182  	code []byte,
   183  	err error,
   184  ) {
   185  	defer reader.tracer.StartChildSpan(
   186  		trace.FVMEnvGetAccountContractCode).End()
   187  
   188  	err = reader.meter.MeterComputation(
   189  		ComputationKindGetAccountContractCode,
   190  		1)
   191  	if err != nil {
   192  		return nil, fmt.Errorf("get account contract code failed: %w", err)
   193  	}
   194  
   195  	code, err = reader.GetCode(common.AddressLocation{
   196  		Address: address,
   197  		Name:    name,
   198  	})
   199  	if err != nil {
   200  		return nil, fmt.Errorf("get account contract code failed: %w", err)
   201  	}
   202  
   203  	return code, nil
   204  }