Skip to content

Commit

Permalink
Merge pull request #2366 from onflow/bastian/private-account-links
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Mar 3, 2023
2 parents 0f76c23 + 1775ebd commit d48265a
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 178 deletions.
1 change: 1 addition & 0 deletions docs/language/accounts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ to the `prepare` phase of the transaction.
fun borrow<T: &Any>(from: StoragePath): T?
fun link<T: &Any>(_ newCapabilityPath: CapabilityPath, target: Path): Capability<T>?
fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>?
fun getCapability<T>(_ path: CapabilityPath): Capability<T>
fun getLinkTarget(_ path: CapabilityPath): Path?
fun unlink(_ path: CapabilityPath)
Expand Down
105 changes: 0 additions & 105 deletions runtime/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2595,111 +2595,6 @@ func TestRuntimeAccountLink(t *testing.T) {
assert.ErrorContains(t, err, "value of type `AuthAccount` has no member `linkAccount`")
})

t.Run("enabled, pragma", func(t *testing.T) {

t.Parallel()

runtime := NewInterpreterRuntime(Config{
AtreeValidationEnabled: true,
AccountLinkingEnabled: true,
})

address1 := common.MustBytesToAddress([]byte{0x1})
address2 := common.MustBytesToAddress([]byte{0x2})

accountCodes := map[Location][]byte{}
var logs []string

signerAccount := address1

runtimeInterface := &testRuntimeInterface{
getCode: func(location Location) (bytes []byte, err error) {
return accountCodes[location], nil
},
storage: newTestLedger(nil, nil),
getSigningAccounts: func() ([]Address, error) {
return []Address{signerAccount}, nil
},
resolveLocation: singleIdentifierLocationResolver(t),
getAccountContractCode: func(address Address, name string) (code []byte, err error) {
location := common.AddressLocation{
Address: address,
Name: name,
}
return accountCodes[location], nil
},
updateAccountContractCode: func(address Address, name string, code []byte) (err error) {
location := common.AddressLocation{
Address: address,
Name: name,
}
accountCodes[location] = code
return nil
},
log: func(message string) {
logs = append(logs, message)
},
}

nextTransactionLocation := newTransactionLocationGenerator()

// Set up account

setupTransaction := []byte(`
#allowAccountLinking
transaction {
prepare(acct: AuthAccount) {
acct.linkAccount(/public/foo)
}
}
`)

signerAccount = address1

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

// Access

accessTransaction := []byte(`
transaction {
prepare(acct: AuthAccount) {
let ref = getAccount(0x1)
.getCapability<&AuthAccount>(/public/foo)
.borrow()!
log(ref.address)
}
}
`)

signerAccount = address2

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

require.Equal(t,
[]string{"0x0000000000000001"},
logs,
)
})

t.Run("publish and claim", func(t *testing.T) {

t.Parallel()
Expand Down
2 changes: 1 addition & 1 deletion runtime/sema/authaccount_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ var AuthAccountType = func() *CompositeType {
{
Label: ArgumentLabelNotRequired,
Identifier: "newCapabilityPath",
TypeAnnotation: NewTypeAnnotation(CapabilityPathType),
TypeAnnotation: NewTypeAnnotation(PrivatePathType),
},
},
ReturnTypeAnnotation: NewTypeAnnotation(
Expand Down
32 changes: 17 additions & 15 deletions runtime/tests/checker/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,27 +1163,29 @@ func TestCheckAccount_linkAccount(t *testing.T) {
},
)

if tc.enabled {
if tc.allowed {
switch tc.domain {
case common.PathDomainPrivate, common.PathDomainPublic:
require.NoError(t, err)

default:
errs := RequireCheckerErrors(t, err, 1)
if !tc.enabled {
errs := RequireCheckerErrors(t, err, 1)

require.IsType(t, &sema.TypeMismatchError{}, errs[0])
}
} else {
errs := RequireCheckerErrors(t, err, 1)
require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0])
return
}

require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0])
}
} else {
if !tc.allowed {
errs := RequireCheckerErrors(t, err, 1)

require.IsType(t, &sema.NotDeclaredMemberError{}, errs[0])

return
}

if tc.domain != common.PathDomainPrivate {
errs := RequireCheckerErrors(t, err, 1)

require.IsType(t, &sema.TypeMismatchError{}, errs[0])
return
}

require.NoError(t, err)
})
}

