-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreadme.txt
383 lines (287 loc) · 15.4 KB
/
readme.txt
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
Name: Delegator2
Description: Key authority event log for Kiwi News Protocol
Requires: EIP-712, EIP-2098
Instructions
------------
Any caller may call `etch` such that:
1. `data[0]` and `data[1]` are respectively the first and the second
part of an EIP-2098 "Compact Signature."
2. `data[2]` is a `bytes32` segmented into:
0x4E774b8530d6f3A21C449A6f0D9A1229aB2b8C47000000000000000000000001
^ ^^ ^^
| || ||
.--------------------------------------..---------------------.|
(a bytes20-long `address to`) (empty) |
|
(the `bool authorize`)
2.1 `address to` is the entity the transaction's sender (`address
from`) is delegating authority or revoking it.
2.2 If `bool authorize` is `true` it means `address from` is
delegating their authority to `address to`.
2.3 If `bool authorize` is `false` it means `address from` is revoking
their authority from `address to`.
3. The signature in `data[0]` and `data[1]` must be signed according to
EIP-712.
3.1 The message is conceptually generated using the following struct:
struct Authorization {
address from;
bool authorize;
}
3.2 And an EIP-712 domain separator using the following types and
values:
struct EIP712Domain {
string name = "kiwinews";
string version = "1.0.0";
uint256 chainId = <chainId>;
address verifyingContract = 0x08b7ECFac2c5754ABafb789c84F8fa37c9f088B0
bytes32 salt = 0xfe7a9d68e99b6942bb3a36178b251da8bd061c20ed1e795207ae97183b590e5b;
}
3.3 The message is then signed by `address to` and tucked into a
transaction signed by `address from` and sent to the network.
Interpretation:
---------------
0. We consider a key delegation from `address from` to `address to` valid
if:
0.1 we can "ecrecover" `address to` (the first 20 bytes of `data[2]`)
from `data[0]` and `data[1]` (an EIP-2098 "Compact Signature") using
the above-mentioned procedure; AND
0.2 if the last bit (`bool authorize`) of `data[2]` is "1"; AND
0.3 if the `address from` of the `Authorization` message appears as the
"from" property on the event log's transaction receipt.
1. We consider a key revocation by `address from` of `address to` valid if:
1.1 we can "ecrecover" `address to` (the first 20 bytes of `data[2]`)
from `data[0]` and `data[1]` (also an EIP-2098 "Compact Signature")
using the above-mentioned procedure; AND
1.2 if the last bit (`bool authorize`) of `data[2]` is "0"; AND
1.3 if the `address from` of the `Authorization` message appears as the
"from" property on the event log's transaction receipt.
Organize
--------
We receive all delegations in an ordered list, validate each according to the
above-outlined rules and then organize the them according to the following
rules:
- Each `address from` can delegate to multiple `address to`.
- Each `address to` can only be delegated to by one `address from`.
- `address to` and `address from` are never the same.
- An `address to` cannot become an `address from` and vice versa.
- The first delegation from an `address from` to an `address to` is
considered the user's true intent.
- A revocation is only valid if there has been a prior delegation.
- A delegation after a revocation does not make a key usable again.
This organization produces an object mapping of each `address to` to the
`address from` of the latest valid delegation. If a delegation is invalid, it
is ignored.
If an `address from` tries to delegate to an `address to` that has already been
delegated to, we ignore the new delegation.
If an `address from` revokes its delegation to an `address to`, we remove the
existing delegation.
If an `address from` tries to delegate to an `address to` after revoking its
delegation to that address, the function ignores the new delegation. This
ensures that a delegation after a revocation does not make a key usable again.
If an `address from` tries to delegate to an `address to` that has already been
used as an `address from`, or if an `address to` tries to become an `address
from`, the function ignores the new delegation. This ensures that an `address
to` cannot become an `address from` and vice versa, maintaining the integrity
of the delegation process.
Rationale
---------
Following is an explaination on why we authorize both:
1. The `address from` to forward privileges to `address to` by signing a
transaction containing the `address to` in calldata; and
2. the `address to` to accept `address from`'s privileges by signing an EIP-712
message including `address from`.
For (1), we require `address from` to sign `address to` as to authorize
`address to` with the Kiwi News node. It confirms that `address from` allows
adding `address to`.
Similarly, for (2), we require `address to` to sign an EIP-712 payload
including `address from` as to authorize for all future messages in the Kiwi
News Protocol that if we ecrecover `address to` from a message's signature,
then we are certain about which `address from` it must be credited to.
This primarily has to do with how the Kiwi News Protocol constructs messages
where we only sign the payload of `href`, `title` and `timestamp` with `address
to` but not to which "identity" to credit the contribution to. As including the
`address to` as a byte payload in a tranaction's call data (unauthorized by
`address to` through a signature), would allow an impersonator to frontrun the
`etch` transaction and furthermore impersonate the original user's transaction.
The frontrunner could manage to register a user's `address to` with then all
future signed messages of `address to`-signed messages being credited to the
frontrunner and not the original user (assuming the user would notice at
first).
Farcaster actually prevents this case by requiring each protocol message to
include essentially the "fid" or or "fid owner address." That is because in
this case they force `address to` to make a claim on which `address from` they
represent which can then be cross-validated with the claim from `address
from`'s onchain claim.
It is, hence, necessary to sign in both directions, however, it would be
possible to just sign from `address from` to `address to` is the protocol
messages then included a signed claim of `address to` to which `address from`
they represent.
Finally, there are two further points of rationale:
- In an even earlier version of the Kiwi News Protocol we had considered
storing delegations on our set reconciliation network. However, it would
have allowed a malicious node operator to back-date a delegation message
or its revocation - which could have interfered with the network's
reconciliation algorithm.
- In a prior interation of the organization procedure we considered always
using the latest delegations and revocations as the user's intent. However,
this increased complexity at the Kiwi News Protocol node level, as then e.g.
an `address to`, priorly assigned to an `address from_1` could suddenly
switch to `address from_2`. As those addresses may be represented by ENS
names, it would be confusing to users if a post suddenly changed author.
Considerations
--------------
- Indexers are recommended to ignore valid delegations where the `address
from`, `address to` and the transaction receipt's "from" property are the
same address to avoid cycles.
- The transaction running the eth-call and its payload are not replayable on
other chains as the chain ID is part of the EIP-712 domain separator. They
are neither replayable on the same chain as both the verifying contract's
address and its version are part of the separator too.
- Using CREATE2, the system may be run on multiple chains in parallel. However,
a total ordering of all transactions from those systems must exist. Hence, if
say one such contract is run on ETH mainnet and another one on an L2, a total
order may be producable by virtue of the L2 using L1 storage. This seems to
hold as well for two L2s considering that their state access on L2 is
happening in atomic transactions over which we can create a total order.
- Other applications that require a similar system may use the delegator2
contract but with a different EIP-712 domain separator. Kiwi News Protocol
would simply consider those payloads invalid, which can be OK if it doesn't
waste too much compute spent on undirected validation.
Deployment
----------
- CREATE2 is used to deploy delegator2 to a deterministic address independent
of chainId.
- `DEPLOYER`: 0x0000000000ffe8b47b3e2130213b802212439497
- `SALT`: 0x0000000000000000000000000000000000000000f00df00df00df00df00df00d
- `INITCODE`: 0x608060405234801561001057600080fd5b5060e18061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80630baa83f814602d575b600080fd5b603c60383660046076565b603e565b005b7f9fcbf2ac7d9825115ae81812d10efa7fce04fcc9ca46f1d416aba53cdea8483e81604051606b9190609c565b60405180910390a150565b600060608284031215608757600080fd5b82606083011115609657600080fd5b50919050565b6060818101908383379291505056fea2646970667358221220a023699f314fe81dbaaa4e917b161d13bc252584f8e3602dca37be52f9e8b5a364736f6c63430008110033
- `ADDRESS`: 0x08b7ECFac2c5754ABafb789c84F8fa37c9f088B0
- Deployed to:
- Optimism
SDK.js
------
(in the ./sdk folder)
Installation
------------
Install via npm. Consider that ethers is a peer dependency.
npm install @attestate/delegator2 ethers@^5.7.0
Note:
If you're concerned about bundle size, consider installing the respective
`@ethersproject` sub packages as defined in `./sdk/package.json`'s
'dependencies'.
Usage
-----
You can import the create, validate and other functions from the package like
so:
import { create, validate } from '@attestate/delegator2';
validate(data, from)
--------------------
This function validates the data from the event `Authorize(bytes32)` and
checks if the from address matches the "from" property from the transaction
receipt of the event log.
Parameters
- `bytes32[3] data`: The `data` from the `Authorize(bytes32)` event.
- `address from`: The "from" property from the transaction receipt of the
event log.
Returns
- An object containing the following properties:
- `address from`: The address which delegates authority.
- `address to`: The address to which authority is being delegated.
- `bool authorize`: A boolean value indicating whether the operation is a
delegation (`true`) or a revocation (`false`).
create(signer, from, to, authorize)
-----------------------------------
This function creates a delegation or revocation message.
Parameters
- `Wallet signer`: An ethers.js signer instance. This is used to sign the
EIP-712 typed data.
- `address from`: The address of the transaction sender.
- `address to`: The address to which authority is being delegated.
- `bool authorize`: A boolean value indicating whether the operation is a
delegation (true) or a revocation (false).
Returns
- An array of three bytes32 strings, each prefixed with 0x. These strings
represent the following (more details? See description of protocol
above):
- The first part of the signed message.
- The second part of the signed message.
- The address to which authority is being delegated or revoked, followed
by a flag indicating the operation (1 for delegation, 0 for
revocation).
organize(payloads, domain = EIP712_DOMAIN)
------------------------------------------
This function organizes a list of payloads into a delegations object.
Parameters
- `Object[] payloads`: An array of objects, each containing data from the
`Authorize(bytes32)` event and a receipt with a "from" property.
- `string domain`: The EIP-712 domain. Defaults to the internal
EIP712_DOMAIN.
Returns
- `Object`: An object mapping 'to' addresses to 'from' addresses. The
'from' address must always be part of the allowlist. Both 'to' and 'from'
are Ethereum addresses. All included addresses are checksummed.
Notes
- Applies the "Organize" rules of the protocol as outlined above.
- The function logs messages for each skipped payload and the reason for
skipping.
eligible(allowlist, delegations, address)
-----------------------------------------
This function checks if an Ethereum address is CURRENTLY eligible based on
the allowlist and delegations.
Parameters
- `Set allowlist`: A list of Ethereum addresses that have minted the Kiwi
News NFT. Addresses must be check-summed.
- `Object delegations`: An object mapping 'to' addresses to 'from'
addresses. The 'from' address must always be part of the allowlist. Both
'to' and 'from' are Ethereum addresses. This object is expected to be the
result of the 'organize' function and all included addresses must be
checksummed.
- `string address`: The Ethereum address to check for eligibility. Must be
check-summed.
Returns
- (string|false): Returns the eligible address if found in the allowlist or
delegations, otherwise returns false.
eligibleAt(accounts, delegations, params = {address, validationTime, tokenId})
------------------------------------------------------------------------------
This function checks if an Ethereum address is (or has been) eligible based
on the accounts and delegations.
Parameters
- `Object accounts`: An object consisting of holder information. Every key
is an Ethereum address that leads to an object with the properties. Below
is how we expect the accounts object to look like:
{
[address]: {
tokens: {
[tokenId] : [{
start: <decimal-unix-timestamp>,
end: <decimal-unix-timestamp>,
},
//...
],
//...
},
balance: <decimal-number-of-kiwi-passes-held>
},
...
}
- `Object delegations`: An object mapping 'to' addresses to 'from'
addresses. The 'from' address must always be part of the allowlist. Both
'to' and 'from' are Ethereum addresses. This object is expected to be the
result of the 'organize' function and all included addresses must be
checksummed.
- `Object pararms`:
- `string address`: The Ethereum address to check for eligibility. Must be
check-summed.
- `Date validationTime`: The date at which the eligibility check should be
made at.
- `string tokenId` (optional): The tokenId with which a user makes their
claim for eligibility. Please note: After v0.4.0, we made the `eligibleAt`
function more precise at evaluating token ownership claims. Omitting the
`string tokenId` will evaluate eligibility based on the imprecise v0.4.0
logic, whereas submitting the tokenId will precisely evaluate the claim
Returns
- (string|false): Returns the eligible address if found in the allowlist or
delegations, otherwise returns false.
License
-------
Contract licensed as SPDX-License-Identifier: AGPL-3.0
SDK licensed as SPDX-License-Identifier: GPL-3.0-only