Skip to the content.

Partition authority using facets

Intent

Separate what participants are allowed to perform what actions.

Consequences

Context

An Agoric smart contracts returns a record which consists of (a subset of) the following 3 objects: a creatorInvitation, a creatorFacet and a publicFacet [1]. The creatorInvitation is an invitation that is given to the entity that started an instance of the smart contract [2]. Agoric describes a facet as an object that exposes an API or particular view of some larger entity, which may be an object itself [2]. The creatorFacet is a facet that is given to the entity that starts an instance of the smart contract, while the publicFacet is available to any entity that has a reference to the instance of the smart contract [1]. Thus: the creatorFacet should host methods that should only be available to the entity that started the instance of the smart contract, while the publicFacet should host methods that are available to potentially all entities.

Example

const start = async zcf => {
  const getConfidentialInformation = () => {
    return 'confidential information';
  } 
  const getPublicInformation = () => {
    return 'public information';
  }
  const publicFacet = Far('publicFacet', {
    getConfidentialInformation,
    getPublicInformation
  });
  return harden({ publicFacet });
};
harden(start);
export { start };

The code above shows a smart contract with 2 methods. The getConfidentialInformation method returns information that should only be known by the entity that started an instance of the smart contract. The getPublicInformation method returns information which can be known by all users. Both methods are made available via the publicFacet.

//Alice starts an instance of the installation
const { publicFacet } = await zoe.startInstance(installation);

//Alice made the instance of the smart contract available via the board
 
//Bob uses the instance to get the publicFacet
  
//Bob uses the publicFacet to call the confidential method
t.deepEqual(
await E(publicFacet).getConfidentialInformation(),
'confidential information'

In the test code above, Alice starts an instance of the initial smart contract. Alice posts the instance of the smart contract to the board, which Agoric describes as a shared, on-chain location where users can post a value and make it accessible to others [2] [3]. Bob retrieves the instance of the smart contract from the board and uses it to retrieve the publicFacet. Then, Bob can use the publicFacet to access the confidential information via the getConfidentialInformation method. This is a problem, since only Alice should be able to access the getConfidentialInformation method.

const start = async zcf => {
  const getConfidentialInformation = () => {
    return 'confidential information';
  }
  const getPublicInformation = () => {
    return 'public information';
  }
  const creatorFacet = Far('creatorFacet', {
    getConfidentialInformation
  });
  const publicFacet = Far('publicFacet', {
    getPublicInformation
  });
  return harden({ creatorFacet, publicFacet });
};
harden(start);
export { start };

The above code shows an improved version of the initial smart contract. In listing this smart contract, the getConfidentialInformation method is returned in the creatorFacet instead of in the publicFacet.

//Alice starts an instance of the installation
const { creatorFacet, publicFacet } = await zoe.startInstance(installation);
  
//Alice made the instance of the smart contract available via the board
  
//Bob uses the instance to get the publicFacet
 
//Bob can no longer access the getConfidentialInformation method  
await t.throwsAsync(async () =>
  {
      await E(publicFacet).getConfidentialInformation()
  },
  {
    instanceOf: TypeError,
    message: 'target has no method "getConfidentialInformation", has ["getPublicInformation"]'
  }
);

As shown in the tests above, the change in the smart contract ensures that Bob can no longer access the getConfidentialInformation method, since it is no longer available to him.

General rule

All methods that should be made publicly available should be hosted in the publicFacet, and all methods that should only be used by the entity that started an instance of the smart contract should be hosted in the creatorFacet. In short, use the facets as described by Agoric [1].

Known uses

References

[1] Agoric, Contract requirements

[2] Agoric, Glossary

[3] Agoric, Deploying smart contracts