Skip to content

Commit

Permalink
all: v2 refactoring to use feed byte identifier
Browse files Browse the repository at this point in the history
  • Loading branch information
pmerkleplant committed Sep 26, 2023
1 parent 31f3059 commit 6d0e56d
Show file tree
Hide file tree
Showing 38 changed files with 1,932 additions and 1,195 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export ECDSA_VS="[]"
export ECDSA_RS="[]"
export ECDSA_SS="[]"
## IScribe::drop
export FEED_INDEX=
export FEED_ID=
## IScribeOptimistic::setOpChallengePeriod
export OP_CHALLENGE_PERIOD=
## IScribeOptimistic::setMaxChallengeReward
Expand All @@ -51,4 +51,4 @@ export TEST_POKE_VAL=
export TEST_POKE_AGE=
export TEST_SCHNORR_SIGNATURE=
export TEST_SCHNORR_COMMITMENT=
export TEST_SCHNORR_SIGNERS_BLOB=
export TEST_SCHNORR_FEED_IDS=
129 changes: 60 additions & 69 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,64 @@ LibSecp256k1Test:testVectors_addAffinePoint() (gas: 2502307)
LibSecp256k1Test:test_isZeroPoint() (gas: 465)
LibSecp256k1Test:test_yParity() (gas: 530)
ScribeInvariantTest:invariant_bar_IsNeverZero() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_feeds_ImageIsZeroToLengthOfPubKeys() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_feeds_LinkToTheirPublicKeys() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_poke_PokeTimestampsAreStrictlyMonotonicallyIncreasing() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_pubKeys_AtIndexZeroIsZeroPoint() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_pubKeys_LengthIsStrictlyMonotonicallyIncreasing() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_pubKeys_NonZeroPubKeyExistsAtMostOnce() (runs: 256, calls: 3840, reverts: 0)
ScribeInvariantTest:invariant_pubKeys_ZeroPointIsNeverAddedAsPubKey() (runs: 256, calls: 3840, reverts: 0)
ScribeOptimisticTest:test_Deployment() (gas: 56925)
ScribeOptimisticTest:test_afterAuthedAction_1_drop() (gas: 248716)
ScribeOptimisticTest:test_afterAuthedAction_1_setBar() (gas: 295527)
ScribeOptimisticTest:test_afterAuthedAction_1_setChallengePeriod() (gas: 294070)
ScribeOptimisticTest:test_afterAuthedAction_2_drop() (gas: 325609)
ScribeOptimisticTest:test_afterAuthedAction_2_setBar() (gas: 384647)
ScribeOptimisticTest:test_afterAuthedAction_2_setChallengePeriod() (gas: 384247)
ScribeOptimisticTest:test_afterAuthedAction_3_drop() (gas: 251331)
ScribeOptimisticTest:test_afterAuthedAction_3_setBar() (gas: 298667)
ScribeOptimisticTest:test_afterAuthedAction_3_setChallengePeriod() (gas: 298140)
ScribeOptimisticTest:test_afterAuthedAction_4_drop() (gas: 327074)
ScribeOptimisticTest:test_afterAuthedAction_4_setBar() (gas: 385846)
ScribeOptimisticTest:test_afterAuthedAction_4_setChallengePeriod() (gas: 384610)
ScribeOptimisticTest:test_drop_IndexZero() (gas: 44243)
ScribeOptimisticTest:test_drop_Multiple_IsAuthProtected() (gas: 14726)
ScribeOptimisticTest:test_drop_Single_IsAuthProtected() (gas: 12760)
ScribeOptimisticTest:test_latestRoundData_isTollProtected() (gas: 13986)
ScribeOptimisticTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 106951)
ScribeOptimisticTest:test_lift_Multiple_FailsIf_MaxFeedsReached() (gas: 20286905)
ScribeOptimisticTest:test_lift_Multiple_IsAuthProtected() (gas: 16446)
ScribeOptimisticTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 22113)
ScribeOptimisticTest:test_lift_Single_FailsIf_MaxFeedsReached() (gas: 19731467)
ScribeOptimisticTest:test_lift_Single_IsAuthProtected() (gas: 12905)
ScribeOptimisticTest:test_opChallenge_FailsIf_CalledSubsequently() (gas: 311597)
ScribeOptimisticTest:test_opChallenge_FailsIf_InvalidSchnorrDataGiven() (gas: 283188)
ScribeOptimisticTest:test_opChallenge_FailsIf_NoOpPokeToChallenge() (gas: 14853)
ScribeOptimisticTest:test_peek_isTollProtected() (gas: 12826)
ScribeOptimisticTest:test_peep_isTollProtected() (gas: 13178)
ScribeOptimisticTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 242731)
ScribeOptimisticTest:test_readWithAge_isTollProtected() (gas: 12357)
ScribeOptimisticTest:test_read_isTollProtected() (gas: 11801)
ScribeOptimisticTest:test_setBar_FailsIf_BarIsZero() (gas: 11771)
ScribeOptimisticTest:test_setBar_IsAuthProtected() (gas: 13686)
ScribeOptimisticTest:test_setMaxChallengeReward_IsAuthProtected() (gas: 13686)
ScribeOptimisticTest:test_setOpChallengePeriod_DropsFinalizedOpPoke_If_NonFinalizedAfterUpdate() (gas: 299610)
ScribeOptimisticTest:test_setOpChallengePeriod_FailsIf_OpChallengePeriodIsZero() (gas: 11545)
ScribeOptimisticTest:test_setOpChallengePeriod_IsAuthProtected() (gas: 12310)
ScribeOptimisticTest:test_toll_diss_IsAuthProtected() (gas: 13216)
ScribeOptimisticTest:test_toll_kiss_IsAuthProtected() (gas: 12397)
ScribeOptimisticTest:test_tryReadWithAge_isTollProtected() (gas: 13702)
ScribeOptimisticTest:test_tryRead_isTollProtected() (gas: 12964)
ScribeTest:test_Deployment() (gas: 40852)
ScribeTest:test_drop_IndexZero() (gas: 16904)
ScribeTest:test_drop_Multiple_IsAuthProtected() (gas: 13750)
ScribeTest:test_drop_Single_IsAuthProtected() (gas: 12126)
ScribeTest:test_latestRoundData_isTollProtected() (gas: 13088)
ScribeTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 106604)
ScribeTest:test_lift_Multiple_FailsIf_MaxFeedsReached() (gas: 20286231)
ScribeTest:test_lift_Multiple_IsAuthProtected() (gas: 15561)
ScribeTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21415)
ScribeTest:test_lift_Single_FailsIf_MaxFeedsReached() (gas: 19732296)
ScribeTest:test_lift_Single_IsAuthProtected() (gas: 12535)
ScribeTest:test_peek_isTollProtected() (gas: 12329)
ScribeTest:test_peep_isTollProtected() (gas: 12439)
ScribeTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 239291)
ScribeTest:test_readWithAge_isTollProtected() (gas: 11969)
ScribeTest:test_read_isTollProtected() (gas: 11673)
ScribeTest:test_setBar_FailsIf_BarIsZero() (gas: 11370)
ScribeTest:test_setBar_IsAuthProtected() (gas: 12779)
ScribeTest:test_toll_diss_IsAuthProtected() (gas: 12489)
ScribeTest:test_toll_kiss_IsAuthProtected() (gas: 12066)
ScribeTest:test_tryReadWithAge_isTollProtected() (gas: 12827)
ScribeTest:test_tryRead_isTollProtected() (gas: 12265)
ScribeInvariantTest:invariant_pubKeys_IndexedViaFeedId() (runs: 256, calls: 3840, reverts: 0)
ScribeOptimisticTest:test_Deployment() (gas: 1228808)
ScribeOptimisticTest:test_afterAuthedAction_1_drop() (gas: 203457)
ScribeOptimisticTest:test_afterAuthedAction_1_setBar() (gas: 239071)
ScribeOptimisticTest:test_afterAuthedAction_1_setChallengePeriod() (gas: 237611)
ScribeOptimisticTest:test_afterAuthedAction_2_drop() (gas: 286191)
ScribeOptimisticTest:test_afterAuthedAction_2_setBar() (gas: 325422)
ScribeOptimisticTest:test_afterAuthedAction_2_setChallengePeriod() (gas: 324975)
ScribeOptimisticTest:test_afterAuthedAction_3_drop() (gas: 206091)
ScribeOptimisticTest:test_afterAuthedAction_3_setBar() (gas: 242256)
ScribeOptimisticTest:test_afterAuthedAction_3_setChallengePeriod() (gas: 241658)
ScribeOptimisticTest:test_afterAuthedAction_4_drop() (gas: 287468)
ScribeOptimisticTest:test_afterAuthedAction_4_setBar() (gas: 326457)
ScribeOptimisticTest:test_afterAuthedAction_4_setChallengePeriod() (gas: 325240)
ScribeOptimisticTest:test_drop_Multiple_IsAuthProtected() (gas: 14532)
ScribeOptimisticTest:test_drop_Single_IsAuthProtected() (gas: 13415)
ScribeOptimisticTest:test_latestRoundData_isTollProtected() (gas: 13912)
ScribeOptimisticTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 79306)
ScribeOptimisticTest:test_lift_Multiple_IsAuthProtected() (gas: 16424)
ScribeOptimisticTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21768)
ScribeOptimisticTest:test_lift_Single_FailsIf_FeedIdAlreadyLifted() (gas: 74296)
ScribeOptimisticTest:test_lift_Single_IsAuthProtected() (gas: 12979)
ScribeOptimisticTest:test_opChallenge_FailsIf_CalledSubsequently() (gas: 255199)
ScribeOptimisticTest:test_opChallenge_FailsIf_InvalidSchnorrDataGiven() (gas: 226894)
ScribeOptimisticTest:test_opChallenge_FailsIf_NoOpPokeToChallenge() (gas: 14764)
ScribeOptimisticTest:test_peek_isTollProtected() (gas: 12768)
ScribeOptimisticTest:test_peep_isTollProtected() (gas: 13120)
ScribeOptimisticTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 186100)
ScribeOptimisticTest:test_readWithAge_isTollProtected() (gas: 12323)
ScribeOptimisticTest:test_read_isTollProtected() (gas: 11784)
ScribeOptimisticTest:test_setBar_FailsIf_BarIsZero() (gas: 11812)
ScribeOptimisticTest:test_setBar_IsAuthProtected() (gas: 13685)
ScribeOptimisticTest:test_setMaxChallengeReward_IsAuthProtected() (gas: 13580)
ScribeOptimisticTest:test_setOpChallengePeriod_DropsFinalizedOpPoke_If_NonFinalizedAfterUpdate() (gas: 243099)
ScribeOptimisticTest:test_setOpChallengePeriod_FailsIf_OpChallengePeriodIsZero() (gas: 11537)
ScribeOptimisticTest:test_setOpChallengePeriod_IsAuthProtected() (gas: 12297)
ScribeOptimisticTest:test_toll_diss_IsAuthProtected() (gas: 13150)
ScribeOptimisticTest:test_toll_kiss_IsAuthProtected() (gas: 12465)
ScribeOptimisticTest:test_tryReadWithAge_isTollProtected() (gas: 13668)
ScribeOptimisticTest:test_tryRead_isTollProtected() (gas: 12951)
ScribeTest:test_Deployment() (gas: 1209573)
ScribeTest:test_drop_Multiple_IsAuthProtected() (gas: 13577)
ScribeTest:test_drop_Single_IsAuthProtected() (gas: 12582)
ScribeTest:test_latestRoundData_isTollProtected() (gas: 13014)
ScribeTest:test_lift_Multiple_FailsIf_ECDSADataInvalid() (gas: 78937)
ScribeTest:test_lift_Multiple_IsAuthProtected() (gas: 15517)
ScribeTest:test_lift_Single_FailsIf_ECDSADataInvalid() (gas: 21073)
ScribeTest:test_lift_Single_FailsIf_FeedIdAlreadyLifted() (gas: 73849)
ScribeTest:test_lift_Single_IsAuthProtected() (gas: 12609)
ScribeTest:test_peek_isTollProtected() (gas: 12271)
ScribeTest:test_peep_isTollProtected() (gas: 12381)
ScribeTest:test_poke_Initial_FailsIf_AgeIsZero() (gas: 182660)
ScribeTest:test_readWithAge_isTollProtected() (gas: 11935)
ScribeTest:test_read_isTollProtected() (gas: 11656)
ScribeTest:test_setBar_FailsIf_BarIsZero() (gas: 11413)
ScribeTest:test_setBar_IsAuthProtected() (gas: 12780)
ScribeTest:test_toll_diss_IsAuthProtected() (gas: 12423)
ScribeTest:test_toll_kiss_IsAuthProtected() (gas: 12134)
ScribeTest:test_tryReadWithAge_isTollProtected() (gas: 12772)
ScribeTest:test_tryRead_isTollProtected() (gas: 12254)
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# VSCode settings
.vscode/

