-
Notifications
You must be signed in to change notification settings - Fork 47
feat(node): support configurable staking collateral token #1130
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
Conversation
Co-authored-by: raulk <raul@protocol.ai>
Co-authored-by: raulk <raul@protocol.ai>
…/ipc into validator-gating
| LibSubnetActor.enforceCollateralValidation(); | ||
| } | ||
| if (msg.value == 0) { | ||
| if (amount == 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.
if the supplySource is native user is still obligated to send the msg.value number in the amount? is there a piece of logic somewhere that auto populates this field is the supplysource is native?
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.
yep, have to do that. This just standardises the interface. So maybe one day nft can be used as well.
| @@ -83,12 +88,12 @@ library SupplySourceHelper { | |||
| /// triggered from the execution. | |||
| /// This function the `safeTransfer` function used before. | |||
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.
Could you clarify this sentence?
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.
nice catch
| return; | ||
| s.collateralSource.transferFunds(payable(msg.sender), amount); | ||
| } else { | ||
| LibStaking.withdraw(msg.sender, amount); |
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 else statement should also contain s.collateralSource.transferFunds(payable(msg.sender), amount); no? I ran a test that unstakes when the subnet is bootstrapped and the validator didnt get their erc20 back
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.
so after the subnet is bootstrapped, that means any validator stake withdraw should not have occurred immediately because of safety issues. It has to go through the checkpointing and confirmed by the current commitee.
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.
@JulissaDantes check this out:
ipc/contracts/contracts/lib/LibStaking.sol
Line 667 in 6d2cc3a
| self.validators.confirmWithdraw(validator, amount); |
It's buffered in a release queue.
See tests:
| saDiamond.rewarder().claim(); |
To withdraw stakes, a lot of checks have to go through.
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.
oh, I think I'm little lost here,
- what is the
current commitee.? - does that mean there is somewhere else in the code where it gets check if any tokens are owed to a validator?
- or does that mean that at the checkpoint library this is handled where they check unstake request and perform the token transfers?
And Im not clear here, my understanding is that a bootstrapped subnet is an active subnet that has the minimum required collateral and validators amount to run. So I still think that there is a path for a validator to just want to unstake certain amount of their stake after a while, where there are several checkpoints being made, and they just wish to reduce their staked amount, although if this is handled elsewhere I understand why its not the case, depending on the answers to the previous questions I guess.
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.
Current committee refers to the current set of validators.
The main issue is not about token owed by the validator, this can be checked pretty easily. It's just that validator withdraw stake needs to be handled more carefully. I could be a malicious validator and I proposed a double spend or some malicious act. Then I immediately withdraw my collateral. If the withdraw go through immediately, then no punishment can be done. So this is just part of the protection mechanism. The current validators must "ack" this change through checkpointing. At the same time, the release queue holds the funds for a "challenge" period in case someone reports with misbehaviour. Keep things accountable.
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.
AHH, thank you, Ive forgotten about the locking period, of course!
karlem
left a comment
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.
In general it looks good.
I just think that we do not have any tests with the collateral supply ERC20 source right?
| } | ||
| } | ||
|
|
||
| pub async fn handle_txn_token<B, D, M>( |
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 think I am missing the point of this function. Could you please explain?
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.
because different combination of collateral source and supply source will have different txn value, if that's the question?
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.
Alright I get it now. It is a bit unclear at first sight. Maybe comment would do.
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.
comment added!
|
@karlem what tests are you referring to? There are plenty of unit and integration tests for the contracts. |
Co-authored-by: Karel Moravec <moravec.wdd@gmail.com>
…ard/ipc into collateral-sourcing
@cryptoAtwill but they all use the native collateral source not the ERC20 right? Correct me if I'm wrong. |
|
@karlem not really, there are permutation and combination, say:
|
raulk
left a comment
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.
Will submit a PR to:
- Enclose the allowance-related complexity inside the
AssetHelper. - Remove the unnecessary error types.
- Use
SafeERC20#safeIncreaseAllowance()instead of implementing this logic ourselves.
This approval is conditional to reviewing and merging the upcoming PR.
| if (genesisCircSupply > 0) { | ||
| SubnetActorGetterFacet(msg.sender).supplySource().lock(genesisCircSupply); | ||
| } | ||
| if (collateral > 0) { | ||
| SubnetActorGetterFacet(msg.sender).collateralSource().lock(collateral); |
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 would support dropping this feature entirely: #1142
| error NotEnoughGenesisValidators(); | ||
| error ValidatorPowerChangeDenied(); | ||
| error IncreaseAllowanceFailed(); | ||
| error Unreachable(); |
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 error type is not needed as it doesn't provide more information than a revert with a string message.
| // now we are handling native token | ||
| if (msg.value < value) { | ||
| revert NoBalanceIncrease(); | ||
| } |
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.
- There is no "handling" here, just an extra assertion.
- There is no "native token", but "native coins".
- This can be an
else if. - Why is this not an equals check?
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.
if msg.value == value, we should pass the check. If msg.value > value, someone wants to do charity, maybe can just take it... or maybe not, haha. I will change this 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.
Best not to. It's stronger to have an invariant that gateway balance across all tokens and the native coin is zero for initialized subnets, than to have one where it can be non zero.
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.
Oh, I realized we have to use msg.value > value. The reason is caller might have msg.value more than just locked value, for other purposes as well. In that way, we can only enforce msg.value > value.
| IGateway(gateway).addStake(amount); | ||
| } | ||
| } else { | ||
| revert Unreachable(); |
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.
Just an unqualified revert with an error string is fine.
| if (s.collateralSource.isNative()) { | ||
| IGateway(gateway).addStake{value: amount}(amount); | ||
| } else { | ||
| s.collateralSource.increaseAllowance(gateway, amount); | ||
| IGateway(gateway).addStake(amount); | ||
| } |
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 rather leaky. The AssetHelper is designed to hide the different asset kinds. I would refactor this as a new function AssetHelper#makeSpendable that makes an amount spendable by some party.
Note: even if you want to keep this conditional here, you don't need an isNative helper, you could just access the kind field of the Asset.
| if (self.kind == AssetKind.ERC20) { | ||
| IERC20 token = IERC20(self.tokenAddress); | ||
| uint256 allowance = token.allowance(address(this), spender); | ||
| if (!token.approve(spender, allowance + amount)) { | ||
| revert IncreaseAllowanceFailed(); | ||
| } | ||
| } |
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.
- Just use
SafeERC20#safeIncreaseAllowance()? - Singleton ifs like this are cleaner if written as a negative if with an early return.
| emit SubnetBootstrapped(s.genesisValidators); | ||
|
|
||
| // register adding the genesis circulating supply (if it exists) | ||
| IGateway(s.ipcGatewayAddr).register{value: totalCollateral + s.genesisCircSupply}(s.genesisCircSupply); |
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 like we had a bug here, where we sent native coins representing the genesis circulating supply, even if the supply source was ERC20. Nice catch!
| if (!supplySourceNative) { | ||
| s.supplySource.increaseAllowance(s.ipcGatewayAddr, genesisCircSupply); | ||
| } else { | ||
| msgValue += genesisCircSupply; | ||
| } | ||
|
|
||
| if (!collateralSourceNative) { | ||
| s.collateralSource.increaseAllowance(s.ipcGatewayAddr, collateral); | ||
| } else { | ||
| msgValue += collateral; | ||
| } |
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 leaky as mentioned elsewhere. I'm going to take a stab to contain this complexity inside the AssetHelper.
raulk
left a comment
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.
Will submit a PR to:
- Enclose the allowance-related complexity inside the
AssetHelper. - Remove the unnecessary error types.
- Use
SafeERC20#safeIncreaseAllowance()instead of implementing this logic ourselves.
This approval is conditional to reviewing and merging the upcoming PR.
|
@cryptoAtwill all blocking review comments addressed in: #1147 Please review and merge if you agree, then feel free to merge this one. @karlem when that's done, could you please make a release? |
03994f1 to
71cae9f
Compare
The general policy should be that if we're not expecting users to catch these errors to handle them specifically, nor are we seeking to optimise gas and/or return size (at the expense of debuggability), we shouldn't be creating dedicated error types, let alone extremely narrow and tiny ones.
This PR makes an effort to support configuration different token for collateral. Previously only native token is allowed, now ERC20 can be used, see #1024.
This PR introduces the following changes:
SupplySourcetoGenericToken,SupplySourceHelpertoGenericTokenHelper.collateralSourceparameter, this is where the token for collateral can be configured.preFund,preReleaseand everything related to token to useGenericToken, removed all hardcoding of native tokens, includingLibStakingandReleaseQueue.List of todos:
ipc-cliupdateTo test in calibration network, try the following
ipcconfiguration:Or you can deploy your own ipc-stack.
Deploy your own ERC20 token on calibration, obtain the address. Then build the
ipc-cli, run: