Skip to content

Commit

Permalink
Merge pull request #2567 from onflow/sainati/capability-upcast-conver…
Browse files Browse the repository at this point in the history
…sion

Convert generic container types on upcast
  • Loading branch information
dsainati1 authored Jun 21, 2023
2 parents 4c889d8 + 585eaa4 commit 0c6e591
Show file tree
Hide file tree
Showing 4 changed files with 1,019 additions and 6 deletions.
319 changes: 319 additions & 0 deletions runtime/entitlements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,322 @@ func TestAccountEntitlementNamingConflict(t *testing.T) {
var accessError *sema.InvalidAccessError
require.ErrorAs(t, errs[0], &accessError)
}

func TestAccountEntitlementCapabilityCasting(t *testing.T) {
t.Parallel()

storage := newTestLedger(nil, nil)
rt := newTestInterpreterRuntimeWithAttachments()
accountCodes := map[Location][]byte{}

deployTx := DeploymentTransaction("Test", []byte(`
pub contract Test {
pub entitlement X
pub entitlement Y
pub resource R {}
pub fun createR(): @R {
return <-create R()
}
}
`))

transaction1 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let r <- Test.createR()
signer.save(<-r, to: /storage/foo)
signer.link<auth(Test.X) &Test.R>(/public/foo, target: /storage/foo)
}
}
`)

transaction2 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let capX = signer.getCapability<auth(Test.X) &Test.R>(/public/foo)
let upCap = capX as Capability<&Test.R>
let downCap = upCap as! Capability<auth(Test.X) &Test.R>
}
}
`)

runtimeInterface1 := &testRuntimeInterface{
storage: storage,
log: func(message string) {},
emitEvent: func(event cadence.Event) error {
return nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getSigningAccounts: func() ([]Address, error) {
return []Address{[8]byte{0, 0, 0, 0, 0, 0, 0, 1}}, nil
},
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
},
}

nextTransactionLocation := newTransactionLocationGenerator()

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

err = rt.ExecuteTransaction(
Script{
Source: transaction1,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

err = rt.ExecuteTransaction(
Script{
Source: transaction2,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)

require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{})
}

func TestAccountEntitlementCapabilityDictionary(t *testing.T) {
t.Parallel()

storage := newTestLedger(nil, nil)
rt := newTestInterpreterRuntimeWithAttachments()
accountCodes := map[Location][]byte{}

deployTx := DeploymentTransaction("Test", []byte(`
pub contract Test {
pub entitlement X
pub entitlement Y
pub resource R {}
pub fun createR(): @R {
return <-create R()
}
}
`))

transaction1 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let r <- Test.createR()
signer.save(<-r, to: /storage/foo)
signer.link<auth(Test.X) &Test.R>(/public/foo, target: /storage/foo)
let r2 <- Test.createR()
signer.save(<-r2, to: /storage/bar)
signer.link<auth(Test.Y) &Test.R>(/public/bar, target: /storage/bar)
}
}
`)

transaction2 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let capX = signer.getCapability<auth(Test.X) &Test.R>(/public/foo)
let capY = signer.getCapability<auth(Test.Y) &Test.R>(/public/bar)
let dict: {Type: Capability<&Test.R>} = {}
dict[capX.getType()] = capX
dict[capY.getType()] = capY
let newCapX = dict[capX.getType()]!
let ref = newCapX.borrow()!
let downCast = ref as! auth(Test.X) &Test.R
}
}
`)

runtimeInterface1 := &testRuntimeInterface{
storage: storage,
log: func(message string) {},
emitEvent: func(event cadence.Event) error {
return nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getSigningAccounts: func() ([]Address, error) {
return []Address{[8]byte{0, 0, 0, 0, 0, 0, 0, 1}}, nil
},
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
},
}

nextTransactionLocation := newTransactionLocationGenerator()

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

err = rt.ExecuteTransaction(
Script{
Source: transaction1,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

err = rt.ExecuteTransaction(
Script{
Source: transaction2,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)

require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{})
}

func TestAccountEntitlementGenericCapabilityDictionary(t *testing.T) {
t.Parallel()

storage := newTestLedger(nil, nil)
rt := newTestInterpreterRuntimeWithAttachments()
accountCodes := map[Location][]byte{}

deployTx := DeploymentTransaction("Test", []byte(`
pub contract Test {
pub entitlement X
pub entitlement Y
pub resource R {}
pub fun createR(): @R {
return <-create R()
}
}
`))

transaction1 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let r <- Test.createR()
signer.save(<-r, to: /storage/foo)
signer.link<auth(Test.X) &Test.R>(/public/foo, target: /storage/foo)
let r2 <- Test.createR()
signer.save(<-r2, to: /storage/bar)
signer.link<auth(Test.Y) &Test.R>(/public/bar, target: /storage/bar)
}
}
`)

transaction2 := []byte(`
import Test from 0x1
transaction {
prepare(signer: AuthAccount) {
let capX = signer.getCapability<auth(Test.X) &Test.R>(/public/foo)
let capY = signer.getCapability<auth(Test.Y) &Test.R>(/public/bar)
let dict: {Type: Capability} = {}
dict[capX.getType()] = capX
dict[capY.getType()] = capY
let newCapX = dict[capX.getType()]!
let ref = newCapX.borrow<auth(Test.X) &Test.R>()!
let downCast = ref as! auth(Test.X) &Test.R
}
}
`)

runtimeInterface1 := &testRuntimeInterface{
storage: storage,
log: func(message string) {},
emitEvent: func(event cadence.Event) error {
return nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getSigningAccounts: func() ([]Address, error) {
return []Address{[8]byte{0, 0, 0, 0, 0, 0, 0, 1}}, nil
},
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
},
}

nextTransactionLocation := newTransactionLocationGenerator()

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

err = rt.ExecuteTransaction(
Script{
Source: transaction1,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)
require.NoError(t, err)

err = rt.ExecuteTransaction(
Script{
Source: transaction2,
},
Context{
Interface: runtimeInterface1,
Location: nextTransactionLocation(),
},
)

require.NoError(t, err)
}
Loading

0 comments on commit 0c6e591

Please sign in to comment.