-
Notifications
You must be signed in to change notification settings - Fork 14
/
RevIssuer.rho
262 lines (250 loc) · 11.5 KB
/
RevIssuer.rho
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/**
* RevIssuerKit lets us make [ERTP][]-style purses containing REV.
*
* Each REV vault contains an ERTP-style purse, but there is
* no way to access it from outside the current REVVault contract.
* We use REVAddress!("fromUnforgeable") to wrap a REVVault in
* an ERTP-style purse for use in higher-order contracts and such.
*
* Since this is possible, the REVVault contract should just have
* a method to access to the purse behind a REVVault made with
* "fromUnforgeable". But the current version does not, so we
* must waste some execution cost on this wrapper.
*
* [ERTP]: https://agoric.com/documentation/ertp/guide/
*/
new log(`rho:rchain:deployId`),
RevIssuerKit,
insertArbitrary(`rho:registry:insertArbitrary`),
lookup(`rho:registry:lookup`), vCh,
REVAddress(`rho:rev:address`)
in {
lookup!(`rho:rchain:revVault`, *vCh) | for (@(_, *RevVault) <- vCh) {
// Test the contract before inserting into the registry.
new kitCh, pCh,
fromCh, toCh, pubKeyCh,
vaultCh, targetVaultCh, revVaultkeyCh,
DeployerIdOps(`rho:rchain:deployerId:ops`),
deployerId(`rho:rchain:deployerId`)
in {
RevIssuerKit!(*kitCh) | for (@{"issuer": *issuer, ..._} <- kitCh) {
log!(["got issuer"]) |
issuer!("makeEmptyPurse", *pCh) | for (purse <- pCh) {
log!(["got purse"]) |
DeployerIdOps!("pubKeyBytes", *deployerId, *pubKeyCh) |
for (@deployerPubKey <- pubKeyCh) {
log!(["got deployer public key", deployerPubKey]) |
purse!("getAddress", *toCh) |
REVAddress!("fromPublicKey", deployerPubKey, *fromCh) |
for (@from <- fromCh; @to <- toCh) {
log!(["got from", from, "to", to]) |
RevVault!("findOrCreate", from, *vaultCh) |
// make sure the target vault it created and the transfer would be done
RevVault!("findOrCreate", to, *targetVaultCh) |
RevVault!("deployerAuthKey", *deployerId, *revVaultkeyCh) |
for (@(true, vault) <- vaultCh ; key <- revVaultkeyCh ; @(true, _) <- targetVaultCh) {
log!(["Beginning transfer of ", 100, "REVlette from", from, "to", to]) |
new resultCh in {
@vault!("transfer", to, 100 /*amount*/, *key, *resultCh) |
for (@result <- resultCh) {
log!(("Finished transfer of ", 100, "REV to", to, "result was:", result)) |
purse!("withdraw", 50, *resultCh) | for (@(ok, *p2) <- resultCh) {
log!(["withdraw ok?", ok]) |
purse!("deposit", *p2, *resultCh) | for (@(ok, x) <- resultCh) {
log!(["deposit ok?", (ok, x)]) |
new kCh, iCh in {
insertArbitrary!(bundle+{*RevIssuerKit}, *kCh) |
insertArbitrary!(bundle+{*issuer}, *iCh) |
for (@kUri <- kCh; @iUri <- iCh) {
log!(["tests passed; inserted RevIssuerKit at", kUri, "REV issuer at", iUri])
}
}
}
}
}
}
}
}
}
}
}
} |
/**
* Make an ERTP-style Issuer Kit for REV
*
* TODO: amountMath, brand?
*/
contract RevIssuerKit(ret) = {
new issuer, makePurseKit in {
// The corresponding mint is the REV mint; clearly we don't have that to give out.
ret!({"mint": Nil, "issuer": bundle+{*issuer}}) |
/**
* The issue has only the ability to make an empty purse.
* Feel free to share it widely. In fact, unless others have
* and the issuer, they have no way to create purses of their
* own and hence no reason to believe purses you send are
* of any value.
*
* TODO: review issuer API; add claim, ...?
*/
contract issuer(@"makeEmptyPurse", ret) = {
new kitCh in {
makePurseKit!(*kitCh) | for (@{"purse": *purse, ..._} <- kitCh) {
ret!(bundle+{*purse}) // already bundled, but... it's a good habit.
}
}
} |
/**
* makePurseKit is closely held. Purses from this issuer trust each other
* based on their ability to demonstrate they have it in scope using a
* form of "sealed dispatch".
*/
contract makePurseKit(kitRet) = {
new un, // un is closely held! It enables withdrawal from this purse's vault.
purse, addrCh, balCh, vaultCh in {
balCh!(0) |
REVAddress!("fromUnforgeable", *un, *addrCh) | for (@purseAddr <- addrCh) {
RevVault!("findOrCreate", purseAddr, *vaultCh) | for (@(true, *vault) <- vaultCh) {
// log!(["new purse addr", purseAddr]) |
// Return purse and address in one round trip.
kitRet!({"purse": bundle+{*purse}, "address": purseAddr}) |
/**
* getAddress facilitates normal REV transfers into this purse.
*
* WARNING: only trust getAddress on purses you trust (for example,
* a purse you created using a trusted issuer), not
* from alleged payments!
*/
contract purse(@"getAddress", ret) = { ret!(purseAddr) } |
// TODO: upgrade to getCurrentAmount
contract purse(@"getBalance", ret) = { for (@balance <<- balCh) { ret!(balance) } } |
/**
* Get a key etc. to withdraw from this purse.
*
* @param sealedDispatch - provides evidence that the
* caller has makePurseKit but protects the caller from
* disclosing it to potential attackers by bundling it
* with a fresh return channel.
* @returns an unforgeableAuthKey along with a corresponding seal
* as well as this purse's vault, address, and balance.
*/
contract purse(@"getKey", @sealedDispatch, ret) = {
// log!(["getKey seal", sealedDispatch.toByteArray(), {bundle0{(*makePurseKit, *ret)}}.toByteArray()]) |
if (sealedDispatch == {bundle0{(*makePurseKit, *ret)}}) {
new keyCh in {
RevVault!("unforgeableAuthKey", *un, *keyCh) |
for (@balance <<- balCh) {
for (@key <- keyCh) {
// log!(["unforgeableAuthKey", key, "balance", balance]) |
ret!((true, {"key": key, "seal": bundle0{(*makePurseKit, key)},
"vault": *vault,
"address": purseAddr, "balance": balance }))
}
}
}
} else {
ret!((false, "bad sealedDispatch"))
}
} |
// TODO: optAmount
contract purse(@"deposit", allegedPayment, ret) = {
// log!("deposit...") |
new keyCh, amtCh, pAddrCh, pvCh, txCh in {
// Ask alleged payment for a key using sealed dispatch (see above).
allegedPayment!("getKey", bundle0{(*makePurseKit, *keyCh)}, *keyCh) |
for (@(true, {"key": pmtAuthKey, "seal": seal,
"vault": *pmtVault,
"address": pmtAddr, "balance": amount }) <- keyCh) {
// log!(["got key for add", pmtAddr,
// "seal", seal.toByteArray(), {bundle0{(*makePurseKit, pmtAuthKey)}}.toByteArray()]) |
// Check the seal before trusting the alleged payment vault's transfer method.
if (seal == {bundle0{(*makePurseKit, pmtAuthKey)}}) {
pmtVault!("transfer", purseAddr, amount, pmtAuthKey, *txCh) |
for (@(ok, x) <- txCh; @balance <- balCh) {
// log!(["deposit transfer result", (ok, x)]) |
ret!((ok, x)) |
match ok {
// RevVault transfer already checked for overflow
true => balCh!(balance + amount)
false => balCh!(balance)
}
}
} else {
ret!((false, "bad seal"))
}
}
}
} |
/**
* Get deposit-only facet
*/
contract purse(@"getDepositFacet", ret) = {
new depositFacet in {
ret!(*depositFacet) |
contract depositFacet(@"receive", allegedPayment, ret) = {
purse!("deposit", *allegedPayment, *ret)
}
}
} |
/**
* Withdraw purse
*
* TODO: distinguish purses from payments to avoid hazzards.
*/
contract purse(@"withdraw", @amount, ret) = {
// log!(["withdraw", amount, "from", purseAddr]) |
new pCh, keyCh, txCh in {
makePurseKit!(*pCh) |
for (@{"purse": *withdrawal, "address": wAddr, ..._} <- pCh) {
// log!(["withrawal purse addr", wAddr]) |
RevVault!("unforgeableAuthKey", *un, *keyCh) | for (authKey <- keyCh) {
// log!(["withrawal authKey", *authKey]) |
vault!("transfer", wAddr, amount, *authKey, *txCh) |
for (@(ok, x) <- txCh; @balance <- balCh) {
// log!(["withrawal transfer", (ok, x), balance]) |
match ok {
true => {
// RevVault transfer already checked for underflow
balCh!(balance - amount) |
ret!((true, *withdrawal))
}
false => {
ret!((false, x)) |
balCh!(balance)
}
}
}
}
}
}
} |
/**
* Withdraw to REVAddress
*/
contract purse(@"withdrawTo", @{ amount /\ Int }, @{ destRevAddr /\ String }, ret) = {
new keyCh, txCh in {
RevVault!("unforgeableAuthKey", *un, *keyCh) | for (authKey <- keyCh) {
vault!("transfer", destRevAddr, amount, *authKey, *txCh) |
for (@(ok, x) <- txCh; @balance <- balCh) {
match ok {
true => {
balCh!(balance - amount) |
ret!((true, x))
}
false => {
ret!((false, x)) |
balCh!(balance)
}
}
}
}
}
}
}
}
}
}
}
}
}
}