Skip to content

Commit 41f25a8

Browse files
authored
all: v2 refactoring to use feed byte identifier (#23)
1 parent 529816b commit 41f25a8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1977
-1561
lines changed

Diff for: .env.example

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export ECDSA_VS="[]"
3434
export ECDSA_RS="[]"
3535
export ECDSA_SS="[]"
3636
## IScribe::drop
37-
export FEED_INDEX=
37+
export FEED_ID=
3838
## IScribeOptimistic::setOpChallengePeriod
3939
export OP_CHALLENGE_PERIOD=
4040
## IScribeOptimistic::setMaxChallengeReward
@@ -51,4 +51,4 @@ export TEST_POKE_VAL=
5151
export TEST_POKE_AGE=
5252
export TEST_SCHNORR_SIGNATURE=
5353
export TEST_SCHNORR_COMMITMENT=
54-
export TEST_SCHNORR_SIGNERS_BLOB=
54+
export TEST_SCHNORR_FEED_IDS=

Diff for: .gas-snapshot

+63-71
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,69 @@
1-
LibBytesTest:test_getByteAtIndex() (gas: 378)
21
LibSecp256k1Test:testVectors_addAffinePoint() (gas: 2502307)
32
LibSecp256k1Test:test_isZeroPoint() (gas: 465)
43
LibSecp256k1Test:test_yParity() (gas: 530)
54
ScribeInvariantTest:invariant_bar_IsNeverZero() (runs: 256, calls: 3840, reverts: 0)
6-
ScribeInvariantTest:invariant_feeds_ImageIsZeroToLengthOfPubKeys() (runs: 256, calls: 3840, reverts: 0)
7-
ScribeInvariantTest:invariant_feeds_LinkToTheirPublicKeys() (runs: 256, calls: 3840, reverts: 0)
85
ScribeInvariantTest:invariant_poke_PokeTimestampsAreStrictlyMonotonicallyIncreasing() (runs: 256, calls: 3840, reverts: 0)
9-
ScribeInvariantTest:invariant_pubKeys_AtIndexZeroIsZeroPoint() (runs: 256, calls: 3840, reverts: 0)
10-
ScribeInvariantTest:invariant_pubKeys_LengthIsStrictlyMonotonicallyIncreasing() (runs: 256, calls: 3840, reverts: 0)
11-
ScribeInvariantTest:invariant_pubKeys_NonZeroPubKeyExistsAtMostOnce() (runs: 256, calls: 3840, reverts: 0)
12-
ScribeInvariantTest:invariant_pubKeys_ZeroPointIsNeverAddedAsPubKey() (runs: 256, calls: 3840, reverts: 0)
13-
ScribeOptimisticTest:test_Deployment() (gas: 59359)
14-
ScribeOptimisticTest:test_afterAuthedAction_1_drop() (gas: 248804)
15-
ScribeOptimisticTest:test_afterAuthedAction_1_setBar() (gas: 295659)
16-
ScribeOptimisticTest:test_afterAuthedAction_1_setChallengePeriod() (gas: 294180)
17-
ScribeOptimisticTest:test_afterAuthedAction_2_drop() (gas: 325741)
18-
ScribeOptimisticTest:test_afterAuthedAction_2_setBar() (gas: 384823)
19-
ScribeOptimisticTest:test_afterAuthedAction_2_setChallengePeriod() (gas: 384423)
20-
ScribeOptimisticTest:test_afterAuthedAction_3_drop() (gas: 251454)
21-
ScribeOptimisticTest:test_afterAuthedAction_3_setBar() (gas: 298843)
22-
ScribeOptimisticTest:test_afterAuthedAction_3_setChallengePeriod() (gas: 298316)
23-
ScribeOptimisticTest:test_afterAuthedAction_4_drop() (gas: 327250)
24-
ScribeOptimisticTest:test_afterAuthedAction_4_setBar() (gas: 386044)
25-
ScribeOptimisticTest:test_afterAuthedAction_4_setChallengePeriod() (gas: 384786)
26-
ScribeOptimisticTest:test_drop_IndexZero() (gas: 44243)
27-
ScribeOptimisticTest:test_drop_Multiple_IsAuthProtected() (gas: 14770)
28-
ScribeOptimisticTest:test_drop_Single_IsAuthProtected() (gas: 12782)
29-
ScribeOptimisticTest:test_latestAnswer_isTollProtected() (gas: 12505)
6+
ScribeInvariantTest:invariant_pubKeys_CannotIndexOutOfBoundsViaUint8Index() (runs: 256, calls: 3840, reverts: 0)
7+
ScribeInvariantTest:invariant_pubKeys_IndexedViaFeedId() (runs: 256, calls: 3840, reverts: 0)
8+
ScribeOptimisticTest:test_Deployment() (gas: 1204337)
9+
ScribeOptimisticTest:test_afterAuthedAction_1_drop() (gas: 202463)
10+
ScribeOptimisticTest:test_afterAuthedAction_1_setBar() (gas: 238545)
11+
ScribeOptimisticTest:test_afterAuthedAction_1_setChallengePeriod() (gas: 237041)
12+
ScribeOptimisticTest:test_afterAuthedAction_2_drop() (gas: 285432)
13+
ScribeOptimisticTest:test_afterAuthedAction_2_setBar() (gas: 325402)
14+
ScribeOptimisticTest:test_afterAuthedAction_2_setChallengePeriod() (gas: 324955)
15+
ScribeOptimisticTest:test_afterAuthedAction_3_drop() (gas: 205138)
16+
ScribeOptimisticTest:test_afterAuthedAction_3_setBar() (gas: 241782)
17+
ScribeOptimisticTest:test_afterAuthedAction_3_setChallengePeriod() (gas: 241184)
18+
ScribeOptimisticTest:test_afterAuthedAction_4_drop() (gas: 286761)
19+
ScribeOptimisticTest:test_afterAuthedAction_4_setBar() (gas: 326467)
20+
ScribeOptimisticTest:test_afterAuthedAction_4_setChallengePeriod() (gas: 325228)
21+
ScribeOptimisticTest:test_drop_Multiple_IsAuthProtected() (gas: 14650)
22+
ScribeOptimisticTest:test_drop_Single_IsAuthProtected() (gas: 13533)
23+
ScribeOptimisticTest:test_latestAnswer_isTollProtected() (gas: 12518)
3024
ScribeOptimisticTest:test_latestRoundData_isTollProtected() (gas: 14030)
31-
ScribeOptimisticTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 106973)
32-
ScribeOptimisticTest:test_lift_Multiple_FailsIf_MaxFeedsReached() (gas: 20286949)
33-
ScribeOptimisticTest:test_lift_Multiple_IsAuthProtected() (gas: 16490)
34-
ScribeOptimisticTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 22135)
35-
ScribeOptimisticTest:test_lift_Single_FailsIf_MaxFeedsReached() (gas: 19731489)
36-
ScribeOptimisticTest:test_lift_Single_IsAuthProtected() (gas: 12905)
37-
ScribeOptimisticTest:test_opChallenge_FailsIf_CalledSubsequently() (gas: 311729)
38-
ScribeOptimisticTest:test_opChallenge_FailsIf_InvalidSchnorrDataGiven() (gas: 283298)
39-
ScribeOptimisticTest:test_opChallenge_FailsIf_NoOpPokeToChallenge() (gas: 14897)
40-
ScribeOptimisticTest:test_peek_isTollProtected() (gas: 12870)
41-
ScribeOptimisticTest:test_peep_isTollProtected() (gas: 13200)
42-
ScribeOptimisticTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 242775)
43-
ScribeOptimisticTest:test_readWithAge_isTollProtected() (gas: 12357)
44-
ScribeOptimisticTest:test_read_isTollProtected() (gas: 11823)
45-
ScribeOptimisticTest:test_setBar_FailsIf_BarIsZero() (gas: 11793)
46-
ScribeOptimisticTest:test_setBar_IsAuthProtected() (gas: 13730)
47-
ScribeOptimisticTest:test_setMaxChallengeReward_IsAuthProtected() (gas: 13730)
48-
ScribeOptimisticTest:test_setOpChallengePeriod_DropsFinalizedOpPoke_If_NonFinalizedAfterUpdate() (gas: 299764)
49-
ScribeOptimisticTest:test_setOpChallengePeriod_FailsIf_OpChallengePeriodIsZero() (gas: 11567)
50-
ScribeOptimisticTest:test_setOpChallengePeriod_IsAuthProtected() (gas: 12332)
51-
ScribeOptimisticTest:test_toll_diss_IsAuthProtected() (gas: 13260)
52-
ScribeOptimisticTest:test_toll_kiss_IsAuthProtected() (gas: 12419)
53-
ScribeOptimisticTest:test_tryReadWithAge_isTollProtected() (gas: 13746)
54-
ScribeOptimisticTest:test_tryRead_isTollProtected() (gas: 12986)
55-
ScribeTest:test_Deployment() (gas: 42326)
56-
ScribeTest:test_drop_IndexZero() (gas: 16904)
57-
ScribeTest:test_drop_Multiple_IsAuthProtected() (gas: 13794)
58-
ScribeTest:test_drop_Single_IsAuthProtected() (gas: 12148)
59-
ScribeTest:test_latestAnswer_isTollProtected() (gas: 12092)
60-
ScribeTest:test_latestRoundData_isTollProtected() (gas: 13132)
61-
ScribeTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 106626)
62-
ScribeTest:test_lift_Multiple_FailsIf_MaxFeedsReached() (gas: 20286275)
63-
ScribeTest:test_lift_Multiple_IsAuthProtected() (gas: 15605)
64-
ScribeTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21437)
65-
ScribeTest:test_lift_Single_FailsIf_MaxFeedsReached() (gas: 19732318)
66-
ScribeTest:test_lift_Single_IsAuthProtected() (gas: 12535)
67-
ScribeTest:test_peek_isTollProtected() (gas: 12373)
68-
ScribeTest:test_peep_isTollProtected() (gas: 12461)
69-
ScribeTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 239335)
70-
ScribeTest:test_readWithAge_isTollProtected() (gas: 11969)
71-
ScribeTest:test_read_isTollProtected() (gas: 11695)
72-
ScribeTest:test_setBar_FailsIf_BarIsZero() (gas: 11392)
73-
ScribeTest:test_setBar_IsAuthProtected() (gas: 12823)
74-
ScribeTest:test_toll_diss_IsAuthProtected() (gas: 12533)
75-
ScribeTest:test_toll_kiss_IsAuthProtected() (gas: 12088)
76-
ScribeTest:test_tryReadWithAge_isTollProtected() (gas: 12871)
77-
ScribeTest:test_tryRead_isTollProtected() (gas: 12287)
25+
ScribeOptimisticTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 78843)
26+
ScribeOptimisticTest:test_lift_Multiple_IsAuthProtected() (gas: 16509)
27+
ScribeOptimisticTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21864)
28+
ScribeOptimisticTest:test_lift_Single_FailsIf_FeedIdAlreadyLifted() (gas: 73859)
29+
ScribeOptimisticTest:test_lift_Single_IsAuthProtected() (gas: 13053)
30+
ScribeOptimisticTest:test_opChallenge_CalldataEncodingAttack() (gas: 1528876)
31+
ScribeOptimisticTest:test_opChallenge_FailsIf_CalledSubsequently() (gas: 256890)
32+
ScribeOptimisticTest:test_opChallenge_FailsIf_InvalidSchnorrDataGiven() (gas: 226291)
33+
ScribeOptimisticTest:test_opChallenge_FailsIf_NoOpPokeToChallenge() (gas: 14879)
34+
ScribeOptimisticTest:test_peek_isTollProtected() (gas: 12886)
35+
ScribeOptimisticTest:test_peep_isTollProtected() (gas: 13216)
36+
ScribeOptimisticTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 185331)
37+
ScribeOptimisticTest:test_readWithAge_isTollProtected() (gas: 12397)
38+
ScribeOptimisticTest:test_read_isTollProtected() (gas: 11836)
39+
ScribeOptimisticTest:test_setBar_FailsIf_BarIsZero() (gas: 11908)
40+
ScribeOptimisticTest:test_setBar_IsAuthProtected() (gas: 13803)
41+
ScribeOptimisticTest:test_setMaxChallengeReward_IsAuthProtected() (gas: 13698)
42+
ScribeOptimisticTest:test_setOpChallengePeriod_DropsFinalizedOpPoke_If_NonFinalizedAfterUpdate() (gas: 242619)
43+
ScribeOptimisticTest:test_setOpChallengePeriod_FailsIf_OpChallengePeriodIsZero() (gas: 11633)
44+
ScribeOptimisticTest:test_setOpChallengePeriod_IsAuthProtected() (gas: 12393)
45+
ScribeOptimisticTest:test_toll_diss_IsAuthProtected() (gas: 13268)
46+
ScribeOptimisticTest:test_toll_kiss_IsAuthProtected() (gas: 12517)
47+
ScribeOptimisticTest:test_tryReadWithAge_isTollProtected() (gas: 13786)
48+
ScribeOptimisticTest:test_tryRead_isTollProtected() (gas: 13047)
49+
ScribeTest:test_Deployment() (gas: 1185225)
50+
ScribeTest:test_drop_Multiple_IsAuthProtected() (gas: 13643)
51+
ScribeTest:test_drop_Single_IsAuthProtected() (gas: 12648)
52+
ScribeTest:test_latestAnswer_isTollProtected() (gas: 12053)
53+
ScribeTest:test_latestRoundData_isTollProtected() (gas: 13080)
54+
ScribeTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 78456)
55+
ScribeTest:test_lift_Multiple_IsAuthProtected() (gas: 15556)
56+
ScribeTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21117)
57+
ScribeTest:test_lift_Single_FailsIf_FeedIdAlreadyLifted() (gas: 73352)
58+
ScribeTest:test_lift_Single_IsAuthProtected() (gas: 12631)
59+
ScribeTest:test_peek_isTollProtected() (gas: 12337)
60+
ScribeTest:test_peep_isTollProtected() (gas: 12425)
61+
ScribeTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 181717)
62+
ScribeTest:test_readWithAge_isTollProtected() (gas: 11957)
63+
ScribeTest:test_read_isTollProtected() (gas: 11678)
64+
ScribeTest:test_setBar_FailsIf_BarIsZero() (gas: 11457)
65+
ScribeTest:test_setBar_IsAuthProtected() (gas: 12846)
66+
ScribeTest:test_toll_diss_IsAuthProtected() (gas: 12489)
67+
ScribeTest:test_toll_kiss_IsAuthProtected() (gas: 12156)
68+
ScribeTest:test_tryReadWithAge_isTollProtected() (gas: 12838)
69+
ScribeTest:test_tryRead_isTollProtected() (gas: 12298)

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# VSCode settings
2+
.vscode/
3+
14
# Compiler files
25
cache/
36
out/

Diff for: CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Common Changelog](https://common-changelog.org/).
66

7+
[2.0.0]: https://github.com/chronicleprotocol/scribe/releases/tag/v2.0.0
78
[1.2.0]: https://github.com/chronicleprotocol/scribe/releases/tag/v1.2.0
89
[1.1.0]: https://github.com/chronicleprotocol/scribe/releases/tag/v1.1.0
910
[1.0.0]: https://github.com/chronicleprotocol/scribe/releases/tag/v1.0.0
1011

12+
## [2.0.0] - 2023-11-27
13+
14+
### Changed
15+
16+
- **Breaking** Use 1-byte identifier for feeds based on highest-order byte of their addresses instead of their storage array's index ([#23](https://github.com/chronicleprotocol/scribe/pull/23))
17+
- **Breaking** Change `IScribe` and `IScribeOptimistic` interfaces to account for new feed identification ([#23](https://github.com/chronicleprotocol/scribe/pull/23))
18+
19+
### Fixed
20+
21+
- DOS vector in `ScribeOptimistic::opPoke` making `ScribeOptimistic::opChallenge` economically unprofitable ([#23](https://github.com/chronicleprotocol/scribe/pull/23))
22+
- Possibility to successfully `opChallenge` a valid `opPoke` via non-default calldata encoding ([#23](https://github.com/chronicleprotocol/scribe/pull/23))
23+
1124
## [1.2.0] - 2023-09-29
1225

1326
### Added

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ $ FOUNDRY_PROFILE=intense forge test # Run all tests in intense mode
3434
$ forge test --nmt "FuzzDifferentialOracleSuite" # Run only non-differential fuzz tests
3535
```
3636

37-
Note that in order to run the whole test suite, i.e. including differential fuzz tests, the oracle-suite's [`schnorr`](https://github.com/chronicleprotocol/oracle-suite) binary needs to be present inside the `bin/` directory.
37+
Note that in order to run the whole test suite, i.e. including differential fuzz tests, the oracle-suite's musig [`schnorr`](https://github.com/chronicleprotocol/musig/tree/master/cmd/schnorr) binary needs to be present inside the `bin/` directory.
3838

3939
Lint:
4040

Diff for: assets/benchmarks.png

-1.78 KB
Loading

Diff for: audits/[email protected]

510 KB
Binary file not shown.

Diff for: docs/Benchmarks.md

+10-8
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
The benchmark for `Scribe` is based on the `poke()` function while the benchmark for `ScribeOptimistic` being based on the `opPoke()` function.
44

5-
| `bar` | `Scribe::poke()` | `ScribeOptimistic::opPoke()` |
6-
|-------|--------------------|------------------------------|
7-
| 5 | 79,428 | 66,462 |
8-
| 10 | 106,862 | 66,534 |
9-
| 15 | 131,834 | 66,603 |
10-
| 20 | 158,263 | 66,663 |
11-
| 50 | 315,655 | 67,437 |
12-
| 100 | 577,919 | 68,845 |
5+
| `bar` | `Scribe::poke()` | `ScribeOptimistic::opPoke()` |
6+
| ----- | ---------------- | ---------------------------- |
7+
| 5 | 81,025 | 68,944 |
8+
| 10 | 106,395 | 69,004 |
9+
| 15 | 134,342 | 69,061 |
10+
| 20 | 159,488 | 69,133 |
11+
| 50 | 320,473 | 69,908 |
12+
| 100 | 585,993 | 71,315 |
13+
| 200 | 1,119,535 | 73,759 |
14+
| 255 | 1,411,702 | 74,852 |
1315

1416
The following visualization shows the gas usage for different numbers of `bar`:
1517

Diff for: docs/Invariants.md

+6-49
Original file line numberDiff line numberDiff line change
@@ -68,67 +68,24 @@ This document specifies invariants of the Scribe and ScribeOptimistic oracle con
6868
6969
## `{Scribe, ScribeOptimistic}::_pubKeys`
7070
71-
* `_pubKeys[0]` is the zero point:
71+
* `_pubKeys`' length is 256:
7272
```
73-
_pubKeys[0].isZeroPoint()
73+
_pubKeys.length == 256
7474
```
7575
76-
* A non-zero public key exists at most once:
76+
* Public keys are stored at the index of their address' first byte:
7777
```
78-
∀x ∊ PublicKeys: x.isZeroPoint() ∨ count(x in _pubKeys) <= 1
79-
```
80-
81-
* Length is strictly monotonically increasing:
82-
```
83-
preTx(_pubKeys.length) != postTx(_pubKeys.length)
84-
→ preTx(_pubKeys.length) < posTx(_pubKeys.length)
85-
```
86-
87-
* Existing public key may only be deleted, never mutated:
88-
```
89-
∀x ∊ uint: x < _pubKeys.length ⋀ preTx(_pubKeys[x]) != postTx(_pubKeys[x])
90-
→ postTx(_pubKeys[x].isZeroPoint())
91-
```
92-
93-
* Newly added public key is non-zero:
94-
```
95-
preTx(_pubKeys.length) != postTx(_pubKeys.length)
96-
→ postTx(!_pubKeys[_pubKeys.length-1].isZeroPoint())
78+
∀id ∊ Uint8: _pubKeys[id].isZeroPoint() ∨ (_pubKeys[id].toAddress() >> 152) == id
9779
```
9880
9981
* Only functions `lift` and `drop` may mutate the array's state:
10082
```
101-
xuint: preTx(_pubKeys[x]) != postTx(_pubKeys[x])
83+
idUint8: preTx(_pubKeys[id]) != postTx(_pubKeys[id])
10284
→ msg.sig ∊ {"lift", "drop"}
10385
```
10486
10587
* Array's state may only be mutated by auth'ed caller:
10688
```
107-
∀x ∊ uint: preTx(_pubKeys[x]) != postTx(_pubKeys[x])
108-
→ authed(msg.sender)
109-
```
110-
111-
## `{Scribe, ScribeOptimistic}::_feeds`
112-
113-
* Image of mapping is `[0, _pubKeys.length)`:
114-
```
115-
∀x ∊ Address: _feeds[x] ∊ [0, _pubKeys.length)
116-
```
117-
118-
* Image of mapping links to feed's public key in `_pubKeys`:
119-
```
120-
∀x ∊ Address: _feeds[x] = y ⋀ y != 0
121-
→ _pubKeys[y].toAddress() == x
122-
```
123-
124-
* Only functions `lift` and `drop` may mutate the mapping's state:
125-
```
126-
∀x ∊ Address: preTx(_feeds[x]) != postTx(_feeds[x])
127-
→ msg.sig ∊ {"lift", "drop"}
128-
```
129-
130-
* Mapping's state may only be mutated by auth'ed caller:
131-
```
132-
∀x ∊ Address: preTx(_feeds[x]) != postTx(_feeds[x])
89+
∀id ∊ Uint8: preTx(_pubKeys[id]) != postTx(_pubKeys[id])
13390
→ authed(msg.sender)
13491
```

Diff for: docs/Management.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ $ forge script \
111111

112112
Set the following environment variables:
113113

114-
- `FEED_INDEX`: The feed's index
114+
- `FEED_ID`: The feed's id
115115

116116
Run:
117117

@@ -120,7 +120,7 @@ $ forge script \
120120
--private-key $PRIVATE_KEY \
121121
--broadcast \
122122
--rpc-url $RPC_URL \
123-
--sig $(cast calldata "drop(address,uint)" $SCRIBE $FEED_INDEX) \
123+
--sig $(cast calldata "drop(address,uint)" $SCRIBE $FEED_ID) \
124124
-vvv \
125125
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
126126
```

Diff for: docs/Scribe.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,24 @@ For more info, see [`LibSecp256k1::addAffinePoint()`](../src/libs/LibSecp256k1.s
5050

5151
The `poke()` function has to receive the set of feeds, i.e. public keys, that participated in the Schnorr multi-signature.
5252

53-
To reduce the calldata load, Scribe does not use type `address`, which uses 20 bytes per feed, but encodes the unique feeds' identifier's byte-wise into a `bytes` type called `signersBlob`.
53+
To reduce the calldata load, Scribe does not use type `address`, which uses 20 bytes per feed, but encodes the feeds' identifier's byte-wise into a `bytes` type called `feedIds`.
5454

55-
For more info, see [`LibSchnorrData.sol`](../src/libs/LibSchnorrData.sol).
55+
A feed's identifier is defined as the highest order byte of the feed's address and can be computed via `uint8(uint(uint160(feedAddress)) >> 152)`.
5656

5757
## Lifting Feeds
5858

5959
Feeds _must_ prove the integrity of their public key by proving the ownership of the corresponding private key. The `lift()` function therefore expects an ECDSA signed message, for more info see [`IScribe.feedRegistrationMessage()`](../src/IScribe.sol).
6060

6161
If public key's would not be verified, the Schnorr signature verification would be vulnerable to rogue-key attacks. For more info, see [`docs/Schnorr.md`](./Schnorr.md#key-aggregation-for-multisignatures).
6262

63-
Also, the number of state-changing `lift()` executions is limited to `type(uint8).max-1`, i.e. 254. After reaching this limit, no further `lift()` calls can be executed. For more info, see [`IScribe.maxFeeds()`](../src/IScribe.sol).
64-
6563
## Chainlink Compatibility
6664

6765
Scribe aims to be partially Chainlink compatible by implementing the most widely, and not deprecated, used functions of the `IChainlinkAggregatorV3` interface.
6866

6967
The following `IChainlinkAggregatorV3` functions are provided:
7068
- `latestRoundData()`
7169
- `decimals()`
70+
- `latestAnswer()`
7271

7372
## Optimistic-Flavored Scribe
7473

Diff for: foundry.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ via_ir = true
1212
extra_output_files = ["metadata", "irOptimized"]
1313

1414
# Testing
15-
fuzz = { runs = 10 }
15+
fuzz = { runs = 50 }
1616
block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT
1717

1818
[invariant]

0 commit comments

Comments
 (0)