# Compiler files
cache/
out/
Expand Down
Binary file modified assets/benchmarks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 10 additions & 8 deletions docs/Benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

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

| `bar` | `Scribe::poke()` | `ScribeOptimistic::opPoke()` |
|-------|--------------------|------------------------------|
| 5 | 79,428 | 66,462 |
| 10 | 106,862 | 66,534 |
| 15 | 131,834 | 66,603 |
| 20 | 158,263 | 66,663 |
| 50 | 315,655 | 67,437 |
| 100 | 577,919 | 68,845 |
| `bar` | `Scribe::poke()` | `ScribeOptimistic::opPoke()` |
| ----- | ---------------- | ---------------------------- |
| 5 | 80,280 | 68,815 |
| 10 | 105,070 | 68,887 |
| 15 | 132,414 | 68,944 |
| 20 | 156,983 | 69,004 |
| 50 | 314,455 | 69,791 |
| 100 | 574,227 | 71,186 |
| 200 | 1,096,599 | 73,630 |
| 255 | 1,382,810 | 74,735 |

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

Expand Down
55 changes: 6 additions & 49 deletions docs/Invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,67 +68,24 @@ This document specifies invariants of the Scribe and ScribeOptimistic oracle con

## `{Scribe, ScribeOptimistic}::_pubKeys`

