-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic signature sampling #956
Conversation
@doubledup I remember the initial suggestion from Web3 team is There is a draft PR #879 for that though I closed later. I'm not sure but my intuition will the current implementation cost much more gas(considering an invariant length array |
@ron after a call with Web3 (I can't remember if you were on the call), we decided to go with this solution, where the number of checked signatures dynamically increases. The benefit of this approach is that relayers don't need to put up deposits. |
@yrong For gas costs, we can potentially reduce them by packing multiple counts into an array like The log arithmetic is already fairly performant as we're rounding to an integer rather than calculating a fraction: https://github.com/Snowfork/snowbridge/pull/956/files#diff-473fead6cd500a4a05f06d0cea4d9ef166c9bdbe2a2e1a06a33d2e1aedcc428eR427-R433 |
Yes, I prefer to extend the key to include session id and would suspect we can merge the two storages Moreover, I would doubt even necessary for the auxiliary storage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest a different data structure for the usage counters, one that has efficient space complexity and can be deleted in constant time.
Since we know we need to keep counters for both the current and next session, we can update the ValidatorSet
struct with a counters
field as follows:
struct ValidatorSet {
uint128 id;
uint128 length;
bytes32 root;
uint256[] counters;
}
This counters
field is a packed array of uint16
usage counters for each validator. Updating the packed array can be performed using bitfield math. Deleting it is a simple delete currentValidatorSet.counters
1-liner.
Oops I see that you suggested mostly the same thing in the PR description 👍 However, if we make sure that the packed array is scoped to a single session, then it is possible to track validators by their leaf index, as they will remain static during a session. As I said previously we need to have a packed array for both the current and next session. On session handover, we also need to copy over the counters from the next session to the current session, i.e:
and then we reset the counters for the next session to zero and to match the size of the new next validator set. I did some gas tests with this approach and it doesn't look too bad honestly:
Test Script (assuming 1000 validators): // SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import {Agent} from "../src/Agent.sol";
contract ArrayThing {
uint256 constant NUM_VALIDATORS = 1024;
uint256 constant ELEMENT_SIZE = 2;
uint256 constant CONTAINER_SIZE = 32;
uint256 constant NUM_CONTAINERS = (NUM_VALIDATORS * ELEMENT_SIZE) / CONTAINER_SIZE;
uint256[] public countersA;
uint256[] public countersB;
function initCountersA() public {
countersA = new uint256[](NUM_CONTAINERS);
}
function initCountersB() public {
countersB = new uint256[](NUM_CONTAINERS);
}
function deleteCountersA() public {
delete countersA;
}
function copyCounters() public {
for (uint i = 0; i < countersB.length; i++) {
countersA[i] = countersB[i];
}
}
}
contract PackedArrayGasTest is Test {
ArrayThing public thing;
function setUp() public {
thing = new ArrayThing();
}
function testCounters() public {
thing.initCountersA();
thing.initCountersB();
thing.copyCounters();
thing.deleteCountersA();
}
} |
@doubledup I've updated Readme with required steps to regenerate beefy test fixtures. Hope that's helpful. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #956 +/- ##
==========================================
- Coverage 77.81% 76.73% -1.09%
==========================================
Files 46 11 -35
Lines 1893 361 -1532
Branches 76 71 -5
==========================================
- Hits 1473 277 -1196
+ Misses 401 68 -333
+ Partials 19 16 -3
Flags with carried forward coverage won't be shown. Click here to find out more.
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty cool! ✨
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far. Left some comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, added some minor comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more thing @alistair-singh, we also need to update the documentation at https://docs.snowbridge.network/architecture/verification/polkadot to describe the design of the dynamic signature sampling algorithm.
Can either edit the markdown in this PR, or via the online gitbook editor. Either way let's make sure this task is tracked.
Another change we may need is for relay logic to not always pick from the first validator snowbridge/relayer/relays/beefy/ethereum-writer.go Lines 156 to 158 in ba4e801
Instead, it makes more sense to pick from the validator set randomly. |
Addressed in 2494471 |
2494471
to
0bcd1bd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
0e7716d
to
191502e
Compare
c7f718f
to
53a6a33
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets merge it!
This counts the number of times each validator's signature is used in the current session. It then uses the recommendation from W3F to adjust the required number of signatures for a commitment to be accepted.
@vgeddes For now, I've stored these counts by validator address only (not session id) as I'm not yet sure of a use for the counts from past sessions. If we need them, I'll extend the key to include session id.
If we can store them by validator index, there's a potential optimisation where we can pack multiple counts into a single word, eg. keep a
uint256[]
where each count is auint16
packed into an entry in that array - similar to how we work with bitfields. We'll have at most2**16-1 = 65 535
uses of each validator's signature insubmitInitial
calls. For reference, with 6s block time & 24h sessions, we'll see24*60*60/6 = 14 400
blocks each session.Resolves: SNO-671, SNO-672, SNO-673, SNO-674, SNO-634