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

[DO NOT MERGE] Capability Controllers #134

Closed
wants to merge 6 commits into from
Closed
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 contracts/CapabilityFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub contract CapabilityFactory {
///
pub struct interface Factory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability
pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability
}

/// Getter defines an interface for retrieval of a Factory if contained within the implementing resource
Expand Down
105 changes: 69 additions & 36 deletions contracts/HybridCustody.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,14 @@ pub contract HybridCustody {
"Capability is not allowed by this account's Parent"
}
}

pub fun issueCapability(from: StoragePath, type: Type): Capability? {
post {
result == nil || [true, nil].contains(self.getManagerCapabilityFilter()?.allowed(cap: result!)):
"Capability is not allowed by this account's Parent"
}
}

pub fun getPublicCapability(path: PublicPath, type: Type): Capability?
pub fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}?
pub fun getPublicCapFromDelegator(type: Type): Capability?
Expand Down Expand Up @@ -584,6 +592,19 @@ pub contract HybridCustody {
return cap
}

pub fun issueCapability(from: StoragePath, type: Type): Capability? {
let child = self.childCap.borrow() ?? panic("failed to borrow child account")
let f = self.factory.borrow()!.getFactory(type)
if f == nil {
return nil
}

let acct = child.borrowAccount()
let cap = f!.issueCapability(acct: acct, from: from)
assert(self.filter.borrow()!.allowed(cap: cap), message: "requested capability is not allowed")
return cap
}

/// Retrieves a private Capability from the Delegator or nil none is found of the given type
///
pub fun getPrivateCapFromDelegator(type: Type): Capability? {
Expand Down Expand Up @@ -647,9 +668,9 @@ pub contract HybridCustody {
switch view {
case Type<MetadataViews.Display>():
let childAddress = self.getAddress()
let manager = getAccount(self.parent).getCapability<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath)
let manager = getAccount(self.parent).capabilities.get<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath)

if !manager.check() {
if manager == nil || !manager!.check() {
return nil
}

Expand Down Expand Up @@ -801,35 +822,22 @@ pub contract HybridCustody {
}

let capDelegatorPublic = PublicPath(identifier: capDelegatorIdentifier)!
let capDelegatorPrivate = PrivatePath(identifier: capDelegatorIdentifier)!

acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(
capDelegatorPublic,
target: capDelegatorStorage
)
acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(
capDelegatorPrivate,
target: capDelegatorStorage
)
let delegator = acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(
capDelegatorPrivate
)
let delegatorCap = acct.capabilities.storage.issue<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(capDelegatorStorage)
acct.capabilities.publish(delegatorCap, at: capDelegatorPublic)
assert(delegatorCap.check(), message: "Delegator capability check failed")

let delegator = acct.capabilities.storage.issue<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(capDelegatorStorage)
assert(delegator.check(), message: "failed to setup capability delegator for parent address")

let borrowableCap = self.borrowAccount().getCapability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>(
HybridCustody.OwnedAccountPrivatePath
)
let borrowableCap = self.borrowAccount().capabilities.storage.issue<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountStoragePath)
let childAcct <- create ChildAccount(borrowableCap, factory, filter, delegator, parentAddress)

let childAccountPrivatePath = PrivatePath(identifier: identifier)!

acct.save(<-childAcct, to: childAccountStorage)
acct.link<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath, target: childAccountStorage)

let delegatorCap = acct.getCapability<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath)
acct.save(<-childAcct, to: childAccountStorage)
let childCap = acct.capabilities.storage.issue<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountStorage)
assert(delegatorCap.check(), message: "Delegator capability check failed")

acct.inbox.publish(delegatorCap, name: identifier, recipient: parentAddress)
acct.inbox.publish(childCap, name: identifier, recipient: parentAddress)
self.parents[parentAddress] = false