* `_pubKeys[0]` is the zero point:
* `_pubKeys`' length is 256:
```
_pubKeys[0].isZeroPoint()
_pubKeys.length == 256
```

* A non-zero public key exists at most once:
* Public keys are stored at the index of their address' first byte:
```
∀x ∊ PublicKeys: x.isZeroPoint() ∨ count(x in _pubKeys) <= 1
```

* Length is strictly monotonically increasing:
```
preTx(_pubKeys.length) != postTx(_pubKeys.length)
→ preTx(_pubKeys.length) < posTx(_pubKeys.length)
```

* Existing public key may only be deleted, never mutated:
```
∀x ∊ uint: x < _pubKeys.length ⋀ preTx(_pubKeys[x]) != postTx(_pubKeys[x])
→ postTx(_pubKeys[x].isZeroPoint())
```

* Newly added public key is non-zero:
```
preTx(_pubKeys.length) != postTx(_pubKeys.length)
→ postTx(!_pubKeys[_pubKeys.length-1].isZeroPoint())
∀id ∊ Uint8: _pubKeys[id].isZeroPoint() ∨ (_pubKeys[id].toAddress() >> 152) == id
```

* Only functions `lift` and `drop` may mutate the array's state:
```
xuint: preTx(_pubKeys[x]) != postTx(_pubKeys[x])
idUint8: preTx(_pubKeys[id]) != postTx(_pubKeys[id])
→ msg.sig ∊ {"lift", "drop"}
```

