Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce account links to be private #2366

Merged
merged 1 commit into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
SupunS marked this conversation as resolved.
Show resolved Hide resolved

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