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 }