* Array's state may only be mutated by auth'ed caller:
```
∀x ∊ uint: preTx(_pubKeys[x]) != postTx(_pubKeys[x])
→ authed(msg.sender)
```

## `{Scribe, ScribeOptimistic}::_feeds`

* Image of mapping is `[0, _pubKeys.length)`:
```
∀x ∊ Address: _feeds[x] ∊ [0, _pubKeys.length)
```

* Image of mapping links to feed's public key in `_pubKeys`:
```
∀x ∊ Address: _feeds[x] = y ⋀ y != 0
→ _pubKeys[y].toAddress() == x
```

* Only functions `lift` and `drop` may mutate the mapping's state:
```
∀x ∊ Address: preTx(_feeds[x]) != postTx(_feeds[x])
→ msg.sig ∊ {"lift", "drop"}
```

* Mapping's state may only be mutated by auth'ed caller:
```
∀x ∊ Address: preTx(_feeds[x]) != postTx(_feeds[x])
∀id ∊ Uint8: preTx(_pubKeys[id]) != postTx(_pubKeys[id])
→ authed(msg.sender)
```
4 changes: 2 additions & 2 deletions docs/Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ $ forge script \

Set the following environment variables:

- `FEED_INDEX`: The feed's index
- `FEED_ID`: The feed's id

