-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: update internal Zoe documentation (#4081)
* chore: update internal Zoe and ERTP documentation * docs(zoe): misc editorial Co-authored-by: Chris Hibbert <Chris-Hibbert@users.noreply.github.com> * docs(zoe): prune obsolete reference to fees Co-authored-by: Chris Hibbert <Chris-Hibbert@users.noreply.github.com> Co-authored-by: Dan Connolly <connolly@agoric.com> Co-authored-by: Chris Hibbert <Chris-Hibbert@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
118694b
commit 9e437d5
Showing
14 changed files
with
790 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# ERTP User Safety | ||
|
||
## Both a protocol and an implementation | ||
|
||
ERTP can be thought of as a protocol and an implementation. There | ||
could be multiple different implementations of the protocol, but for | ||
now, we just have one (@agoric/ertp) whose types specify the protocol. | ||
The distinction between protocol and implementation becomes important | ||
because when Zoe escrows assets for use in contracts, it does not | ||
verify that the assets were made using the latest @agoric/ertp | ||
implementation. This means that the issuers and brands of assets that | ||
users escrow may misbehave. | ||
|
||
Zoe specifically uses the @agoric/ertp implementation for contract invitations. | ||
|
||
For every new brand of token, there is: | ||
* A mint - this is the only object with the ability to mint tokens. To mint, call `mint.mintPayment`. | ||
- A good example of an object capability - anyone who has access to the `mint` object can mint, and *only those* who have access to the `mint` object can mint. | ||
* A brand - this is the object that is the embodiment of the brand of token. When making offers, including the brand allows you to specify exactly what tokens you are giving and will accept. | ||
* An issuer - The trusted authority of which objects hold which tokens. | ||
|
||
The only objects that can “hold” assets: | ||
* Purses: An object that a user holds for the long-term, made directly using the issuer for the token | ||
* Payments: An short-lived object that is usually created by withdrawing from a purse (Payments can also be created by minting.) Payments are what are sent to other people and sent as an offer to Zoe | ||
|
||
More generally in ERTP: | ||
|
||
* In ERTP, users rely on issuers, and must be very careful which issuers they choose to rely on. | ||
* Users do not trust payments from other users. Users must use a trusted issuer to claim the payment or a purse (made from a trusted issuer) to deposit the payment. | ||
* Users do not accept purses from other users. | ||
* Users send amounts (amount = brand + value) to other users to reference the intended kind of an asset. | ||
|
||
|
||
| If someone sends you x, you should: | | | ||
|-------------------------------------|----| | ||
|An issuer | Ask yourself: Given what I know about the sender and what the sender says the issuer is an authority on, am I willing to rely on this issuer as the authority for this particular brand of token? (See below for more information on when to accept issuers.) | | ||
|A payment | Call `E(payment).getAllegedBrand()` to get the purported brand of the payment. Then, using the brand, look up the corresponding issuer or purse. Then, do either: `const newPaymentJustForMe = await E(issuer).claim(payment);` (This produces a new payment that only the user has access to. The old payment (which the sender might have access to) is used up and can be discarded.) Or: `await E(purse).deposit(payment);` This will also use up the old payment; it can now be discarded. The amount within the payment is transferred to the purse. | | ||
| A purse | DO NOT ACCEPT. Just drop it. | | ||
| A mint | Ask: why would someone do this? Probably, do not accept it. Mints are meant to stay in a contract and not be exposed, so someone sending a mint is highly unusual. | | ||
| An amount | This is a very normal part of negotiation about a potential trade or deal. If you want to check whether the amount is well-formed for a particular brand that you already know, you can create a safe copy with: `const myAmount = AmountMath.coerce(particularBrand, amount);` | ||
| A brand | This is a normal part of negotiation, if the value part of the amount isn’t yet known. This is how someone would tell you the kind of token that is wanted or given. | | ||
| A value | Normally this would be part of an amount. Just make sure you know which brand or potential brands you are talking about so there’s no confusion. | | ||
|
||
|
||
## Accepting Issuers | ||
|
||
The dangers of accepting an issuer: | ||
* The issuer is actually a different one than the one that you think | ||
it is. | ||
* The issuer wasn’t made using the ERTP implementation, and its actual | ||
code does things like revoke assets from purses and payments | ||
unexpectedly. | ||
|
||
Examples of receiving purported “issuers” that should not be trusted: | ||
* A stranger tells you about a new currency with issuer x. | ||
* A stranger sends you issuer x and says it’s the issuer for something valuable, like the Agoric staking token. | ||
|
||
Examples of receiving purported issuers that could be trusted: | ||
* Your best friend sends you the issuer for a new currency they're | ||
excited about | ||
* A currency’s public website sends your wallet the issuer for its own currency | ||
E.g. The well known AMM website tells your wallet that its liquidity | ||
token issuer for a certain pool is x | ||
|
||
If the issuer was made in a Zoe contract (currently, the only way for users to create issuers on-chain), then asserting that the issuer was produced in a contract by inspecting the contract code itself and the mechanisms by which the issuer is exposed, is the best way to be assured that an issuer is well-behaving. | ||
|
||
Note that Zoe assumes that issuers may misbehave. Users of Zoe are required to rely on issuers at their own risk. A misbehaving issuer will not hurt Zoe or other users, but it will potentially hurt a user who makes an offer that `wants` or `gives` assets of the same `brand` as the issuer. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# Handling of Authority in ERTP and Zoe | ||
|
||
Note: These are internal documentation notes. For how to use Zoe and | ||
how to develop smart contracts, please see | ||
https://agoric.com/documentation/ | ||
|
||
This document gives a quick overview of how Agoric handles authority | ||
in ERTP and Zoe. Two case studies will be used: first, a study of how | ||
ERTP purses interact with the ERTP paymentLedger, and second, a study | ||
of the escrow code in Zoe. | ||
|
||
## Use of Closures | ||
Agoric has deliberately chosen to use closures to create objects and | ||
store state. Agoric does not use classes, and many functions in Agoric | ||
code are not purely functional. Rather, the functions and objects are | ||
the means of conveying authority. | ||
|
||
## Division of Files | ||
Files in ERTP and Zoe have been structured to isolate and attenuate | ||
authority so that POLA (Principle of Least Authority) is followed. For | ||
example, in ERTP, the core code that controls the movement of assets | ||
is isolated to paymentLedger.js. Because of this organization choice, | ||
we can confine the `paymentLedger` WeakStore to this file and only | ||
export attenuated access, in the form of an `issuer` and `mint` | ||
object. | ||
|
||
## Why not imports? Why pass functions and objects around? | ||
|
||
In the object capabilities paradigm, access control is achieved by | ||
selectively passing objects (or functions). This is a fundamental | ||
aspect of object capabilities. These objects have methods which allow | ||
the holder to take certain actions. Importantly, only the holder can | ||
take these actions. | ||
|
||
We use this pattern both in our external APIs and also within services | ||
and packages. We do this in order to achieve defense-in-depth and | ||
least authority. For example, we want the bare minimum of code to be | ||
able to withdraw funds from Zoe’s escrow purses, and we want the code | ||
to be easy to audit. | ||
|
||
Imports do not serve the same purposes. If powerful capabilities can | ||
be imported, then we have to restrict which files can import what, | ||
thus relying on a lower level to give us guarantees. By passing | ||
powerful capabilities instead, we can merely inspect the code and how | ||
it is being used to ensure that the powerful capability does not | ||
wrongfully escape. | ||
|
||
## Case study: ERTP purses and the ERTP paymentLedger | ||
|
||
First, some quick context on the file structure in ERTP. There are two | ||
main entrypoints: amountMath.js and issuerKit.js. It is common for | ||
users of ERTP to import `AmountMath` as a stateless library and use it | ||
with already existing ERTP assets. issuerKit.js is how users create | ||
new assets. | ||
|
||
There is specific malicious behavior that ERTP must not allow: | ||
* Users stealing funds from other users | ||
* Users minting who do not have access to the mint | ||
* Holders of a mint revoking assets. | ||
|
||
In ERTP, the most powerful object is `paymentLedger`, which is a | ||
mapping from payment objects to the amounts which they are said to | ||
hold. Any movement of assets (payment to payment, or purse to payment, | ||
or payment to purse) makes a change to `paymentLedger`. The malicious | ||
behavior listed above would have to engage with the `paymentLedger` in | ||
some regard to be a successful attack. Thus, we want to try to isolate | ||
this powerful authority as much as possible, and be very clear about | ||
when it can be accessed. To achieve this, the `paymentLedger` has its | ||
own file, and never escapes the file. | ||
|
||
Other parts of ERTP are also split into their own files. For example, | ||
payments themselves have no internal state, so the code for making a | ||
payment can be in an external file. The same with brands. Purses are | ||
slightly different. We’ve chosen to divide up the code for making a | ||
purse. Rather than add all of the purse-making code to | ||
`paymentLedger`, we chose to only add the `deposit` and `withdraw` | ||
logic. This allowed us to group together the code that accesses the | ||
`paymentLedger`. The alternatives, such as passing the paymentLedger | ||
itself, or moving all of the purse code into the paymentLedger file | ||
would not be as easy to audit. | ||
|
||
## Case study: EscrowStorage in Zoe | ||
As one of its features, Zoe provides escrow services for users. This | ||
means that within Zoe, there are ERTP purses containing all of the | ||
funds for all of the users of contracts. This is obviously an area of | ||
high authority that needs as much protection as possible. | ||
|
||
To achieve this goal, the code for escrowing is in a separate file: | ||
https://github.com/Agoric/agoric-sdk/blob/198e5c37b4ce3738ed5776c36c949847a226265c/packages/zoe/src/zoeService/escrowStorage.js | ||
|
||
Isolating the purses in this way means that there is no way to | ||
directly access the purse objects, outside the file. And more, the | ||
only way that assets can be withdrawn from the purses is through the | ||
function `withdrawPayments`. | ||
|
||
Thus, once the file itself has been audited to ensure these | ||
assumptions are correct, then the reviewer only has to search for uses | ||
of `withdrawPayments`. There is only one usage, which is to make a | ||
payout: | ||
https://github.com/Agoric/agoric-sdk/blob/198e5c37b4ce3738ed5776c36c949847a226265c/packages/zoe/src/zoeService/zoeSeat.js#L49 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Platform Specification | ||
|
||
Note: These are internal documentation notes. For how to use Zoe and | ||
how to develop smart contracts, please see | ||
https://agoric.com/documentation/ | ||
|
||
Zoe and ERTP depend on layers of Agoric-specific infrastructure. | ||
|
||
Dean Tribble (@dtribble) gave a presentation that specifies and | ||
explains the infrastructure that Zoe and ERTP depend on. | ||
|
||
[Link to | ||
Recording](https://drive.google.com/file/d/1dhPD2PWxIpiepvIx3_bek0tDBTI_DQB6/view) | ||
(ask for permission to view) | ||
|
||
[Link to | ||
Slides](https://docs.google.com/presentation/d/1Ejrs4PH3ZfQ9uKFO47wQvOYCHSMbcJxgjCyfO-Rg3VM/edit?usp=sharing) | ||
(should be accessible to all) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# Seats in the Zoe Service and Zoe Contract Facet | ||
|
||
Note: These are internal documentation notes. For how to use Zoe and | ||
how to develop smart contracts, please see | ||
https://agoric.com/documentation/ | ||
|
||
|
||
__UserSeat.tryExit() Flow:__ | ||
|
||
![UserSeat Exit Flow](./user-seat-exit-flow.png) | ||
|
||
__ZCFSeat.exit() Flow:__ | ||
|
||
![ZCFSeat Exit Flow](./zcf-seat-exit-flow.png) | ||
|
||
__ZCF.reallocate() Flow:__ | ||
|
||
![ZCF Reallocate Flow](./zcf-reallocate-flow.png) | ||
|
||
|
||
## UserSeat | ||
|
||
The `UserSeat` is what is returned when a user calls | ||
`E(zoe).offer(invitation, proposal, payments, offerArgs, feePurse)`. It has the following | ||
type: | ||
|
||
```js | ||
/** | ||
* @typedef {Object} UserSeat | ||
* @property {() => Promise<Allocation>} getCurrentAllocation | ||
* @property {() => Promise<ProposalRecord>} getProposal | ||
* @property {() => Promise<PaymentPKeywordRecord>} getPayouts | ||
* @property {(keyword: Keyword) => Promise<Payment>} getPayout | ||
* @property {() => Promise<OfferResult>} getOfferResult | ||
* @property {() => void=} tryExit | ||
* @property {() => Promise<boolean>} hasExited | ||
* @property {() => Promise<Notifier<Allocation>>} getNotifier | ||
*/ | ||
``` | ||
|
||
Note that `tryExit` is only present if the exit rule is `onDemand`. The | ||
user can use the seat to get their payout, get the result of their | ||
offer (whatever the contract chooses to return. This varies, but | ||
examples are a string and an invitation for another user.) | ||
|
||
## ZCFSeat | ||
|
||
The `ZCFSeat` is a facet of the same seat, specifically for the | ||
contract to manipulate. It is the `ZCFSeat` that is passed as the first | ||
parameter to `offerHandlers`: | ||
|
||
```js | ||
const buyItems = buyerSeat => { | ||
const proposal = buyerSeat.getProposal(); | ||
const moneyGiven = buyerSeat.getAmountAllocated('Money', moneyBrand); | ||
... | ||
``` | ||
The type of the ZCFSeat is: | ||
```js | ||
/** | ||
* @typedef {Object} ZCFSeat | ||
* @property {() => void} exit | ||
* @property {ZCFSeatFail} fail | ||
* @property {() => Notifier<Allocation>} getNotifier | ||
* @property {() => boolean} hasExited | ||
* @property {() => ProposalRecord} getProposal | ||
* @property {ZCFGetAmountAllocated} getAmountAllocated | ||
* @property {() => Allocation} getCurrentAllocation | ||
* @property {() => Allocation} getStagedAllocation | ||
* @property {() => boolean} hasStagedAllocation | ||
* @property {(newAllocation: Allocation) => boolean} isOfferSafe | ||
* @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} incrementBy | ||
* @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} decrementBy | ||
* @property {() => void} clear | ||
*/ | ||
``` | ||
## ZoeSeatAdmin | ||
Internal to Zoe Service code and passed to ZCF. Never external. | ||
The `ZoeSeatAdmin` is the administrative facet of a seat within Zoe. | ||
When `exit()` is called on this object, the payouts accessible through | ||
the `UserSeat` are resolved. `replaceAllocation` changes the Zoe | ||
allocation to the `replacementAllocation`. | ||
The type of the `ZoeSeatAdmin` is: | ||
```js | ||
/** | ||
* @typedef {Object} ZoeSeatAdmin | ||
* @property {(allocation: Allocation) => void} replaceAllocation | ||
* @property {ZoeSeatAdminExit} exit | ||
* @property {ShutdownWithFailure} fail called with the reason | ||
* for calling fail on this seat, where reason is normally an instanceof Error. | ||
*/ | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
@startuml UserSeat.tryExit() flow | ||
|
||
package ZoeService <<Rectangle>> { | ||
object UserSeat | ||
UserSeat : tryExit() | ||
UserSeat : ... | ||
|
||
object ZoeSeatAdmin | ||
ZoeSeatAdmin : exit() | ||
ZoeSeatAdmin : ... | ||
} | ||
|
||
package ZCF <<Rectangle>> { | ||
object ZCFSeat | ||
ZCFSeat : exit() | ||
ZCFSeat : ... | ||
|
||
object ExitObj | ||
ExitObj : exit() | ||
ExitObj : ... | ||
} | ||
|
||
ZoeService -[hidden]> ZCF | ||
UserSeat -[hidden]> ZoeSeatAdmin | ||
UserSeat --|> ExitObj : (1) exit | ||
ExitObj --|> ZCFSeat: (2) exit | ||
ZCFSeat --|> ZoeSeatAdmin: (3) exit | ||
@enduml |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
@startuml ZCF.reallocate() flow | ||
|
||
package ZoeService <<Rectangle>> { | ||
object UserSeat | ||
UserSeat : tryExit() | ||
UserSeat : ... | ||
|
||
object ZoeSeatAdmin | ||
ZoeSeatAdmin : exit() | ||
ZoeSeatAdmin : replaceAllocation() | ||
} | ||
|
||
package ZCF <<Rectangle>> { | ||
object ZCFSeat | ||
ZCFSeat : exit() | ||
ZCFSeat : ... | ||
|
||
object ZCFSeatAdmin | ||
ZCFSeatAdmin : commit() | ||
|
||
} | ||
|
||
ZCFSeat --|> ZCFSeatAdmin : 2) looked up in internal map | ||
ZCFSeatAdmin --|> ZoeSeatAdmin : 3) commit() | ||
ZoeSeatAdmin --|> ZoeSeatAdmin : 4) replaceAllocation() | ||
@enduml |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
@startuml ZCFSeat.exit() flow | ||
|
||
package ZoeService <<Rectangle>> { | ||
object UserSeat | ||
UserSeat : tryExit() | ||
UserSeat : ... | ||
|
||
object ZoeSeatAdmin | ||
ZoeSeatAdmin : exit() | ||
ZoeSeatAdmin : ... | ||
} | ||
|
||
package ZCF <<Rectangle>> { | ||
object ZCFSeat | ||
ZCFSeat : exit() | ||
ZCFSeat : ... | ||
|
||
object ExitObj | ||
ExitObj : exit() | ||
ExitObj : ... | ||
} | ||
|
||
ZCFSeat --|> ZoeSeatAdmin: (1) exit | ||
@enduml |
Oops, something went wrong.