Skip to content

Conversation

@cryptoAtwill
Copy link
Contributor

@cryptoAtwill cryptoAtwill commented Sep 6, 2024

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:

  • Rename SupplySource to GenericToken, SupplySourceHelper to GenericTokenHelper.
  • Introduced collateralSource parameter, this is where the token for collateral can be configured.
  • Aligned preFund, preRelease and everything related to token to use GenericToken, removed all hardcoding of native tokens, including LibStaking and ReleaseQueue.

List of todos:

  • Feature implementation
  • Unit tests
  • Integration tests
  • Deployment script udpate
  • ipc-cli update
  • Calirabtion network tests

To test in calibration network, try the following ipc configuration:

gateway_addr = "0xA4dDECD395590765C8623F9B9c49160080046d3c"
registry_addr = "0x06c89f2Aa8b580a66F273053B23d0622e6f87a14"

Or you can deploy your own ipc-stack.

Deploy your own ERC20 token on calibration, obtain the address. Then build the ipc-cli, run:

# create the subnet with collateral source
./target/release/ipc-cli subnet create --parent /r314159 --min-validator-stake 0.01 --min-cross-msg-fee 0 --min-validators 0 --bottomup-check-period 50 --permission-mode collateral --supply-source-kind native --collateral-source-kind erc20 --collateral-source-address <YOUR ERC20 ADDRESS>

# approve ERC20 token to spend 1 fil by the SubnetActor

# perform collateral related operations
./target/release/ipc-cli subnet join --subnet /r314159/${SUBNET} --collateral 0.02
./target/release/ipc-cli subnet stake --subnet /r314159/${SUBNET} --collateral 0.02

@cryptoAtwill cryptoAtwill requested a review from a team as a code owner September 6, 2024 07:00
@raulk raulk linked an issue Sep 17, 2024 that may be closed by this pull request
Base automatically changed from validator-gating to main September 18, 2024 20:33
LibSubnetActor.enforceCollateralValidation();
}
if (msg.value == 0) {
if (amount == 0) {
Copy link
Contributor

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?

Copy link
Contributor Author

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.
Copy link
Contributor

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?

Copy link
Contributor Author

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);
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JulissaDantes check this out:

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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!

Copy link
Contributor

@karlem karlem left a 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>(
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment added!

@cryptoAtwill
Copy link
Contributor Author

@karlem what tests are you referring to? There are plenty of unit and integration tests for the contracts.

@karlem
Copy link
Contributor

karlem commented Sep 23, 2024

@karlem what tests are you referring to? There are plenty of unit and integration tests for the contracts.

@cryptoAtwill but they all use the native collateral source not the ERC20 right? Correct me if I'm wrong.

@cryptoAtwill
Copy link
Contributor Author

@karlem not really, there are permutation and combination, say:

function testSubnetActorDiamond_CollateralERC20_SupplyERC20_SameToken_RegisteredInGateway() public {

Copy link
Contributor

@raulk raulk left a 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:

  1. Enclose the allowance-related complexity inside the AssetHelper.
  2. Remove the unnecessary error types.
  3. Use SafeERC20#safeIncreaseAllowance() instead of implementing this logic ourselves.

This approval is conditional to reviewing and merging the upcoming PR.

Comment on lines +40 to +44
if (genesisCircSupply > 0) {
SubnetActorGetterFacet(msg.sender).supplySource().lock(genesisCircSupply);
}
if (collateral > 0) {
SubnetActorGetterFacet(msg.sender).collateralSource().lock(collateral);
Copy link
Contributor

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();
Copy link
Contributor

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.

Comment on lines 64 to 67
// now we are handling native token
if (msg.value < value) {
revert NoBalanceIncrease();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. There is no "handling" here, just an extra assertion.
  2. There is no "native token", but "native coins".
  3. This can be an else if.
  4. Why is this not an equals check?

Copy link
Contributor Author

@cryptoAtwill cryptoAtwill Sep 25, 2024

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

Copy link
Contributor

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.

Copy link
Contributor Author

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();
Copy link
Contributor

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.

Comment on lines 587 to 592
if (s.collateralSource.isNative()) {
IGateway(gateway).addStake{value: amount}(amount);
} else {
s.collateralSource.increaseAllowance(gateway, amount);
IGateway(gateway).addStake(amount);
}
Copy link
Contributor

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.

Comment on lines 217 to 223
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();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Just use SafeERC20#safeIncreaseAllowance()?
  2. 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);
Copy link
Contributor

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!

Comment on lines 173 to 183
if (!supplySourceNative) {
s.supplySource.increaseAllowance(s.ipcGatewayAddr, genesisCircSupply);
} else {
msgValue += genesisCircSupply;
}

if (!collateralSourceNative) {
s.collateralSource.increaseAllowance(s.ipcGatewayAddr, collateral);
} else {
msgValue += collateral;
}
Copy link
Contributor

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.

Copy link
Contributor

@raulk raulk left a 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:

  1. Enclose the allowance-related complexity inside the AssetHelper.
  2. Remove the unnecessary error types.
  3. Use SafeERC20#safeIncreaseAllowance() instead of implementing this logic ourselves.

This approval is conditional to reviewing and merging the upcoming PR.

@raulk
Copy link
Contributor

raulk commented Sep 24, 2024

@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?

@raulk raulk force-pushed the collateral-sourcing branch from 03994f1 to 71cae9f Compare September 25, 2024 09:12
raulk and others added 3 commits September 25, 2024 11:12
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.
@cryptoAtwill cryptoAtwill merged commit b0b1dd0 into main Sep 25, 2024
@cryptoAtwill cryptoAtwill deleted the collateral-sourcing branch September 25, 2024 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Archived in project

Development

Successfully merging this pull request may close these issues.

[Staking] Token-bound validator collateral

4 participants