Skip to content

Commit

Permalink
Merge pull request #3061 from onflow/bastian/fix-optional-storage-ref…
Browse files Browse the repository at this point in the history
…erence-access
  • Loading branch information
turbolent authored Jan 31, 2024
2 parents 4092595 + 2414df1 commit 079e1f9
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 2 deletions.
11 changes: 9 additions & 2 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,15 @@ func (interpreter *Interpreter) checkMemberAccess(
}
}

if _, ok := target.(*StorageReferenceValue); ok {
// NOTE: Storage reference value accesses are already checked in StorageReferenceValue.dereference
// NOTE: accesses of (optional) storage reference values
// are already checked in StorageReferenceValue.dereference
_, isStorageReference := target.(*StorageReferenceValue)
if !isStorageReference {
if optional, ok := target.(*SomeValue); ok {
_, isStorageReference = optional.value.(*StorageReferenceValue)
}
}
if isStorageReference {
return
}

Expand Down
125 changes: 125 additions & 0 deletions runtime/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4192,3 +4192,128 @@ func TestRuntimeStorageIteration(t *testing.T) {
})
})
}

func TestRuntimeStorageReferenceAccess(t *testing.T) {

t.Parallel()

runtime := newTestInterpreterRuntime()
runtime.defaultConfig.ResourceOwnerChangeHandlerEnabled = true

address := common.MustBytesToAddress([]byte{0x1})

ledger := newTestLedger(nil, nil)

deployTx := DeploymentTransaction("Test", []byte(`
pub contract Test {
pub resource R {
pub var balance: Int
init() {
self.balance = 10
}
}
pub fun createR(): @R {
return <-create R()
}
}
`))

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

runtimeInterface := &testRuntimeInterface{
storage: ledger,
getSigningAccounts: func() ([]Address, error) {
return []Address{address}, nil
},
resolveLocation: singleIdentifierLocationResolver(t),
updateAccountContractCode: func(location common.AddressLocation, code []byte) error {
accountCodes[location] = code
return nil
},
getAccountContractCode: func(location common.AddressLocation) (code []byte, err error) {
code = accountCodes[location]
return code, nil
},
emitEvent: func(event cadence.Event) error {
events = append(events, event)
return nil
},
}

nextTransactionLocation := newTransactionLocationGenerator()

// Deploy contract

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

t.Run("top-level reference", func(t *testing.T) {

transferTx := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
signer.save(<-Test.createR(), to: /storage/test)
let ref = signer.borrow<&Test.R>(from: /storage/test)!
let value <- signer.load<@Test.R>(from: /storage/test)!
destroy value
ref.balance
}
}
`)

err = runtime.ExecuteTransaction(
Script{
Source: transferTx,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DereferenceError{})
})

t.Run("optional reference", func(t *testing.T) {

transferTx := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
signer.save(<-Test.createR(), to: /storage/test)
let ref = signer.borrow<&Test.R>(from: /storage/test)
let value <- signer.load<@Test.R>(from: /storage/test)!
destroy value
ref?.balance
}
}
`)

err = runtime.ExecuteTransaction(
Script{
Source: transferTx,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)
RequireError(t, err)
require.ErrorAs(t, err, &interpreter.DereferenceError{})
})
}

0 comments on commit 079e1f9

Please sign in to comment.