Expand Down
76 changes: 24 additions & 52 deletions runtime/tests/interpreter/capability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,30 +473,26 @@ func TestInterpretCapability_borrow(t *testing.T) {
`
#allowAccountLinking
fun link() {
account.linkAccount(/public/acct)
fun link(): Capability {
return account.linkAccount(/private/acct)!
}
fun address(_ path: CapabilityPath): Address {
return account.getCapability(path).borrow<&AuthAccount>()!.address
fun address(_ cap: Capability): Address {
return cap.borrow<&AuthAccount>()!.address
}
fun borrow(): Address {
return address(/public/acct)
fun borrow(_ cap: Capability): Address {
return address(cap)
}
fun borrowAuth(): auth &AuthAccount? {
return account.getCapability(/public/acct).borrow<auth &AuthAccount>()
fun borrowAuth(_ cap: Capability): auth &AuthAccount? {
return cap.borrow<auth &AuthAccount>()
}
fun nonExistent(): Address {
return address(/public/nonExistent)
}
fun unlinkAfterBorrow(): Address {
let ref = account.getCapability(/public/acct).borrow<&AuthAccount>()!
fun unlinkAfterBorrow(_ cap: Capability): Address {
let ref = cap.borrow<&AuthAccount>()!
account.unlink(/public/acct)
account.unlink(/private/acct)
return ref.address
}
Expand All @@ -508,12 +504,12 @@ func TestInterpretCapability_borrow(t *testing.T) {

// link

_, err := inter.Invoke("link")
capability, err := inter.Invoke("link")
require.NoError(t, err)

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

value, err := inter.Invoke("borrow")
value, err := inter.Invoke("borrow", capability)
require.NoError(t, err)

RequireValuesEqual(t,
Expand All @@ -525,23 +521,15 @@ func TestInterpretCapability_borrow(t *testing.T) {

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

value, err := inter.Invoke("borrowAuth")
value, err := inter.Invoke("borrowAuth", capability)
require.NoError(t, err)

require.Equal(t, interpreter.NilValue{}, value)
})

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

_, err := inter.Invoke("nonExistent")
RequireError(t, err)

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

t.Run("unlink after borrow", func(t *testing.T) {

_, err := inter.Invoke("unlinkAfterBorrow")
_, err := inter.Invoke("unlinkAfterBorrow", capability)
RequireError(t, err)

require.ErrorAs(t, err, &interpreter.DereferenceError{})
Expand Down Expand Up @@ -922,24 +910,16 @@ func TestInterpretCapability_check(t *testing.T) {
`
#allowAccountLinking
fun link() {
account.linkAccount(/public/acct)
}
fun checkPath(_ path: CapabilityPath): Bool {
return account.getCapability(path).check<&AuthAccount>()
fun link(): Capability {
return account.linkAccount(/private/acct)!
}
fun check(): Bool {
return checkPath(/public/acct)
fun check(_ cap: Capability): Bool {
return cap.check<&AuthAccount>()
}
fun checkAuth(): Bool {
return account.getCapability(/public/acct).check<auth &AuthAccount>()
}
fun nonExistent(): Bool {
return checkPath(/public/nonExistent)
fun checkAuth(_ cap: Capability): Bool {
return cap.check<auth &AuthAccount>()
}
`,
sema.Config{
Expand All @@ -949,28 +929,20 @@ func TestInterpretCapability_check(t *testing.T) {

// link

_, err := inter.Invoke("link")
capability, err := inter.Invoke("link")
require.NoError(t, err)

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

value, err := inter.Invoke("check")
value, err := inter.Invoke("check", capability)
require.NoError(t, err)

require.Equal(t, interpreter.TrueValue, value)
})

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

value, err := inter.Invoke("checkAuth")
require.NoError(t, err)

require.Equal(t, interpreter.FalseValue, value)
})

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

value, err := inter.Invoke("nonExistent")
value, err := inter.Invoke("checkAuth", capability)
require.NoError(t, err)

require.Equal(t, interpreter.FalseValue, value)
Expand Down
10 changes: 5 additions & 5 deletions runtime/tests/interpreter/memory_metering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6976,7 +6976,7 @@ func TestInterpretStorageCapabilityValueMetering(t *testing.T) {
pub fun main(account: AuthAccount) {
let r <- create R()
account.save(<-r, to: /storage/r)
let x = account.link<&R>(/public/capo, target: /storage/r)
let x = account.link<&R>(/public/cap, target: /storage/r)
}
`
meter := newTestMemoryGauge()
Expand All @@ -7000,7 +7000,7 @@ func TestInterpretStorageCapabilityValueMetering(t *testing.T) {
pub fun main(account: AuthAccount) {
let r <- create R()
account.save(<-r, to: /storage/r)
let x = account.link<&R>(/public/capo, target: /storage/r)
let x = account.link<&R>(/public/cap, target: /storage/r)
let y = [x]
}
Expand All @@ -7026,7 +7026,7 @@ func TestInterpretPathLinkValueMetering(t *testing.T) {
resource R {}
pub fun main(account: AuthAccount) {
account.link<&R>(/public/capo, target: /private/p)
account.link<&R>(/public/cap, target: /private/p)
}
`
meter := newTestMemoryGauge()
Expand All @@ -7052,7 +7052,7 @@ func TestInterpretAccountLinkValueMetering(t *testing.T) {
#allowAccountLinking
pub fun main(account: AuthAccount) {
account.linkAccount(/public/capo)
account.linkAccount(/private/cap)
}
`

Expand Down Expand Up @@ -8705,7 +8705,7 @@ func TestInterpretStorageMapMetering(t *testing.T) {
pub fun main(account: AuthAccount) {
let r <- create R()
account.save(<-r, to: /storage/r)
account.link<&R>(/public/capo, target: /storage/r)
account.link<&R>(/public/cap, target: /storage/r)
account.borrow<&R>(from: /storage/r)
}
`
Expand Down

0 comments on commit d48265a

Please sign in to comment.