Skip to content

Commit

Permalink
Merge pull request #3417 from onflow/supun/contract-mutability
Browse files Browse the repository at this point in the history
Import contracts as references
  • Loading branch information
SupunS authored Jun 24, 2024
2 parents 57a46f8 + f69da85 commit a1b1e4e
Show file tree
Hide file tree
Showing 10 changed files with 546 additions and 30 deletions.
59 changes: 44 additions & 15 deletions runtime/coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/onflow/cadence/runtime/parser"
"github.com/onflow/cadence/runtime/stdlib"
. "github.com/onflow/cadence/runtime/tests/runtime_utils"
"github.com/onflow/cadence/runtime/tests/utils"
)

func TestRuntimeNewLocationCoverage(t *testing.T) {
Expand Down Expand Up @@ -1768,46 +1769,74 @@ func TestRuntimeCoverageWithNoStatements(t *testing.T) {

t.Parallel()

importedScript := []byte(`
contract := []byte(`
access(all) contract FooContract {
access(all) resource interface Receiver {
}
}
`)

script := []byte(`
import "FooContract"
import FooContract from 0x1
access(all) fun main(): Int {
Type<@{FooContract.Receiver}>().identifier
return 42
}
`)

coverageReport := NewCoverageReport()
runtime := NewInterpreterRuntime(Config{
CoverageReport: coverageReport,
})

scriptlocation := common.ScriptLocation{0x1b, 0x2c}
scriptLocation := common.ScriptLocation{0x1b, 0x2c}

transactionLocation := NewTransactionLocationGenerator()
txLocation := transactionLocation()

authorizers := []Address{{0, 0, 0, 0, 0, 0, 0, 1}}
accountCodes := map[Location][]byte{}

runtimeInterface := &TestRuntimeInterface{
OnGetCode: func(location Location) (bytes []byte, err error) {
switch location {
case common.StringLocation("FooContract"):
return importedScript, nil
default:
return nil, fmt.Errorf("unknown import location: %s", location)
}
Storage: NewTestLedger(nil, nil),
OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
return accountCodes[location], nil
},
OnGetSigningAccounts: func() ([]Address, error) {
return authorizers, nil
},
OnEmitEvent: func(event cadence.Event) error {
return nil
},
OnResolveLocation: NewSingleIdentifierLocationResolver(t),
}
runtime := NewInterpreterRuntime(Config{
CoverageReport: coverageReport,
})
coverageReport.ExcludeLocation(scriptlocation)

coverageReport.ExcludeLocation(txLocation)
deploy := utils.DeploymentTransaction("FooContract", contract)
err := runtime.ExecuteTransaction(
Script{
Source: deploy,
},
Context{
Interface: runtimeInterface,
Location: txLocation,
CoverageReport: coverageReport,
},
)
require.NoError(t, err)

coverageReport.ExcludeLocation(scriptLocation)
value, err := runtime.ExecuteScript(
Script{
Source: script,
},
Context{
Interface: runtimeInterface,
Location: scriptlocation,
Location: scriptLocation,
CoverageReport: coverageReport,
},
)
Expand Down
93 changes: 93 additions & 0 deletions runtime/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,96 @@ func TestRuntimeCheckCyclicImportToSelfDuringDeploy(t *testing.T) {
errs := checker.RequireCheckerErrors(t, checkerErr, 1)
require.IsType(t, &sema.CyclicImportsError{}, errs[0])
}

func TestRuntimeContractImport(t *testing.T) {

t.Parallel()

addressValue := Address{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
}

runtime := NewTestInterpreterRuntime()

contract := []byte(`
access(all) contract Foo {
access(all) let x: [Int]
access(all) fun answer(): Int {
return 42
}
access(all) struct Bar {}
init() {
self.x = []
}
}`,
)

deploy := DeploymentTransaction("Foo", contract)

script := []byte(`
import Foo from 0x01
access(all) fun main() {
var foo: &Foo = Foo
var x: &[Int] = Foo.x
var bar: Foo.Bar = Foo.Bar()
}
`)

accountCodes := map[Location][]byte{}
var events []cadence.Event

runtimeInterface := &TestRuntimeInterface{
OnGetCode: func(location Location) (bytes []byte, err error) {
return accountCodes[location], nil
},
Storage: NewTestLedger(nil, nil),
OnGetSigningAccounts: func() ([]Address, error) {
return []Address{addressValue}, nil
},
OnResolveLocation: NewSingleIdentifierLocationResolver(t),
OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
return accountCodes[location], nil
},
OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
OnCreateAccount: func(payer Address) (address Address, err error) {
return addressValue, nil
},
OnEmitEvent: func(event cadence.Event) error {
events = append(events, event)
return nil
},
}

nextTransactionLocation := NewTransactionLocationGenerator()

err := runtime.ExecuteTransaction(
Script{
Source: deploy,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

nextScriptLocation := NewScriptLocationGenerator()

_, err = runtime.ExecuteScript(
Script{
Source: script,
},
Context{
Interface: runtimeInterface,
Location: nextScriptLocation(),
},
)
require.NoError(t, err)
}
38 changes: 35 additions & 3 deletions runtime/interpreter/interpreter_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/sema"
)

Expand Down Expand Up @@ -83,9 +84,40 @@ func (interpreter *Interpreter) importResolvedLocation(resolvedLocation sema.Res

for _, name := range names {
variable := variables[name]
if variable == nil {
continue
}

interpreter.setVariable(name, variable)
interpreter.Globals.Set(name, variable)
}
// Lazily load the value
getter := func() Value {
value := variable.GetValue(interpreter)

// If the variable is a contract value, then import it as a reference.
// This must be done at the type of importing, rather than when declaring the contract value.
compositeValue, ok := value.(*CompositeValue)
if !ok || compositeValue.Kind != common.CompositeKindContract {
return value
}

staticType := compositeValue.StaticType(interpreter)
semaType, err := interpreter.ConvertStaticToSemaType(staticType)
if err != nil {
panic(err)
}

return NewEphemeralReferenceValue(
interpreter,
UnauthorizedAccess,
compositeValue,
semaType,
LocationRange{
Location: interpreter.Location,
},
)
}

importedVariable := NewVariableWithGetter(interpreter, getter)
interpreter.setVariable(name, importedVariable)
interpreter.Globals.Set(name, importedVariable)
}
}
35 changes: 33 additions & 2 deletions runtime/program_params_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,26 +698,57 @@ func TestRuntimeTransactionParameterTypeValidation(t *testing.T) {

storage := NewTestLedger(nil, nil)

authorizers := []Address{{0, 0, 0, 0, 0, 0, 0, 1}}
accountCodes := map[Location][]byte{}

runtimeInterface := &TestRuntimeInterface{
Storage: storage,
OnResolveLocation: NewSingleIdentifierLocationResolver(t),
OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
return contracts[location], nil
return accountCodes[location], nil
},
OnDecodeArgument: func(b []byte, t cadence.Type) (value cadence.Value, err error) {
return json.Decode(nil, b)
},
OnEmitEvent: func(event cadence.Event) error {
return nil
},
OnGetSigningAccounts: func() ([]Address, error) {
return authorizers, nil
},
}
addPublicKeyValidation(runtimeInterface, nil)

transactionLocation := NewTransactionLocationGenerator()
for location, contract := range contracts {
deploy := DeploymentTransaction(location.Name, contract)
err := rt.ExecuteTransaction(
Script{
Source: deploy,
},
Context{
Interface: runtimeInterface,
Location: transactionLocation(),
},
)

require.NoError(t, err)
}

authorizers = nil

return rt.ExecuteTransaction(
Script{
Source: []byte(script),
Arguments: [][]byte{encodedArg},
},
Context{
Interface: runtimeInterface,
Location: common.TransactionLocation{},
Location: transactionLocation(),
},
)
}
Expand Down
Loading

0 comments on commit a1b1e4e

Please sign in to comment.