-
Notifications
You must be signed in to change notification settings - Fork 42
Conversation
Deep inside ECDSA keep factory implementation, `BondedSortitionPool` was updating the minimum bondable value for each new group selection. The idea was to automatically remove from the pool operators that are no longer able to satisfy application needs about the minimum available bondable value and prevent operators with too low value griefing signer selection. We now have two minimum bond values: one defining the minimum unbonded value the operator needs to have so that it can join and stay in the pool, and another defining the required bond value for the current selection. If the given operator has enough unbonded value to stay in the pool but it does not have enough unbonded value for the current group selection, it is skipped. In ECDSA keep factory, sortition pool is created with a minimum bond of 20 ETH to avoid small operators joining and griefing future selections before the minimum bond is set to the right value by the application. Anyone can create a sortition pool for an application with the default minimum bond value but the application can change this value later, at any point. 20 ETH per operator should be more than enough to cover 1 BTC deposit in 3-operators group. The default is fine for most cases <now> but the application is expected to refresh the minimum bondable value, depending on the current situation and needs. For tBTC, the minimum bond value proposed here is be the minimum lot size expressed in wei given the current BTC price with 150% collateralization. For such a value, operator should be able to participate in at least 3 keeps, or - in extreme circumstances - hold one keep just by themselves. Here we add a function allowing to refresh the minimum bondable value given the current BTC price. The function can be called by anyone at any moment. It is recommended to call this function on tBTC initialization and after the minimum lot size update.
Is this something we would also expect |
Minimum unbonded value should be updated every time minimum lot size changes. To automate this process and do not require another transaction to be executed, we automatically update the minimum unbonded value when finalizing lot size update.
The first lot size does not have to be the smaller one. When updating the minimum unbonded value, we need to find the smallest lot size from all that are currently available. Also, improved the test coverage.
Yes, I think it's a good idea. I have not originally added it because I thought not every lot sizes update needs to modify the minimum lot. On the other hand, if we do the refresh and the minimum lot size is updated, we'll save gas on not submitting another transaction. Also, this could be a good opportunity to update the minimum unbonded value given the most recent ETBBTC price. It's done - please see the last two commits. |
|
||
/// @notice Returns the minimum lot size in satoshi. | ||
function getMinimumLotSizeSatoshi() public view returns (uint256) { | ||
uint256 minimum = lotSizesSatoshis[0]; |
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.
It feels like we'd do better to ensure the lot size array is in order when we set it, rather than trying to sort it later?
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.
Yeah, I wasn't sure if I can do that. The mandatory lot size looks to me like something we might want to pass as the very first value just for convenience but given that it's not a function we call every day, expecting lot sizes to come sorted makes sense. If you agree with this change, I am happy to update the code 🙌
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.
Done in 5ec1459.
uint256 bondRequirementWei = _fetchBitcoinPrice().mul( | ||
bondRequirementSatoshis | ||
); | ||
keepFactorySelection.setMinimumBondableValue(bondRequirementWei); |
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.
Since we're setting this on the sortition pool, shouldn't it be a per-operator value? That is, shouldn't it be 50% rather than 150%?
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.
The reasoning for using 150% was to set up the bar on a low level but not on the lowest level: we make sure every operator we add to the pool can create a keep only with its members if needed. Given that we talk about the minimum lot size, it sounds to me like a sane expectation especially that we need to account for ETHBTC price fluctuations. Technically, we can lower it to 50% but I am not sure if we want to. -_-
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.
The other danger I'm seeing here is that we've really blurred the lines of responsibility on who decides what about bonds:
- The threshold/member count for keeps is static, but it's managed by
DepositFactory
. But it's ultimately passed throughDeposit
toTBTCSystem
and then finally to the ECDSA keep. - The math on total bond amount is done in
DepositFunding.sol
, but relies entirely on information thatTBTCSystem
has at its disposal. - The math that turns the bond amount and n/m count into a per-node bond is done in
BondedECDSAKeep
.
I would suggest that we:
- Move
n
/m
values intoTBTCSystem
's constructor and drop them fromDepositFactory
. - Stop passing these through
DepositFactory
andDeposit
. - Have
requestNewKeep
pass those along toBondedECDSAKeepFactory
. - Calculate the initial bond requirement that is passed to
BondedECDSAKeepFactory
entirely inTBTCSystem
, rather than doing it inDeposit
and passing that info along. - Reuse the
n
/m
info and initial bond requirement calculation function in the implementation ofrefreshMinimumBondableValue
.
EDIT: one more bullet. In a perfect world, we would pass the initial required bond + the n
/m
info to BondedECDSAKeepFactory
and let it decide what the implications are on minimum bondable value.
The whole point here is that the minimum bondable value is as much as possible an implementation detail of BondedECDSAKeepFactory
, the Keep setup is as much as possible an implementation detail of TBTCSystem
, and the deposit and deposit factory are entirely concerned with the deposit's overall collateralization info.
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.
EDIT: one more bullet. In a perfect world, we would pass the initial required bond + the n/m info to BondedECDSAKeepFactory and let it decide what the implications are on minimum bondable value.
The whole point here is that the minimum bondable value is as much as possible an implementation detail of BondedECDSAKeepFactory, the Keep setup is as much as possible an implementation detail of TBTCSystem, and the deposit and deposit factory are entirely concerned with the deposit's overall collateralization info.
I am no sure if I understand correctly what you say but I think this is ~ what's happening now. TBTCSystem
is calculating the bond required for the whole keep and BondedECDSAKeepFactory
splits this bond between members.
tBTC needs to be aware of the minimum unbonded value because the refresh needs to happen outside of the function opening a keep. In the previous approach we were updating the minimum unbonded value based on the bonding parameter passed to BondedECDSAKeepFactory
when opening a keep but it doesn't work well.
I think there is something I am missing here... 🤔
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.
Taking this discussion offline briefly, will summarize the outcome.
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.
We decided to allow operators in a sortition pool who can support only one keep (one member in one keep to be precise). For this reason, we decided for setMinimumBondValue(minimumBondValue, keepThreshold, keepSize)
, in the BondedECDSAKeepFactory
that will basically take that and divide minimumBondValue
by keepSize
to determine the correct value to set on the sortition pool. This lets the factory be the center of determining how it spreads bonds across operators (which it already is when opening a new keep), while passing along the full info needed for the final setting in the sortition pool.
keep-ecdsa
change is here: keep-network/keep-ecdsa#510
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.
keep-network/keep-ecdsa#510 has been merged and I updated the code in this PR in 56ae488
By expecting the lot size array to be sorted we can simplify the code by not requiring to iterate over the array to determine minimum and maximum lot size.
The new version of keep-ecdsa allows refreshing the minimum unbonded value expected from operator per application.
This way, we do not need to pass those values around from DepositFactory to DepositFunding and Deposit, and have them eventually passed to TBTCSystem. Group size and threshold are global, system parameters so it does make sense to keep them on a global level. Also, this change will help us clean up bond calculation code (more to come in the next commits).
Instead of having the same code in two places - TBTCSystem and DepositFunding, we now calculate the required bond only in TBTCSystem. Instead of passing the bond value to requestNewKeep function, DepositFunding passes the lot size and TBTCSystem does all the calculations.
requestNewKeep reverts the transaction if lot size it not supported and although those tests are passing without a valid lot size - they revert TX before lot size is validated - it's better to use a valid lot size for clarity.
I marked it as a ready for review, it's ready for another chance @Shadowfiend. |
uint256 _m, | ||
uint256 _n, | ||
uint256 _bond, | ||
uint64 _lotSizeSatoshis, |
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.
Well now I feel weird about this taking the lot size 🤔
I think it's a marked improvement though.
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.
Hmm, it feels quite natural to me but I don't have the full context. Deposit
is initialized with the lot size and TBTCSystem
does want to work with supported lot sizes. The moment we open a keep is where lot sizes ends and wei bonds come into play.
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.
It feels like by the time we reach the concept of a keep, we should be requesting the keep with a particular bond for the keep, rather than passing the lot size. But I like that the price feed and bond calculation is here now. So I'm still a little ways away from figuring out what's bothering me about it :) Regardless, I don't think it's worth doing anything more here for now.
Having it public leaks the pricing info.
@@ -638,6 +625,19 @@ contract TBTCSystem is Ownable, ITBTCSystem, DepositLog { | |||
return false; | |||
} | |||
|
|||
/// @notice Calculates bond requirement in wei for the given lot size in | |||
/// satoshis given the current BTCETH price. |
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.
Super tiny nit that I'm not going to block merge on, but I would say “based on the current ETHBTC price”, mostly because we're using “given” earlier to refer specifically to something that is passed in.
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.
Wait wait, I can edit it 😆
We now say “... based on the current ETHBTC price”, because we're using “given” earlier to refer specifically to something that is passed in.
@@ -626,7 +626,7 @@ contract TBTCSystem is Ownable, ITBTCSystem, DepositLog { | |||
} | |||
|
|||
/// @notice Calculates bond requirement in wei for the given lot size in | |||
/// satoshis given the current BTCETH price. | |||
/// satoshis based on the current ETHBTC price. |
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.
🐗
We now leave the decision to BondedECDSAKeepFactory how to distribute the bond expectation between members.
Ready for another chance @Shadowfiend 🙌 |
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.
Changes look good here.
Refs keep-network/keep-ecdsa#506
Depends on keep-network/keep-ecdsa#508Depends on keep-network/sortition-pools#84Deep inside ECDSA keep factory implementation,
BondedSortitionPool
was updating the minimum bondable value for each new group selection. The idea was to automatically remove from the pool operators that are no longer able to satisfy application needs about the minimum available bondable value and prevent operators with too low value griefing signer selection.We now have two minimum bond values: one defining the minimum unbonded value the operator needs to have so that it can join and stay in the pool, and another defining the required bond value for the current selection. If the given operator has enough unbonded value to stay in the pool but it does not have enough unbonded value for the current group selection, it is skipped.
In ECDSA keep factory, sortition pool is created with a minimum bond of 20 ETH to avoid small operators joining and griefing future selections before the minimum bond is set to the right value by the application. Anyone can create a sortition pool for an application with the default minimum bond value but the application can change this value later, at any point. 20 ETH per operator should be more than enough to cover 1 BTC deposit in 3-operators group.
The default is fine for most cases but the application is expected to refresh the minimum bondable value, depending on the current situation and needs.
For tBTC, the minimum bond value proposed here is be the minimum lot size expressed in wei given the current BTC price with 150% collateralization. For such a value, operator should be able to participate in at least 3 keeps.
Here we add a function allowing to refresh the minimum bondable value given the current BTC price. The function can be called by anyone at any moment. It is recommended to call this function on tBTC initialization and after the minimum lot size update.