Skip to content

Commit

Permalink
290 update the node based on the core contracts changes (#81)
Browse files Browse the repository at this point in the history
* adapt the node to the updated core-contracts

fetch the latest changes of the core-contracts, compile and run the tests;
add the new functions and events that we will for the bindings;
update the bindings and the initializers for the core contracts;
update the commission command to accept new flags and to execute a set, apply, and claim commission;
make the commission flag common, then add it in the register-validator command, and update the contract function parameters;
optimize the functions encoding to directly use the contractsapi structs in order to easily set the input parameters;
delete unused parameters;
update the docs;

* update git submodules commit

* delete redundant comment

* delete commented variable

* update

allow users to set 0 commission;
update output;
make the commission as a required flag for the register-validator command;
  • Loading branch information
Vitomir2 authored Nov 25, 2024
1 parent ac19313 commit f1cde45
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 149 deletions.
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,21 @@ After your node is operational and fully synced, you're ready to become a valida

### Register account as validator and stake

Hydra's validator set is unique as it offers a permissionless opportunity on a first come/first serve. It supports up to 150 validators and uses exponentiating formula to ensure consolidation is countered for a maximum Nakamoto Coefficient. The requirements to become a validator: a) to have a minimum of 15,000 HYDRA and b) there to be vacant slots in the validator sets. Inactive validators are going to be ejected after 72 hours in order to ensure fair environment and highest level of network security.
Hydra's validator set is unique as it offers a permissionless opportunity on a first come/first serve. It supports up to 150 validators and uses exponentiating formula to ensure consolidation is countered for a maximum Nakamoto Coefficient. The requirements to become a validator: a) to have a minimum of 15,000 HYDRA and b) there to be vacant slots in the validator sets. Inactive validators are going to be ejected after approximately 72 hours in order to ensure fair environment and highest level of network security.

After ensuring you have a minimum of 15,000 HYDRA in your validator wallet, you can execute the following command.

```
hydra hydragon register-validator --data-dir ./node-secrets --stake 15000000000000000000000 --jsonrpc http://localhost:8545
```

The above command both register the validator and stakes the specified amount.
The command above both registers the validator and stakes the specified amount. To specify an initial commission rate that will be deducted from future delegators’ rewards, use the `--commission` flag with the desired value. The commission rate must be between 0 and 100. The command will look as follows:

Use the following command in case you want to execute the stake operation only:
```
hydra hydragon register-validator --data-dir ./node-secrets --stake 15000000000000000000000 --commission 10 --jsonrpc http://localhost:8545
```

Use the following command if you want to perform a stake operation only or increase your existing stake:

```
hydra hydragon stake --data-dir ./node-secrets --self true --amount 15000000000000000000000 --jsonrpc http://localhost:8545
Expand All @@ -181,23 +185,34 @@ hydra hydragon stake --data-dir ./node-secrets --self true --amount 150000000000

Congratulations! You have successfully become a validator on the Hydra Chain. For further information and support, join our Telegram group and engage with the community.

### Set or update the commission for the delegators
### Update the commission for the delegators

After becoming a validator, you can set the desired commission that will be deducted from the delegators' rewards.
Additionally, you can update the commission if you need to.
After becoming a validator, you can still update the commission rate if needed. The process begins with executing a command to set the new commission as pending. This is followed by a 15-day waiting period, after which you can apply the updated commission. This waiting period is designed to prevent validators from acting dishonestly by executing commission changes shortly before delegators claim their rewards. You can initialize the new commission using the following command:

```
hydra hydragon commission --data-dir ./node-secrets --commission 10 --jsonrpc http://localhost:8545
hydra hydragon commission --data-dir ./node-secrets --commission 15 --jsonrpc http://localhost:8545
```

### Claiming the generated rewards from validating
After the waiting period ends, execute the following command to apply the commission rate set in the previous step:

```
hydra hydragon commission --data-dir ./node-secrets --apply true --jsonrpc http://localhost:8545
```

### Claiming the generated rewards from validating and commission rewards from delegators

Once a validator starts validating blocks, rewards are generated at the end of each epoch based on the validator’s activity level. Validators can claim rewards at any time, provided rewards have been generated. If no rewards are available, the transaction will fail. Use the following command:

```
hydra hydragon claim-rewards --data-dir ./node-secrets --jsonrpc http://localhost:8545
```

Additionally, the rewards that are generated from the claimings of the delegators, you can claim it by executing the command below:

```
hydra hydragon commission --data-dir ./node-secrets --claim true --jsonrpc http://localhost:8545
```

### Ban Validator
To reduce the risk of stalling caused by validators experiencing temporary issues or acting maliciously, we’ve implemented an ejection and ban mechanism. Anyone who recongizes a suspicious activity, and the rules are met, can execute the ban process. Below is an outline of how the system works (specific conditions are detailed in our [genesis contracts](https://github.com/Hydra-Chain/hydragon-core-contracts)):
1. **Initial Ejection**: If your validator stops proposing or participating in consensus whether due to hardware failure, software issues, or malicious intent—the ban procedure will be initiated. The validator will be ejected, allowing time for recovery. If no action is taken, a ban may follow. The threshold to trigger this process is initially set at 18,000 blocks (~2 hours), depending on block creation speed.
Expand All @@ -220,7 +235,7 @@ To reduce the risk of stalling caused by validators experiencing temporary issue
If your validator has been banned, you can still withdraw the remaining funds after the penalty and burned rewards have been deducted. Use the following command:

```
hydra hydragon withdraw --banned --data-dir ./node-secrets --jsonrpc http://localhost:8545
hydra hydragon withdraw --data-dir ./node-secrets --banned --jsonrpc http://localhost:8545
```

***Note:*** If your machine is no longer running, you can use [our RPC](#adding-hydragon-network-to-metamask) as the value for the jsonrpc flag.
Expand Down
2 changes: 2 additions & 0 deletions command/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const (
IBFTValidatorTypeFlag = "ibft-validator-type"

SecretsConfigFlag = "secrets-config"

CommissionFlag = "commission"
)

const (
Expand Down
129 changes: 109 additions & 20 deletions command/sidechain/commission/commission.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import (
var (
params setCommissionParams

delegationManager = contracts.HydraDelegationContract
setCommissionFn = contractsapi.HydraDelegation.Abi.Methods["setCommission"]
commissionUpdatedEventABI = contractsapi.HydraDelegation.Abi.Events["CommissionUpdated"]
delegationManager = contracts.HydraDelegationContract
pendingCommissionAddedEventABI = contractsapi.HydraDelegation.Abi.Events["PendingCommissionAdded"]
commissionUpdatedEventABI = contractsapi.HydraDelegation.Abi.Events["CommissionUpdated"]
commissionClaimedEventABI = contractsapi.HydraDelegation.Abi.Events["CommissionClaimed"]
)

func GetCommand() *cobra.Command {
Expand All @@ -36,7 +37,6 @@ func GetCommand() *cobra.Command {
}

setFlags(setCommissionCmd)
helper.SetRequiredFlags(setCommissionCmd, params.getRequiredFlags())

return setCommissionCmd
}
Expand All @@ -58,9 +58,24 @@ func setFlags(cmd *cobra.Command) {

cmd.Flags().Uint64Var(
&params.commission,
commissionFlag,
command.CommissionFlag,
0,
"mandatory flag that represents the commission percentage of the validator (staker)",
"flag that represents the commission percentage of the validator (staker)",
)

cmd.Flags().BoolVar(
&params.apply,
applyFlag,
false,
"a flag to indicate whether to set or apply the commission. "+
"If the flag is set, it will apply the pending commission, otherwise will set new pending commission.",
)

cmd.Flags().BoolVar(
&params.claim,
claimFlag,
false,
"a flag to indicate whether to claim the commission generated from the delegators.",
)

cmd.Flags().BoolVar(
Expand All @@ -73,6 +88,7 @@ func setFlags(cmd *cobra.Command) {
helper.RegisterJSONRPCFlag(cmd)

cmd.MarkFlagsMutuallyExclusive(polybftsecrets.AccountConfigFlag, polybftsecrets.AccountDirFlag)
cmd.MarkFlagsMutuallyExclusive(command.CommissionFlag, applyFlag, claimFlag)
}

func runPreRun(cmd *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -107,52 +123,88 @@ func runCommand(cmd *cobra.Command, _ []string) error {
return err
}

receipt, err := setCommission(txRelayer, validatorAccount)
var input []byte
if params.apply { //nolint:gocritic
input, err = generateApplyPendingCommissionFn()
} else if params.claim {
input, err = generateClaimCommissionFn(validatorAccount)
} else {
input, err = generateSetPendingCommissionFn()
}

if err != nil {
return err
}

receipt, err := sendCommissionTx(txRelayer, validatorAccount, input)
if err != nil {
return err
}

if receipt.Status != uint64(types.ReceiptSuccess) {
return errors.New("register validator transaction failed")
return errors.New("commission transaction failed")
}

result := &setCommissionResult{}
foundPendingCommissionLog := false
foundCommissionUpdatedLog := false
foundClaimCommissionLog := false

for _, log := range receipt.Logs {
if pendingCommissionAddedEventABI.Match(log) {
event, err := pendingCommissionAddedEventABI.ParseLog(log)
if err != nil {
return err
}

result.staker = event["staker"].(ethgo.Address).String() //nolint:forcetypeassert
result.commission = event["newCommission"].(*big.Int).Uint64() //nolint:forcetypeassert
result.isPending = true
foundPendingCommissionLog = true
}

if commissionUpdatedEventABI.Match(log) {
event, err := commissionUpdatedEventABI.ParseLog(log)
if err != nil {
return err
}

result.staker = event["staker"].(ethgo.Address).String() //nolint:forcetypeassert
result.newCommission = event["newCommission"].(*big.Int).Uint64() //nolint:forcetypeassert
result.staker = event["staker"].(ethgo.Address).String() //nolint:forcetypeassert
result.commission = event["newCommission"].(*big.Int).Uint64() //nolint:forcetypeassert
foundCommissionUpdatedLog = true
}

if commissionClaimedEventABI.Match(log) {
event, err := commissionClaimedEventABI.ParseLog(log)
if err != nil {
return err
}

result.staker = event["to"].(ethgo.Address).String() //nolint:forcetypeassert
result.commission = event["amount"].(*big.Int).Uint64() //nolint:forcetypeassert
result.isClaimed = true
foundClaimCommissionLog = true
}
}

if !foundCommissionUpdatedLog {
if params.apply && !foundCommissionUpdatedLog { //nolint:gocritic
return fmt.Errorf("could not find an appropriate log in the receipt that validates the new commission update")
} else if params.claim && !foundClaimCommissionLog {
return fmt.Errorf("could not find an appropriate log in the receipt that validates the commission claim")
} else if !foundPendingCommissionLog {
return fmt.Errorf("could not find an appropriate log in the receipt that validates the new pending commission")
}

outputter.WriteCommandResult(result)

return nil
}

func setCommission(sender txrelayer.TxRelayer, account *wallet.Account) (*ethgo.Receipt, error) {
encoded, err := setCommissionFn.Encode([]interface{}{
new(big.Int).SetUint64(params.commission),
})
if err != nil {
return nil, fmt.Errorf("encoding set commission function failed: %w", err)
}

func sendCommissionTx(sender txrelayer.TxRelayer, account *wallet.Account, input []byte) (*ethgo.Receipt, error) {
txn := sidechain.CreateTransaction(
account.Ecdsa.Address(),
(*ethgo.Address)(&delegationManager),
encoded,
input,
nil,
)

Expand All @@ -164,3 +216,40 @@ func setCommission(sender txrelayer.TxRelayer, account *wallet.Account) (*ethgo.

return receipt, err
}

func generateSetPendingCommissionFn() ([]byte, error) {
setPendingCommisionFn := &contractsapi.SetPendingCommissionHydraDelegationFn{
NewCommission: new(big.Int).SetUint64(params.commission),
}

encoded, err := setPendingCommisionFn.EncodeAbi()
if err != nil {
return nil, fmt.Errorf("encoding set pending commission function failed: %w", err)
}

return encoded, err
}

func generateApplyPendingCommissionFn() ([]byte, error) {
applyPendingCommisionFn := &contractsapi.ApplyPendingCommissionHydraDelegationFn{}

encoded, err := applyPendingCommisionFn.EncodeAbi()
if err != nil {
return nil, fmt.Errorf("encoding apply pending commission function failed: %w", err)
}

return encoded, err
}

func generateClaimCommissionFn(account *wallet.Account) ([]byte, error) {
claimCommisionFn := &contractsapi.ClaimCommissionHydraDelegationFn{
To: (types.Address)(account.Ecdsa.Address()),
}

encoded, err := claimCommisionFn.EncodeAbi()
if err != nil {
return nil, fmt.Errorf("encoding claim commission function failed: %w", err)
}

return encoded, err
}
44 changes: 30 additions & 14 deletions command/sidechain/commission/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,25 @@ import (
)

const (
commissionFlag = "commission"
applyFlag = "apply"
claimFlag = "claim"
)

type setCommissionParams struct {
accountDir string
accountConfig string
commission uint64
apply bool
claim bool
jsonRPC string
insecureLocalStore bool
}

type setCommissionResult struct {
staker string
newCommission uint64
}

func (scp *setCommissionParams) getRequiredFlags() []string {
return []string{
commissionFlag,
}
staker string
commission uint64
isPending bool
isClaimed bool
}

func (scp *setCommissionParams) validateFlags() error {
Expand All @@ -54,13 +53,30 @@ func (scp *setCommissionParams) validateFlags() error {
func (scr setCommissionResult) GetOutput() string {
var buffer bytes.Buffer

var vals []string
var addressString string

var valueString string

buffer.WriteString("\n[COMMISSION SET]\n")
if scr.isPending { //nolint:gocritic
buffer.WriteString("\n[NEW COMMISSION IS PENDING AND YOU MUST EXECUTE APPLY TX AFTER 15 DAYS]\n")

addressString = "Staker Address"
valueString = "New Commission"
} else if scr.isClaimed {
buffer.WriteString("\n[COMMISSION CLAIMED]\n")

addressString = "Received Address"
valueString = "Claimed Commission (wei)"
} else {
buffer.WriteString("\n[COMMISSION APPLIED]\n")

addressString = "Staker Address"
valueString = "Applied Commission"
}

vals = make([]string, 0, 3)
vals = append(vals, fmt.Sprintf("Staker Address|%s", scr.staker))
vals = append(vals, fmt.Sprintf("New Commission |%v", scr.newCommission))
vals := make([]string, 0, 2)
vals = append(vals, fmt.Sprintf("%s|%s", addressString, scr.staker))
vals = append(vals, fmt.Sprintf("%s|%v", valueString, scr.commission))

buffer.WriteString(helper.FormatKV(vals))
buffer.WriteString("\n")
Expand Down
Loading

0 comments on commit f1cde45

Please sign in to comment.