Run:

Expand All @@ -120,7 +120,7 @@ $ forge script \
--private-key $PRIVATE_KEY \
--broadcast \
--rpc-url $RPC_URL \
--sig $(cast calldata "drop(address,uint)" $SCRIBE $FEED_INDEX) \
--sig $(cast calldata "drop(address,uint)" $SCRIBE $FEED_ID) \
-vvv \
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
```
Expand Down
6 changes: 3 additions & 3 deletions docs/Scribe.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ For more info, see [`LibSecp256k1::addAffinePoint()`](../src/libs/LibSecp256k1.s

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

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`.
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`.

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)`.

For more info, see [`LibSchnorrData.sol`](../src/libs/LibSchnorrData.sol).

Expand All @@ -60,8 +62,6 @@ Feeds _must_ prove the integrity of their public key by proving the ownership of

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).

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).

## Chainlink Compatibility

Scribe aims to be partially Chainlink compatible by implementing the most widely, and not deprecated, used functions of the `IChainlinkAggregatorV3` interface.
Expand Down
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ via_ir = true
extra_output_files = ["metadata", "irOptimized"]

# Testing
fuzz = { runs = 10 }
fuzz = { runs = 50 }
block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT

[invariant]
Expand Down
30 changes: 12 additions & 18 deletions script/Scribe.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ contract ScribeScript is Script {
require(!pubKey.isZeroPoint(), "Public key cannot be zero point");
require(pubKey.isOnCurve(), "Public key must be valid secp256k1 point");
bool isFeed;
(isFeed, /*feedIndex*/ ) = IScribe(self).feeds(pubKey.toAddress());
(isFeed, /*feedId*/ ) = IScribe(self).feeds(pubKey.toAddress());
require(!isFeed, "Public key already lifted");

address recovered =
Expand Down Expand Up @@ -143,8 +143,7 @@ contract ScribeScript is Script {
"Public key must be valid secp256k1 point"
);
bool isFeed;
(isFeed, /*feedIndex*/ ) =
IScribe(self).feeds(pubKeys[i].toAddress());
(isFeed, /*feedId*/ ) = IScribe(self).feeds(pubKeys[i].toAddress());
require(!isFeed, "Public key already lifted");
}

Expand Down Expand Up @@ -180,15 +179,13 @@ contract ScribeScript is Script {
}
}

/// @dev Drops feed with index `feedIndex`.
function drop(address self, uint feedIndex) public {
require(feedIndex != 0, "Feed index cannot be zero");

/// @dev Drops feed with id `feedId`.
function drop(address self, uint8 feedId) public {
vm.startBroadcast();
IScribe(self).drop(feedIndex);
IScribe(self).drop(feedId);
vm.stopBroadcast();

console2.log("Dropped", feedIndex);
console2.log("Dropped", feedId);
}

// -- View Functions
Expand Down Expand Up @@ -314,13 +311,13 @@ contract ScribeScript is Script {
/// script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
/// ```
function deactivate(address self) public {
// Get current feeds' indexes.
uint[] memory feedIndexes;
( /*feeds*/ , feedIndexes) = IScribe(self).feeds();
// Get lifted feed ids.
uint8[] memory feedIds;
( /*feeds*/ , feedIds) = IScribe(self).feeds();

// Drop all feeds.
vm.startBroadcast();
IScribe(self).drop(feedIndexes);
IScribe(self).drop(feedIds);
vm.stopBroadcast();

// Create new random private key.
Expand All @@ -336,12 +333,9 @@ contract ScribeScript is Script {

// Lift feed.
vm.startBroadcast();
uint feedIndex = IScribe(self).lift(feed.pubKey, ecdsaData);
IScribe(self).lift(feed.pubKey, ecdsaData);
vm.stopBroadcast();

// Set feed's assigned feedIndex.
feed.index = uint8(feedIndex);

// Set bar to 1.
vm.startBroadcast();
IScribe(self).setBar(uint8(1));
Expand All @@ -365,7 +359,7 @@ contract ScribeScript is Script {

// Drop feed again.
vm.startBroadcast();
IScribe(self).drop(feed.index);
IScribe(self).drop(feed.id);
vm.stopBroadcast();

// Set bar to type(uint8).max.
Expand Down
Loading

0 comments on commit 6d0e56d

Please sign in to comment.