emit ChildAccountPublished(
Expand Down Expand Up @@ -893,21 +901,33 @@ pub contract HybridCustody {
let capDelegatorIdentifier = HybridCustody.getCapabilityDelegatorIdentifier(parent)

let acct = self.borrowAccount()
acct.unlink(PrivatePath(identifier: identifier)!)
acct.unlink(PublicPath(identifier: identifier)!)

acct.unlink(PrivatePath(identifier: capDelegatorIdentifier)!)
acct.unlink(PublicPath(identifier: capDelegatorIdentifier)!)
// destroy delegator capabilities
let childStoragePath = StoragePath(identifier: capDelegatorIdentifier)!
let childControllers = acct.capabilities.storage.getControllers(forPath: childStoragePath)
for c in childControllers {
c.delete()
}
acct.capabilities.unpublish(PublicPath(identifier: identifier)!)


// destroy delegator capabilities
let delegatorStoragePath = StoragePath(identifier: capDelegatorIdentifier)!
acct.capabilities.storage.forEachController(forPath: delegatorStoragePath, fun (c: &StorageCapabilityController): Bool {
c.delete()
return true
})
acct.capabilities.unpublish(PublicPath(identifier: capDelegatorIdentifier)!)

destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: identifier)!)
destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: capDelegatorIdentifier)!)

self.parents.remove(key: parent)
emit AccountUpdated(id: self.uuid, child: self.acct.address, parent: parent, active: false)

let parentManager = getAccount(parent).getCapability<&Manager{ManagerPublic}>(HybridCustody.ManagerPublicPath)
if parentManager.check() {
parentManager.borrow()?.removeParentCallback(child: self.owner!.address)
let parentManager = getAccount(parent).capabilities.get<&Manager{ManagerPublic}>(HybridCustody.ManagerPublicPath)
if parentManager != nil && parentManager!.check() {
parentManager!.borrow()!.removeParentCallback(child: self.owner!.address)
}

return true
Expand Down Expand Up @@ -950,18 +970,22 @@ pub contract HybridCustody {

let acct = self.borrowAccount()
// Unlink existing owner's Capability if owner exists
// TODO: remove this once linking is removed from cadence
if self.acctOwner != nil {
acct.unlink(
PrivatePath(identifier: HybridCustody.getOwnerIdentifier(self.acctOwner!))!
)
}

let controllers = acct.capabilities.storage.getControllers(forPath: HybridCustody.OwnedAccountStoragePath)
for c in controllers {
c.delete()
}

// Link a Capability for the new owner, retrieve & publish
let identifier = HybridCustody.getOwnerIdentifier(to)
let cap = acct.link<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>(
PrivatePath(identifier: identifier)!,
target: HybridCustody.OwnedAccountStoragePath
) ?? panic("failed to link child account capability")

let cap = acct.capabilities.storage.issue<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountStoragePath)
acct.inbox.publish(cap, name: identifier, recipient: to)

self.pendingOwner = to
Expand Down Expand Up @@ -1001,21 +1025,30 @@ pub contract HybridCustody {
return true
})

let controllers = acct.capabilities.account.getControllers()

// Link a new AuthAccount Capability
// NOTE: This path cannot be sufficiently randomly generated, an app calling this function could build a
// capability to this path before it is made, thus maintaining ownership despite making it look like they
// gave it away. Until capability controllers, this method should not be fully trusted.
let authAcctPath = "HybridCustodyRelinquished".concat(HybridCustody.account.address.toString()).concat(getCurrentBlock().height.toString())
let acctCap = acct.linkAccount(PrivatePath(identifier: authAcctPath)!)!
let acctCap = acct.capabilities.account.issue<&AuthAccount>()


self.acct = acctCap
let newAcct = self.acct.borrow()!

// cleanup, remove all previously found paths. We had to do it in this order because we will be unlinking
// the existing path which will cause a deference issue with the originally borrowed auth account
// TODO: remove once capability controllers are the only way to access Caps on an account
for p in pathsToUnlink {
newAcct.unlink(p)
}

// also revoke all keys via capcons
for c in controllers {
c.delete()
}
}

/// Revokes all keys on an account, unlinks all currently active AuthAccount capabilities, then makes a new one
Expand Down
4 changes: 4 additions & 0 deletions contracts/factories/FTAllFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ pub contract FTAllFactory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability {
return acct.getCapability<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(path)
}

pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability {
return acct.capabilities.storage.issue<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(from)
}
}
}
4 changes: 4 additions & 0 deletions contracts/factories/FTProviderFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ pub contract FTProviderFactory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability {
return acct.getCapability<&{FungibleToken.Provider}>(path)
}

pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability {
return acct.capabilities.storage.issue<&{FungibleToken.Provider}>(from)
}
}
}
4 changes: 4 additions & 0 deletions contracts/factories/NFTCollectionPublicFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ pub contract NFTCollectionPublicFactory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability {
return acct.getCapability<&{NonFungibleToken.CollectionPublic}>(path)
}

pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability {
return acct.capabilities.storage.issue<&{NonFungibleToken.CollectionPublic}>(from)
}
}
}
4 changes: 4 additions & 0 deletions contracts/factories/NFTProviderAndCollectionFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ pub contract NFTProviderAndCollectionFactory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability {
return acct.getCapability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(path)
}

pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability {
return acct.capabilities.storage.issue<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(from)
}
}
}
4 changes: 4 additions & 0 deletions contracts/factories/NFTProviderFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ pub contract NFTProviderFactory {
pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability {
return acct.getCapability<&{NonFungibleToken.Provider}>(path)
}

pub fun issueCapability(acct: &AuthAccount, from: StoragePath): Capability {
return acct.capabilities.storage.issue<&{NonFungibleToken.Provider}>(from)
}
}
}
2 changes: 0 additions & 2 deletions test/HybridCustody_tests.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ pub fun testRemoveChildAccount() {
setupChildAndParent_FilterKindAll(child: child, parent: parent)
checkForAddresses(child: child, parent: parent)


assert(isParent(child: child, parent: parent) == true, message: "is not parent of child account")
txExecutor("hybrid-custody/remove_child_account.cdc", [parent], [child.address], nil, nil)
assert(isParent(child: child, parent: parent) == false, message: "child account was not removed from parent")
Expand Down Expand Up @@ -710,7 +709,6 @@ pub fun testGetChildAccountFTCapabilities(){

let ftTypeIds = scriptExecutor("hybrid-custody/get_child_account_ft_capabilities.cdc", [parent.address])! as! {Address: [String]}
assert(ftTypeIds[child.address]![0] == nftIdentifier, message: "typeId should be: ".concat(nftIdentifier))

}

pub fun testBlockchainNativeOnboarding() {
Expand Down
2 changes: 1 addition & 1 deletion transactions/hybrid-custody/accept_ownership.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ transaction(childAddress: Address, filterAddress: Address?, filterPath: PublicPa
let m <- HybridCustody.createManager(filter: filter)
acct.save(<- m, to: HybridCustody.ManagerStoragePath)

acct.unlink(HybridCustody.ManagerPublicPath)
acct.capabilities.unpublish(HybridCustody.ManagerPublicPath)
acct.unlink(HybridCustody.ManagerPrivatePath)

acct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerPrivatePath, target: HybridCustody.ManagerStoragePath)
Expand Down
9 changes: 7 additions & 2 deletions transactions/hybrid-custody/set_manager_filter_cap.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ transaction(filterAddress: Address, childAddress: Address) {
let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
?? panic("manager not found")

let cap = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)
assert(cap.check(), message: "capability filter is not valid")
var cap = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)

if cap == nil {
cap = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)
}
assert(cap != nil, message: "manager capability not found")
assert(cap!.check(), message: "capability filter is not valid")

m.setManagerCapabilityFilter(cap: cap, childAddress: childAddress)
}
Expand Down
9 changes: 3 additions & 6 deletions transactions/hybrid-custody/setup_manager.cdc
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to check my understanding with folks on this, but it sounds like the linking of the private manager path is no longer possible/needed, so I have removed it

Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ transaction(filterAddress: Address?, filterPath: PublicPath?) {
acct.save(<- m, to: HybridCustody.ManagerStoragePath)
}

acct.unlink(HybridCustody.ManagerPublicPath)
acct.unlink(HybridCustody.ManagerPrivatePath)

acct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerPrivatePath, target: HybridCustody.ManagerStoragePath)
acct.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath, target: HybridCustody.ManagerStoragePath)
acct.capabilities.unpublish(HybridCustody.ManagerPublicPath)
let publicCap = acct.capabilities.storage.issue<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath)
acct.capabilities.publish(publicCap, at: HybridCustody.ManagerPublicPath)
}
}

Loading