diff --git a/CHANGELOG.md b/CHANGELOG.md index 115cc38..ded1c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +## 1.4.0 +* SDK dependency updated to version 0.12.0 (see [SDK changelog](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.12.0/CHANGELOG.md)) +* Fork configuration to enable new on-chain delegated staking reward mechanism and new handling of rewards from mainchain +* Added support for metrics endpoint +* Bug fix: Modify entrypoint.sh: Improve declared ip detection in MacOS environments ## 1.3.1 * Bug fix: Modify entrypoint.sh: Allow all node types to set FORGER_MAXCONNECTIONS env variable diff --git a/api/version.json b/api/version.json index 123ec1e..24bc431 100644 --- a/api/version.json +++ b/api/version.json @@ -2,15 +2,15 @@ { "network":"mainnet", "networkName": "eon", - "version":"1.3.1", - "dockerImages":["zencash/evmapp:1.3.1","zencash/evmapp:latest"], - "ghReleaseLink":"github.com/HorizenOfficial/eon/releases/tag/1.3.1" + "version":"1.4.0", + "dockerImages":["zencash/evmapp:1.4.0","zencash/evmapp:latest"], + "ghReleaseLink":"github.com/HorizenOfficial/eon/releases/tag/1.4.0" }, { "network":"testnet", "networkName": "gobi", - "version":"1.3.1", - "dockerImages":["zencash/evmapp:1.3.1","zencash/evmapp:latest"], - "ghReleaseLink":"github.com/HorizenOfficial/eon/releases/tag/1.3.1" + "version":"1.4.0", + "dockerImages":["zencash/evmapp:1.4.0","zencash/evmapp:latest"], + "ghReleaseLink":"github.com/HorizenOfficial/eon/releases/tag/1.4.0" } -] +] \ No newline at end of file diff --git a/bootstraptool/pom.xml b/bootstraptool/pom.xml index 488f0a1..76edc39 100644 --- a/bootstraptool/pom.xml +++ b/bootstraptool/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.horizen bootstraptool - 1.3.1 + 1.4.0 2023 UTF-8 @@ -16,12 +16,12 @@ io.horizen sidechains-sdk-account_sctools - 0.11.0 + 0.12.0 io.horizen eon - 1.3.1 + 1.4.0 compile diff --git a/ci/setup_env.sh b/ci/setup_env.sh index fe15333..92d83e0 100755 --- a/ci/setup_env.sh +++ b/ci/setup_env.sh @@ -98,8 +98,8 @@ if [ -n "${TRAVIS_TAG}" ]; then check_signed_tag "${TRAVIS_TAG}" # Checking format of production release pom version - if ! [[ "${ROOT_POM_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?(-RC[0-9]+)?$ ]]; then - echo "Warning: package(s) version is in the wrong format for PRODUCTION} release. Expecting: d.d.d(-RC[0-9]+)?. The build is not going to be released !!!" + if ! [[ "${ROOT_POM_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$ ]]; then + echo "Warning: package(s) version is in the wrong format for PRODUCTION} release. Expecting: d.d.d(-[0-9]+)?. The build is not going to be released !!!" export IS_A_RELEASE="false" fi @@ -124,21 +124,30 @@ if [ -n "${TRAVIS_TAG}" ]; then check_signed_tag "${TRAVIS_TAG}" # Checking if package version matches DEV release version - if ! [[ "${ROOT_POM_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?(-RC[0-9]+)?(-SNAPSHOT){1}$ ]]; then - echo "Warning: package(s) version is in the wrong format for DEVELOPMENT release. Expecting: d.d.d(-RC[0-9]+)?(-SNAPSHOT){1}. The build is not going to be released !!!" - export IS_A_RELEASE="false" - fi - - # Checking Github tag format - if ! [[ "${TRAVIS_TAG}" =~ "${ROOT_POM_VERSION}"[0-9]*$ ]]; then - echo "" && echo "=== Warning: GIT tag format differs from the pom file version. ===" && echo "" - echo -e "Github tag name: ${TRAVIS_TAG}\nPom file version: ${ROOT_POM_VERSION}.\nThe build is not going to be released !!!" + if [[ "${ROOT_POM_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?(-SNAPSHOT){1}$ ]]; then + if [[ "${TRAVIS_TAG}" =~ "${ROOT_POM_VERSION}"[0-9]*$ ]]; then + echo "" && echo "=== Development release ===" && echo "" + export IS_A_RELEASE="true" + else + echo "" && echo "=== Warning: GIT tag format differs from the pom file version. ===" && echo "" + echo -e "Github tag name: ${TRAVIS_TAG}\nPom file version: ${ROOT_POM_VERSION}.\nThe build is not going to be released !!!" + export IS_A_RELEASE="false" + fi + elif [[ "${ROOT_POM_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-RC[0-9]+){1}$ ]]; then + if [[ "${TRAVIS_TAG}" == "${ROOT_POM_VERSION}" ]]; then + echo "" && echo "=== RC release ===" && echo "" + export IS_A_RELEASE="true" + else + echo "" && echo "=== Warning: GIT tag format differs from the pom file version. ===" && echo "" + echo -e "Github tag name: ${TRAVIS_TAG}\nPom file version: ${ROOT_POM_VERSION}.\nThe build is not going to be released !!!" + export IS_A_RELEASE="false" + fi + else + echo "Warning: package(s) version is in the wrong format for DEVELOPMENT or RC release. Expecting: d.d.d(-SNAPSHOT){1} or d.d.d(-RC[0-9]+){1}. The build is not going to be released !!!" export IS_A_RELEASE="false" fi if [ "${IS_A_RELEASE}" = "true" ]; then - echo "" && echo "=== Development release ===" && echo "" - export PROD_RELEASE="false" export IS_A_GH_PRERELEASE="true" fi diff --git a/doc/api/index.md b/doc/api/index.md index 7dcc756..ce73072 100644 --- a/doc/api/index.md +++ b/doc/api/index.md @@ -40,10 +40,18 @@ Following endpoints are available on EON node: #### Forge stakes management: -[/transaction/makeForgerStake](/doc/api/transaction/makeForgerStake.md)\ -[/transaction/spendForgingStake](/doc/api/transaction/spendForgingStake.md)\ [/transaction/allForgingStakes](/doc/api/transaction/allForgingStakes.md)\ -[/transaction/myForgingStakes](/doc/api/transaction/myForgingStakes.md) +[/transaction/myForgingStakes](/doc/api/transaction/myForgingStakes.md)\ +[/transaction/pagedForgersStakesByForger](/doc/api/transaction/pagedForgersStakesByForger.md)\ +[/transaction/pagedForgersStakesByDelegator](/doc/api/transaction/pagedForgersStakesByDelegator.md)\ +[/transaction/registerForger](/doc/api/transaction/registerForger.md)\ +[/transaction/updateForger](/doc/api/transaction/updateForger.md) + +Deprecated methods: + +[/transaction/pagedForgingStakes](/doc/api/transaction/pagedForgingStakes.md)\ +[/transaction/makeForgerStake](/doc/api/transaction/makeForgerStake.md)\ +[/transaction/spendForgingStake](/doc/api/transaction/spendForgingStake.md) #### Restricted forgers management: @@ -97,3 +105,6 @@ Following endpoints are available on EON node: [/node/blacklistedPeers](/doc/api/node/blacklistedPeers.md)\ [/node/stop](/doc/api/node/stop.md) +## Metrics endpoints +[/metrics](/doc/api/metrics/index.md) + diff --git a/doc/api/metrics/index.md b/doc/api/metrics/index.md new file mode 100644 index 0000000..21361ea --- /dev/null +++ b/doc/api/metrics/index.md @@ -0,0 +1,46 @@ +[< EON API Documentation](/doc/api/index.md) +### metrics + +Returns metrics about this node in Prometheus format. + +**Parameters** + +No parameters + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/metrics' -H 'Content-Type: application/json' -H 'accept: application/json' + +**Response** + + The metrics will be exposed one line per metric, in this format: + +``` +metric_id value +``` + +Available metrics: + +Following metrics will be available (also listed in the endpoint /metrics/help): + +- **block_apply_time**
+Time to apply block to node wallet and state (milliseconds) +- **block_apply_time_fromslotstart**
+Delta between timestamp when block has been applied successfully on this node and start timestamp of the slot it belongs to (milliseconds) +- **block_applied_ok**
+Number of received blocks applied successfully (absolute value since start of the node) +- **block_applied_ko**
+Number of received blocks not applied (absolute value since start of the node) +- **mempool_size**
+Mempool size (number of transactions in this node mempool) +- **forge_block_count**
+Number of forged blocks by this node (absolute value since start of the node) +- **forge_lottery_time**
+Time to execute the lottery (milliseconds) +- **forge_blockcreation_time**
+Time to create a new forged block (calculated from the start timestamp of the slot it belongs to) (milliseconds) + + + + + diff --git a/doc/api/transaction/allForgingStakes.md b/doc/api/transaction/allForgingStakes.md index b43c36b..ccab2a8 100644 --- a/doc/api/transaction/allForgingStakes.md +++ b/doc/api/transaction/allForgingStakes.md @@ -14,7 +14,6 @@ Returns all forging stakes. { "result" : { "stakes" : [ { - "stakeId" : "15f8f1054ae53bd707078248ce44d434b975dbdd250af65b21b6be4b4e601021", "forgerStakeData" : { "forgerPublicKeys" : { "blockSignPublicKey" : { @@ -30,7 +29,6 @@ Returns all forging stakes. "stakedAmount" : 1000000000000000000 } }, { - "stakeId" : "f62dcc5facc6440d6da2f7b927c6440e9bcb643817a12bf733e5304cd655ecd9", "forgerStakeData" : { "forgerPublicKeys" : { "blockSignPublicKey" : { @@ -41,7 +39,7 @@ Returns all forging stakes. } }, "ownerPublicKey" : { - "address" : "62b1bc6fd237b775138d910274ff2911d7aea5cc" + "address" : "138d910274ff29162b1bc6fd237b7751d7aea5cc" }, "stakedAmount" : 99000000000000000000 } diff --git a/doc/api/transaction/getKeysOwnerScAddresses.md b/doc/api/transaction/getKeysOwnerScAddresses.md index 7b4ee86..ef09ab2 100644 --- a/doc/api/transaction/getKeysOwnerScAddresses.md +++ b/doc/api/transaction/getKeysOwnerScAddresses.md @@ -1,8 +1,8 @@ [< EON API Documentation](/doc/api/index.md) ### transaction/getKeysOwnerScAddresses -Return the list of all EON sidechain addresses that have at least one mainchain associated.\ -This function is used for voting pourposes in ZENDAO. +Returns the list of all EON sidechain addresses that have at least one mainchain address associated.\ +This function is used for voting purposes in ZENDAO. **Parameters** @@ -17,4 +17,4 @@ No parameter needed. { "owners": ["", "", ...] - } \ No newline at end of file + } diff --git a/doc/api/transaction/getKeysOwnership.md b/doc/api/transaction/getKeysOwnership.md index 691882d..e07582f 100644 --- a/doc/api/transaction/getKeysOwnership.md +++ b/doc/api/transaction/getKeysOwnership.md @@ -2,7 +2,7 @@ ### transaction/getKeysOwnership Return the list of Horizen mainchain addresses associated to a given EON sidechain addres.\ -This function is used for voting pourposes in ZENDAO. +This function is used for voting purposes in ZENDAO. **Parameters** @@ -22,4 +22,4 @@ This function is used for voting pourposes in ZENDAO. "keysOwnership": { "": ["", "", ...] } - } \ No newline at end of file + } diff --git a/doc/api/transaction/makeForgerStake.md b/doc/api/transaction/makeForgerStake.md index 7bde296..31c21ef 100644 --- a/doc/api/transaction/makeForgerStake.md +++ b/doc/api/transaction/makeForgerStake.md @@ -4,6 +4,10 @@ Creates and signs a transaction to create a forger stake.\ Can be performed by both the forger and by other delegators. +> [!IMPORTANT] +> This endpoint is deprecated and disabled from EON 1.4.0. +> Same action will be possible through the native smart contract method delegate on [ForgerStakesV2](/doc/nativesc/contracts/ForgerStakesV2.md) + **Parameters** See request body below. diff --git a/doc/api/transaction/myForgingStakes.md b/doc/api/transaction/myForgingStakes.md index 9128335..305f33b 100644 --- a/doc/api/transaction/myForgingStakes.md +++ b/doc/api/transaction/myForgingStakes.md @@ -14,7 +14,6 @@ Returns the forging stakes belonging to keys contained in the node wallet. { "result" : { "stakes" : [ { - "stakeId" : "15f8f1054ae53bd707078248ce44d434b975dbdd250af65b21b6be4b4e601021", "forgerStakeData" : { "forgerPublicKeys" : { "blockSignPublicKey" : { @@ -30,7 +29,6 @@ Returns the forging stakes belonging to keys contained in the node wallet. "stakedAmount" : 1000000000000000000 } }, { - "stakeId" : "f62dcc5facc6440d6da2f7b927c6440e9bcb643817a12bf733e5304cd655ecd9", "forgerStakeData" : { "forgerPublicKeys" : { "blockSignPublicKey" : { @@ -41,7 +39,7 @@ Returns the forging stakes belonging to keys contained in the node wallet. } }, "ownerPublicKey" : { - "address" : "62b1bc6fd237b775138d910274ff2911d7aea5cc" + "address" : "5138d910274ff2962b1bc6fd237b7711d7aea5cc" }, "stakedAmount" : 99000000000000000000 } diff --git a/doc/api/transaction/openForgerList.md b/doc/api/transaction/openForgerList.md index 34aab9b..bfe11c7 100644 --- a/doc/api/transaction/openForgerList.md +++ b/doc/api/transaction/openForgerList.md @@ -10,7 +10,7 @@ When the majority of the closed forgers have voted to open forging, everyone wil | Name | Type | Required | Description | | -------- | ------- | ------- | ------- | | forgerIndex | integer | yes | Index (0-based) of the forger that is voting, referred to the allowed forgers list in the configuration file. Forger's keys of the voter must be present in the local wallet. | -| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | | gasInfo | object | no | Info about GAS | Parameters of the gasInfo object: diff --git a/doc/api/transaction/pagedForgersStakesByDelegator.md b/doc/api/transaction/pagedForgersStakesByDelegator.md new file mode 100644 index 0000000..1004e0d --- /dev/null +++ b/doc/api/transaction/pagedForgersStakesByDelegator.md @@ -0,0 +1,55 @@ +[< EON API Documentation](/doc/api/index.md) +### transaction/pagedForgerStakesByDelegator + +Returns the paginated list of forging stakes, filtered by a specific delegator.
+Available from: EON 1.4.0 + +**Parameters** + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| delegatorAddress | address | yes | Address of the delegator | +| startPos | integer | no (default 0) | Start position in the paginated list | +| size | integer | no (default 10)| Number of records to return | + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/transaction/pagedForgerStakesByDelegator' -H 'Content-Type: application/json' -H 'accept: application/json' -d + +The request body format is like this: + + { + "delegatorAddress": "62b1bc6fd237b775138d910274ff2911d7aea5cc", + "startPos": 0, + "size": 10 + } + + +**Example response** + + { + "result" : { + "nextPos": 10, + "stakes" : [ { + "forgerPublicKeys" : { + "blockSignPublicKey" : { + "publicKey" : "10e9b5236a56cddb9f0332e9dd6d69151494f24172b26ab24a27473bbc92a181" + }, + "vrfPublicKey" : { + "publicKey" : "6a376f8a88b386f69296baa0792641d393c85a19b28dfd4a11d8f0a74618873280" + } + }, + "stakedAmount" : 1000000000000000000 + }, { + "forgerPublicKeys" : { + "blockSignPublicKey" : { + "publicKey" : "4172b26ab24a27473bbc910e9b5236a56cddb9f0332e9dd6d69151494f22a181" + }, + "vrfPublicKey" : { + "publicKey" : "aa0792641d393c85a19b28dfd4a6a376f8a88b386f69296b11d8f0a74618873280" + } + }, + "stakedAmount" : 99000000000000000000 + }] + } + } diff --git a/doc/api/transaction/pagedForgersStakesByForger.md b/doc/api/transaction/pagedForgersStakesByForger.md new file mode 100644 index 0000000..bd0ddea --- /dev/null +++ b/doc/api/transaction/pagedForgersStakesByForger.md @@ -0,0 +1,57 @@ +[< EON API Documentation](/doc/api/index.md) +### transaction/pagedForgerStakesByForger + +Returns the paginated list of forging stakes, filtered by a specific forger.
+Available from: EON 1.4.0 + +**Parameters** + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| blockSignPubKey | string | yes | blockSignPubKey identifying the forger | +| vrfPubKey | string | yes | vrfPubKey identifying the forger | +| startPos | integer | no (default 0) | Start position in the paginated list | +| size | integer | no (dafault 10)| Number of records to return | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | +| gasInfo | object | no | Info about GAS | + +Parameters of the gasInfo object: + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| gasLimit | biginteger | yes | GAS limit | +| maxPriorityFeePerGas | biginteger | yes | Max priority fee| +| maxFeePerGas | biginteger | yes | Max fee per gas | + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/transaction/pagedForgerStakesByForger' -H 'Content-Type: application/json' -H 'accept: application/json' -d + +The request body format is like this: + + { + "blockSignPubKey": "10e9b5236a56cddb9f0332e9dd6d69151494f24172b26ab24a27473bbc92a181", + "vrfPubKey": "6a376f8a88b386f69296baa0792641d393c85a19b28dfd4a11d8f0a74618873280", + "startPos": 0, + "size": 10 + } + + +**Example response** + + { + "result" : { + "nextPos": 10, + "stakes" : [ { + "delegator" : { + "address" : "62b1bc6fd237b775138d910274ff2911d7aea5cc" + }, + "stakedAmount" : 1000000000000000000 + }, { + "delegator" : { + "address" : "775138d910274ff291162b1bc6fd237bd7aea5cc" + }, + "stakedAmount" : 99000000000000000000 + }] + } + } \ No newline at end of file diff --git a/doc/api/transaction/pagedForgingStakes.md b/doc/api/transaction/pagedForgingStakes.md new file mode 100644 index 0000000..8481420 --- /dev/null +++ b/doc/api/transaction/pagedForgingStakes.md @@ -0,0 +1,68 @@ +[< EON API Documentation](/doc/api/index.md) +### transaction/pagedForgingStakes + +Returns the paginated list of forging stakes.
+Available from: EON 1.3.0 + +> [!IMPORTANT] +> This endpoint is deprecated and disabled from EON 1.4.0. + +**Parameters** + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| startPos | integer | no (default 0) | Start position in the paginated list | +| size | integer | no (default 10)| Number of records to return | + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/transaction/pagedForgingStakes' -H 'Content-Type: application/json' -H 'accept: application/json' -d + +The request body format is like this: + + { + "startPos": 0, + "size": 2 + } + + +**Example response** + + { + "result" : { + "nextPos": 2, + "stakes" : [ { + "stakeId" : "74b685e3b35941394a88085e864810ced41f978b008c60c4b9bc6364ff80c3a3", + "forgerStakeData" : { + "forgerPublicKeys" : { + "blockSignPublicKey" : { + "publicKey" : "041e38dc04de208df1c05eeee8a91b92e4336e11f5113b6505dd745df417b4ae" + }, + "vrfPublicKey" : { + "publicKey" : "a6b64b0b6eff71b6514d9893d730664ef2297302db4f86e32280f270b5e4370a00" + } + } + }, + "ownerPublicKey" : { + "address" : "edeb4bf692a4a1bfecad78e09be5c946ecf6c6da" + }, + "stakedAmount" : 1000000000000000 + }, { + "stakeId" : "665ea03b9f9c5e4060f28c50816289e69799548f6771de339ca4f318a845e9e0", + "forgerStakeData" : { + "forgerPublicKeys" : { + "blockSignPublicKey" : { + "publicKey" : "041e38dc04de208df1c05eeee8a91b92e4336e11f5113b6505dd745df417b4ae" + }, + "vrfPublicKey" : { + "publicKey" : "a6b64b0b6eff71b6514d9893d730664ef2297302db4f86e32280f270b5e4370a00" + } + } + }, + "ownerPublicKey" : { + "address" : "0676335330fa1e80427069a1de4612d9b79fcbe2" + }, + "stakedAmount" : 1000000000000000000 + } ] + } + } \ No newline at end of file diff --git a/doc/api/transaction/registerForger.md b/doc/api/transaction/registerForger.md new file mode 100644 index 0000000..7d5c02a --- /dev/null +++ b/doc/api/transaction/registerForger.md @@ -0,0 +1,48 @@ +[< EON API Documentation](/doc/api/index.md) +### transaction/registerForger + +Register a forger.
+Available from: EON 1.4.0
+ +Note: starting from EON 1.4.0 this action is mandatory before being able to forge/receive new stakes. A minimum amount of 10 ZEN is required to perform the action.
+ +**Parameters** + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| blockSignPubKey | string | yes | Key of this forger (private key must be in the local wallet) | +| vrfPubKey | string |yes | Vrfkey of this forger (private key must be in the local wallet) | +| rewardShare | integer | yes | Reward to be redirected to a separate reward address (integer, range from 0 to 1000 - where 1000 represents 100%) | +| rewardAddress | string | yes if rewardShare > 0 | External reward address (may be a single EOA or (more likely) a smart contract handling rewards distribution to delegators) | +| stakedAmount | long |yes | Amount to be assigned as first stake to this forger. Specified in zennies, and must be >= 10 ZEN | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| gasInfo | object | no | Info about GAS | + +Parameters of the gasInfo object: + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| gasLimit | biginteger | yes | GAS limit | +| maxPriorityFeePerGas | biginteger | yes | Max priority fee| +| maxFeePerGas | biginteger | yes | Max fee per gas | + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/transaction/registerForger' -H 'Content-Type: application/json' -H 'accept: application/json' -d + +The request body format is like this: + + { + "blockSignPubKey": "10e9b5236a56cddb9f0332e9dd6d69151494f24172b26ab24a27473bbc92a181", + "vrfPubKey": "6a376f8a88b386f69296baa0792641d393c85a19b28dfd4a11d8f0a74618873280", + "rewardShare": "234", + "rewardAddress": "62b1bc6fd237b775138d910274ff2911d7aea5cc", + "stakedAmount": "100000000000", + } + + +**Example response** + + { + "transactionId": "xxxxxxxxxxxxxxxxx" + } \ No newline at end of file diff --git a/doc/api/transaction/removeKeysOwnership.md b/doc/api/transaction/removeKeysOwnership.md index 42a4658..4f59857 100644 --- a/doc/api/transaction/removeKeysOwnership.md +++ b/doc/api/transaction/removeKeysOwnership.md @@ -2,14 +2,14 @@ ### transaction/removeKeysOwnership Create and send a transaction to remove a previously created association between Horizen mainchain address and EON sidechain addres.\ -This function is used for voting pourposes in ZENDAO. +This function is used for voting purposes in ZENDAO. **Parameters** | Name | Type | Required | Description | | -------- | ------- | ------- | ------- | | ownershipInfo | object | yes | Info about keys ownership (see below) | -| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | | gasInfo | object | no | Info about GAS | Parameters of the ownershipInfo object: @@ -47,4 +47,4 @@ The request body format is like this: { "transactionId": "xxxxxxxxxxxxxxxxx" - } \ No newline at end of file + } diff --git a/doc/api/transaction/sendKeysOwnership.md b/doc/api/transaction/sendKeysOwnership.md index c34d707..89ce755 100644 --- a/doc/api/transaction/sendKeysOwnership.md +++ b/doc/api/transaction/sendKeysOwnership.md @@ -4,14 +4,14 @@ Create and send a transaction to associate a Horizen mainchain address to one EON sidechain addres.\ It internally performs the verification of the ownership of the mainchain address (a signature must be provided).\ It can be called many times from the same EON address.\ -This function is used for voting pourposes in ZENDAO. +This function is used for voting purposes in ZENDAO. **Parameters** | Name | Type | Required | Description | | -------- | ------- | ------- | ------- | | ownershipInfo | object | yes | Info about keys ownership (see below) | -| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | | gasInfo | object | no | Info about GAS | Parameters of the ownershipInfo object: @@ -51,4 +51,4 @@ The request body format is like this: { "transactionId": "xxxxxxxxxxxxxxxxx" - } \ No newline at end of file + } diff --git a/doc/api/transaction/spendForgingStake.md b/doc/api/transaction/spendForgingStake.md index 27b3c17..9598e44 100644 --- a/doc/api/transaction/spendForgingStake.md +++ b/doc/api/transaction/spendForgingStake.md @@ -4,12 +4,16 @@ Creates and signs a transaction to remove a forger stake and have ZEN back in the wallet.\ Note: is not possible to remove partially a forging stake, all the stacked ZEN associated to the specific stakeId will be removed in a single transaction. +> [!IMPORTANT] +> This endpoint is deprecated and disabled from EON 1.4.0. +> Same action will be possible through the native smart contract method withdraw on [ForgerStakesV2](/doc/nativesc/contracts/ForgerStakesV2.md) + **Parameters** | Name | Type | Required | Description | | -------- | ------- | ------- | ------- | | stakeId | string | yes | Id of the stake to be removed | -| nonce | string | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| nonce | string | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | | gasInfo | object | no | | Parameters of the gasInfo object: diff --git a/doc/api/transaction/updateForger.md b/doc/api/transaction/updateForger.md new file mode 100644 index 0000000..c7c64d0 --- /dev/null +++ b/doc/api/transaction/updateForger.md @@ -0,0 +1,46 @@ +[< EON API Documentation](/doc/api/index.md) +### transaction/updateForger + +Updates an existing forger.
+Available from: EON 1.4.0
+ +Note: this action can be performed only for forgers with rewardShare = 0 and rewardAddress not set, and only to assign them a value. This operation can be called only if at least 2 epochs are passed by since the 1.4 fork activation.
+ +**Parameters** + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| blockSignPubKey | string | yes | Key of this forger (private key must be in the local wallet) | +| vrfPubKey | string |yes | Vrfkey of this forger (private key must be in the local wallet) | +| rewardShare | integer | yes | Reward to be redirected to a separate reward address (integer, range from 0 to 1000 - where 1000 represents 100%) | +| rewardAddress | string | yes | External reward address (may be a single EOA or (more likely) a smart contract handling rewards distribution to delegators) | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | +| gasInfo | object | no | Info about GAS | + +Parameters of the gasInfo object: + +| Name | Type | Required | Description | +| -------- | ------- | ------- | ------- | +| gasLimit | biginteger | yes | GAS limit | +| maxPriorityFeePerGas | biginteger | yes | Max priority fee| +| maxFeePerGas | biginteger | yes | Max fee per gas | + +**Example request** + + curl -sX POST 'http://127.0.0.1:9085/transaction/updateForger' -H 'Content-Type: application/json' -H 'accept: application/json' -d + +The request body format is like this: + + { + "blockSignPubKey": "10e9b5236a56cddb9f0332e9dd6d69151494f24172b26ab24a27473bbc92a181", + "vrfPubKey": "6a376f8a88b386f69296baa0792641d393c85a19b28dfd4a11d8f0a74618873280", + "rewardShare": "234", + "rewardAddress": "62b1bc6fd237b775138d910274ff2911d7aea5cc" + } + + +**Example response** + + { + "transactionId": "xxxxxxxxxxxxxxxxx" + } \ No newline at end of file diff --git a/doc/api/transaction/withdrawCoins.md b/doc/api/transaction/withdrawCoins.md index f446919..5188822 100644 --- a/doc/api/transaction/withdrawCoins.md +++ b/doc/api/transaction/withdrawCoins.md @@ -7,7 +7,7 @@ Creates and posts a backward transfer transaction to sends some ZEN back to the | Name | Type | Required | Description | | -------- | ------- | ------- | ------- | -| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted the next valid nonce will be calculated automatically. | +| nonce | integer | no | Nonce associated to the address that is sending the tx. If omitted, the latest nonce saved in the state will be used by default. | | withdrawalRequest | object | yes | Description of the withdrawal request | | gasInfo | object | no | Info about GAS | diff --git a/doc/howto/DelegatedStaking.sol b/doc/howto/DelegatedStaking.sol new file mode 100644 index 0000000..b0dc82b --- /dev/null +++ b/doc/howto/DelegatedStaking.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import "./interfaces/ForgerStakesV2.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +contract DelegatedStaking is ReentrancyGuard { + + bytes32 public signPublicKey; + bytes32 public forgerVrf1; + bytes1 public forgerVrf2; + ForgerStakesV2 public forger = ForgerStakesV2(0x0000000000000000000022222222222222222333); + + mapping(address => uint32) public lastClaimedEpochForAddress; + uint32 public constant MAX_NUMBER_OF_EPOCH = 100; + + struct ClaimData { + uint32 epochNumber; + uint256 claimedReward; + } + //events + event Claim(bytes32 signPublicKey, bytes32 indexed forgerVrf1, bytes1 indexed forgerVrf2, address indexed delegator, ClaimData[] claimData); + //error + error TooManyEpochs(uint256 lastClaimedEpoch, uint256 currentEpoch); + error NothingToClaim(); + error ArraysHaveDifferentLengths(); + + //constructor + constructor(bytes32 _signPublicKey, bytes32 vrf1, bytes1 vrf2) { + signPublicKey = _signPublicKey; + forgerVrf1 = vrf1; + forgerVrf2 = vrf2; + } + + function claimReward(address payable owner) nonReentrant external { + (uint256 totalToClaim, ClaimData[] memory claimDetails) = calcReward(owner); + if(totalToClaim == 0) { + revert NothingToClaim(); + } + + //update last claimed epoch + lastClaimedEpochForAddress[owner] = claimDetails[claimDetails.length - 1].epochNumber; + + //transfer reward + owner.transfer(totalToClaim); + emit Claim(signPublicKey, forgerVrf1, forgerVrf2, owner, claimDetails); + } + + function calcReward(address owner) public view returns(uint256 totalToClaim, ClaimData[] memory claimDetails) { + int32 startEpochSigned = _getStartEpochForAddress(owner); + if(startEpochSigned == -1 ) { + return (0, new ClaimData[](0)); + } + + uint32 startEpoch = uint32(startEpochSigned); + if(startEpoch >= forger.getCurrentConsensusEpoch()) { + //nothing to claim + //we are et epoch N, delegator already claimed until N-1 OR + //we are at an epoch that is lower than 2 (2 is minimum for startEpoch) + return (0, new ClaimData[](0)); + } + + //get sum fees + uint256[] memory sumFeeAccruedInEpoch = forger.rewardsReceived(signPublicKey, forgerVrf1, forgerVrf2, startEpoch, MAX_NUMBER_OF_EPOCH); + uint32 length = uint32(sumFeeAccruedInEpoch.length); + + uint256[] memory delegatorStakes = forger.stakeTotal(signPublicKey, forgerVrf1, forgerVrf2, owner, startEpoch - 2, length); + uint256[] memory totalStakes = forger.stakeTotal(signPublicKey, forgerVrf1, forgerVrf2, address(0), startEpoch - 2, length); + + //check lengths + if(length != delegatorStakes.length || length != totalStakes.length) revert ArraysHaveDifferentLengths(); + + claimDetails = new ClaimData[](length); + + uint32 i; //loop + uint32 epoch = startEpoch; + + while(i != length) { + uint256 claimedReward; + + if(totalStakes[i] == 0) { + claimedReward = 0; //avoid divison by 0 + } + else { + claimedReward = sumFeeAccruedInEpoch[i] * delegatorStakes[i] / totalStakes[i]; + } + + totalToClaim += claimedReward; + + claimDetails[i] = ClaimData(epoch, claimedReward); + unchecked { ++i; ++epoch; } + } + + return (totalToClaim, claimDetails); + } + + function _getStartEpochForAddress(address owner) internal view returns(int32) { + uint32 lastClaimedEpoch = lastClaimedEpochForAddress[owner]; + int32 startEpoch; + if(lastClaimedEpoch == 0) { + int32 stakeStartForUser = forger.stakeStart(signPublicKey, forgerVrf1, forgerVrf2, owner); + //start from the first epoch user has staked + if(stakeStartForUser == -1) return -1; + startEpoch = stakeStartForUser + 2; + } + else { + startEpoch = int32(lastClaimedEpoch) + 1; //start from the next to claim + } + + return startEpoch < 2 ? int32(2) : startEpoch; //nothing to claim in first two epochs due to n-2 + } +} \ No newline at end of file diff --git a/doc/howto/delegatedstakingcontract.md b/doc/howto/delegatedstakingcontract.md new file mode 100644 index 0000000..c1c9f4f --- /dev/null +++ b/doc/howto/delegatedstakingcontract.md @@ -0,0 +1,41 @@ +[< EON Documentation index](/doc/index.md) +# Delegated Staking contract + +The Delegated Staking contract is used by Forgers to: + +- Receive block rewards for staking +- Handle distribution of the rewards between the different delegators of the forger. + +A [template implementation](/doc/howto/DelegatedStaking.sol) is provided for the contract. + +## Template implementation details + +- It should be instantiated specifying the forger's parameters: + - `bytes32 signPubKey` + - `bytes32 vrf1`: first 32 bytes of the 33-bytes VRF key for the forger + - `bytes1 vrf2`: last byte of the 33-bytes VRF key for the forger + + `ForgerStakesV2` address is hardcoded to `0x0000000000000000000022222222222222222333` + + +- It has no `receive`, `fallback` or `payable` function: that means that it can't directly receive ZEN with a blockchain transaction, but it can receive it choosing the contract address as receiver of the forger's rewards. + +- Two methods are offered for the delegator(s): + - `calcReward(address)` returns a tuple containing the total reward that the specified address could claim at the current moment as first element; an array of `ClaimData` objects, each one containing the number of the claimable epoch paired with the amount of ZEN that is claimed for that epoch. + - `claimReward(address)` executes the claim for the specified address, transferring the amount calculated by `calcReward` and updating the internal state. Please note that **any** address can execute the claim for another address, if the target one is a valid delegator. The method emit a `Claim` event with the forger's identifiers, the delegator address, and the `ClaimData` array. To avoid reentrancy attacks, the method uses the `nonReentrant` modifier inherited by OpenZeppelin's `ReentrancyGuard`. + +- The implementation is **not** upgradeable. + +## Rewards Calculation + +The rewards for each delegator in an epoch `N` are calculated as the following: + +``` +reward_at_epoch_N = (total_reward_for_forger_at_N-2 * current_delegator_stakes_at_epoch_N) / total_delegator_stakes_at_epoch_N +``` + +That means that a user will be able to claim the rewards starting from 2 epochs after they execute the stakes. + +When calculating or executing the claim, the smart contract checks if the target address actually delegated to the current forger invoking the `stakeStart` method, that returns the first valid epoch in which the delegator delegated or `-1` otherwise. Then, the smart contract calculates the claim amount for each epoch with the above formula starting from the epoch returned by `stakeStart` method if the address never claimed the rewards, or from the last epoch for which the address claimed the rewards (stored into the `lastClaimedEpochForAddress` mapping) otherwise. + +To avoid to exceed block gas limit when executing a claim after too much epoch, the maximum number of epochs that can be claimed with one invokation is limited by the `MAX_NUMBER_OF_EPOCH` constant, in the template contract set to 100. \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 7c26c32..f3ad3cb 100644 --- a/doc/index.md +++ b/doc/index.md @@ -9,9 +9,10 @@ Find [here documentation](/doc/nativesc/index.md) about EON native smart contrac # HowTOs and tutorials - [EON logging configuration](/doc/howto/customlog.md) - +- [Delegated Staking contract](/doc/howto/delegatedstakingcontract.md) # EON Release Notes +## Version [1.4.0](/doc/release/1.4.0.md) ## Version [1.3.0](/doc/release/1.3.0.md) ## Version [1.2.1](/doc/release/1.2.1.md) ## Version [1.2.0](/doc/release/1.2.0.md) diff --git a/doc/nativesc/contracts/ForgerStakes.md b/doc/nativesc/contracts/ForgerStakes.md index f85d9bc..6d39584 100644 --- a/doc/nativesc/contracts/ForgerStakes.md +++ b/doc/nativesc/contracts/ForgerStakes.md @@ -3,6 +3,9 @@ This native smart contract manages the forger stakes. +> [!IMPORTANT] +> After the activation of the EON 1.4.0 hardfork, this smart contract has been deprecated, and replaced by [ForgerStakesV2](/doc/nativesc/contracts/ForgerStakesV2.md) + | | | | -------- | ------- | | Contract address: | 0x0000000000000000000022222222222222222222 | diff --git a/doc/nativesc/contracts/ForgerStakesV2.json b/doc/nativesc/contracts/ForgerStakesV2.json new file mode 100644 index 0000000..86a4607 --- /dev/null +++ b/doc/nativesc/contracts/ForgerStakesV2.json @@ -0,0 +1,714 @@ +{ + "abi": [ + { + "anonymous": false, + "inputs": [], + "name": "ActivateStakeV2", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "DelegateForgerStake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "reward_address", + "type": "address" + } + ], + "name": "RegisterForger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "reward_address", + "type": "address" + } + ], + "name": "UpdateForger", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "WithdrawForgerStake", + "type": "event" + }, + { + "inputs": [], + "name": "activate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentConsensusEpoch", + "outputs": [ + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + } + ], + "name": "getForger", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "internalType": "address", + "name": "reward_address", + "type": "address" + } + ], + "internalType": "struct ForgerStakesV2.ForgerInfo", + "name": "forgerInfo", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int32", + "name": "startIndex", + "type": "int32" + }, + { + "internalType": "int32", + "name": "pageSize", + "type": "int32" + } + ], + "name": "getPagedForgers", + "outputs": [ + { + "internalType": "int32", + "name": "nextIndex", + "type": "int32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "internalType": "address", + "name": "reward_address", + "type": "address" + } + ], + "internalType": "struct ForgerStakesV2.ForgerInfo[]", + "name": "listOfForgerInfo", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "int32", + "name": "startIndex", + "type": "int32" + }, + { + "internalType": "int32", + "name": "pageSize", + "type": "int32" + } + ], + "name": "getPagedForgersStakesByDelegator", + "outputs": [ + { + "internalType": "int32", + "name": "nextIndex", + "type": "int32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + } + ], + "internalType": "struct ForgerStakesV2.StakeDataForger[]", + "name": "listOfForgerStakes", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "int32", + "name": "startIndex", + "type": "int32" + }, + { + "internalType": "int32", + "name": "pageSize", + "type": "int32" + } + ], + "name": "getPagedForgersStakesByForger", + "outputs": [ + { + "internalType": "int32", + "name": "nextIndex", + "type": "int32" + }, + { + "components": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + } + ], + "internalType": "struct ForgerStakesV2.StakeDataDelegator[]", + "name": "listOfDelegatorStakes", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrfKey1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrfKey2", + "type": "bytes1" + }, + { + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "sign1_1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign1_2", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_2", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_3", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "sign2_4", + "type": "bytes1" + } + ], + "name": "registerForger", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint32", + "name": "consensusEpochStart", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxNumOfEpoch", + "type": "uint32" + } + ], + "name": "rewardsReceived", + "outputs": [ + { + "internalType": "uint256[]", + "name": "listOfRewards", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + } + ], + "name": "stakeStart", + "outputs": [ + { + "internalType": "int32", + "name": "consensusEpochStart", + "type": "int32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint32", + "name": "consensusEpochStart", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxNumOfEpoch", + "type": "uint32" + } + ], + "name": "stakeTotal", + "outputs": [ + { + "internalType": "uint256[]", + "name": "listOfStakes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint32", + "name": "rewardShare", + "type": "uint32" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "sign1_1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign1_2", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_1", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_2", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sign2_3", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "sign2_4", + "type": "bytes1" + } + ], + "name": "updateForger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "signPubKey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "vrf1", + "type": "bytes32" + }, + { + "internalType": "bytes1", + "name": "vrf2", + "type": "bytes1" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} diff --git a/doc/nativesc/contracts/ForgerStakesV2.md b/doc/nativesc/contracts/ForgerStakesV2.md new file mode 100644 index 0000000..e43ecc9 --- /dev/null +++ b/doc/nativesc/contracts/ForgerStakesV2.md @@ -0,0 +1,172 @@ +[< EON Native Smart Contracts Documentation](/doc/nativesc/index.md) +### ForgerStakesV2 + +This native smart contract manages the forger stakes from EON 1.4.0 version. + +| | | +| -------- | ------- | +| Contract address: | 0x0000000000000000000022222222222222222333 | +| ABI descriptor: | [Click here](/doc/nativesc/contracts/ForgerStakesV2.json) | +| Solidity interface: | [Click here](/doc/nativesc/contracts/ForgerStakesV2.sol) | + + + +**Methods available** + +- registerForger + + function registerForger(bytes32 signPubKey, bytes32 vrfKey1, bytes1 vrfKey2, uint32 rewardShare, + address rewardAddress, bytes32 sign1_1, bytes32 sign1_2, + bytes32 sign2_1, bytes32 sign2_2, bytes32 sign2_3, bytes1 sign2_4) external payable; + + Register a new forger. + rewardShare can range in [0..1000] and can be 0 if and only if rewardAddress == 0x000..00.
+ Vrf key and signatures are split in two or more separate parameters, being longer than 32 bytes.
+ sign1_x are the 25519 signature chunks and sign2_x are the Vfr signature chunks.
+ The message to sign is the first 31 bytes of Keccak256 hash of a string formed by the concatenation + of signPubKey+vrfKey+rewardShare+rewardAddress. rewardAddress is represented in the Eip55 + checksum format and hex strings are lowercase with no prefix.
+ The method accepts WEI value: the sent value will be converted to the initial stake assigned to the forger.
+ The initial stake amount must be >= min threshold (10 Zen) + +- updateForger + + function updateForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint32 rewardShare, address rewardAddress, bytes32 signature1, bytes32 signature2) external; + + Updates an existing forger.
+ A forger can be updated just once and only if rewardAddress == 0x000..00 and rewardShare == 0.
+ Vrf key is split in two separate parameters, being longer than 32 bytes.
+ This operation should be called only if at least 2 epochs are passed by since the 1.4 fork activation. + +- delegate + + function delegate(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2) external payable; + + Delegates a stake to a previously registered forger.
+ Vrf key is split in two separate parameters, being longer than 32 bytes.
+ All the ZEN sent with the transaction will be delegated, and the owner will be the sender address. + +- withdraw + + function withdraw(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint256 amount) external; + + Withdraws (unstake) a previously assigned stake.
+ Vrf key is split in two separate parameters, being longer than 32 bytes.
+ The amount is added to the balance of the sender of the transaction. + +- stakeTotal + + function stakeTotal(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, address delegator, uint32 consensusEpochStart, uint32 maxNumOfEpoch) external view returns (uint256[] memory listOfStakes); + + Returns the total stake amount, at the end of one or more consensus epochs, assigned to a specific forger.
+ vrf, signKey and delegator are optional: if all are null, the total stake amount will be returned. If only + delegator is null, all the stakes assigned to the forger will be summed.
+ If vrf and signKey are null, but delegator is defined, the method will fail.
+ consensusEpochStart and maxNumOfEpoch are optional: if both null, the data at the current consensus epoch is returned. + Be aware that following convention applies when we talk about 'null' values: for bytes parameters, as addresses or keys etc., a byte array of the expected length with all 0 values is interpreted as null, eg "0x0000000000000000000000000000000000000000" for addresses.
+ For consensusEpochStart and maxNumOfEpoch, it is 0.
+ Returned array contains also elements with 0 value. Returned values are ordered by epoch, and the array length may + be < maxNumOfEpoch if the current consensus epoch is < (consensusEpochStart + maxNumOfEpoch). + + +- rewardsReceived + + function rewardsReceived(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint32 consensusEpochStart, uint32 maxNumOfEpoch) external view returns (uint256[] memory listOfRewards); + + Returns total sum paid to the forger reward_address at the end of one or more consensus epochs.
+ Returned array contains also elements with 0 value. Returned values are ordered by epoch, and the array length may + be < maxNumOfEpoch if the current consensus epoch is < (consensusEpochStart + maxNumOfEpoch -1). + +- stakeStart + + function stakeStart(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, address delegator) external view returns (int32 consensusEpochStart); + + Returns the first consensus epoch when a stake is present for a specific delegator.
+ signPubKey, vrf1, vrf2 and delegator parameters are mandatory.
+ If no stake has been found (the delegator never staked anything to this forger) the method returns -1 + +- getForger + + function getForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2) external view returns (ForgerInfo memory forgerInfo); + + Returns the info of a specific registered forger.
+ The return data structure has the following format: + + struct ForgerInfo { + bytes32 signPubKey; + bytes32 vrf1; + bytes1 vrf2; + uint32 rewardShare; + address reward_address; + } + + rewardShare is an integer value 0-1000, meaning 0 => 0% and 1000 => 100% + +- getPagedForgers + + function getPagedForgers(int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, ForgerInfo[] memory listOfForgerInfo); + + Returns the paginated list of all the registered forgers.
+ Each element of the list is the detail of a specific forger (see above method getForger for a description of the data structure).
+ nextIndex will contain the index of the next element not returned yet.
If no element is still present, next will be -1. + + +- getPagedForgersStakesByForger + + function getPagedForgersStakesByForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, StakeDataDelegator[] memory listOfDelegatorStakes); + + Returns the paginated list of stakes delegated to a specific forger, grouped by delegator address.
+ Each element of the list is the total amount delegated by a specific address. The data structure has the following format:
+ The returned array length may be less than pageSize even if there are still additional elements because stakes with 0 amount are filtered out. + + struct StakeDataDelegator { + address delegator; + uint256 stakedAmount; + } + + nextIndex will contain the index of the next element not returned yet. If no element is still present, next will be -1. + +- getPagedForgersStakesByDelegator + + function getPagedForgersStakesByDelegator(address delegator, int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, StakeDataForger[] memory listOfForgerStakes); + + Returns the paginated list of stakes delegated by a specific address, grouped by forger.
+ Each element of the list is the total amount delegated to a specific forger. The data structure has the following format:
+ The returned array length may be less than pageSize even if there are still additional elements because stakes with 0 amount are filtered out. + + + struct StakeDataForger { + bytes32 signPubKey; + bytes32 vrf1; + bytes1 vrf2; + uint256 stakedAmount; + } + + nextIndex will contain the index of the next element not returned yet. If no element is still present, next will be -1. + +- getCurrentConsensusEpoch + + function getCurrentConsensusEpoch() external view returns (uint32 epoch); + + Returns the current consensus epoch. + +- activate + + function activate() external; + + Activation function for this smart contract. Must be called only once after the hardfork. After the activation the old ForgerStake native smart contract will be de-activated. + + + + + + + + + + + + + + + diff --git a/doc/nativesc/contracts/ForgerStakesV2.sol b/doc/nativesc/contracts/ForgerStakesV2.sol new file mode 100644 index 0000000..9b55bad --- /dev/null +++ b/doc/nativesc/contracts/ForgerStakesV2.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/* + Native Contract managing forgers stakes - Version 2 (activated from EON version 1.4) + contract address: 0x0000000000000000000022222222222222222333 +*/ +interface ForgerStakesV2 { + + // Event declaration + // Up to 3 parameters can be indexed. + // Indexed parameters help you filter the logs by the indexed parameter + event RegisterForger(address indexed sender, bytes32 signPubKey, bytes32 indexed vrf1, bytes1 indexed vrf2, uint256 value, uint32 rewardShare, address reward_address); + event UpdateForger(address indexed sender, bytes32 signPubKey, bytes32 indexed vrf1, bytes1 indexed vrf2, uint32 rewardShare, address reward_address); + event DelegateForgerStake(address indexed sender, bytes32 signPubKey, bytes32 indexed vrf1, bytes1 indexed vrf2, uint256 value); + event WithdrawForgerStake(address indexed sender, bytes32 signPubKey, bytes32 indexed vrf1, bytes1 indexed vrf2, uint256 value); + event ActivateStakeV2(); + + + //Data structures + struct ForgerInfo { + bytes32 signPubKey; + bytes32 vrf1; + bytes1 vrf2; + uint32 rewardShare; + address reward_address; + } + + struct StakeDataDelegator { + address delegator; + uint256 stakedAmount; + } + + struct StakeDataForger { + bytes32 signPubKey; + bytes32 vrf1; + bytes1 vrf2; + uint256 stakedAmount; + } + + //read-write methods + + /* + Register a new forger. + rewardShare can range in [0..1000] and can be 0 if and only if rewardAddress == 0x000..00. + Vrf key and signatures are split in two or more separate parameters, being longer than 32 bytes. + sign1_x are the 25519 signature chunks and sign2_x are the Vfr signature chunks. + The message to sign is the first 31 bytes of Keccak256 hash of a string formed by the concatenation + of signPubKey+vrfKey+rewardShare+rewardAddress. rewardAddress is represented in the Eip55 + checksum format and hex strings are lowercase with no prefix. + The method accepts WEI value: the sent value will be converted to the initial stake assigned to the forger. + The initial stake amount must be >= min threshold (10 Zen) + */ + function registerForger(bytes32 signPubKey, bytes32 vrfKey1, bytes1 vrfKey2, uint32 rewardShare, + address rewardAddress, bytes32 sign1_1, bytes32 sign1_2, + bytes32 sign2_1, bytes32 sign2_2, bytes32 sign2_3, bytes1 sign2_4) external payable; + + /* + Updates an existing forger. + A forger can be updated just once and only if rewardAddress == 0x000..00 and rewardShare == 0. + See above the registerForger command for the parameters meaning. + This operation should be called only if at least 2 epochs are passed by since the 1.4 fork activation. + */ + function updateForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint32 rewardShare, + address rewardAddress, bytes32 sign1_1, bytes32 sign1_2, + bytes32 sign2_1, bytes32 sign2_2, bytes32 sign2_3, bytes1 sign2_4) external; + + /* + Delegate a stake to a previously registered forger. + Vrf key is split in two separate parameters, being longer than 32 bytes. + */ + function delegate(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2) external payable; + + /* + Withdraw (unstake) a previously assigned stake. + Vrf key is split in two separate parameters, being longer than 32 bytes. + */ + function withdraw(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint256 amount) external; + + //read only methods + + /* + Returns the total stake amount, at the end of one or more consensus epochs, assigned to a specific forger. + vrf, signKey and delegator are optional: if all are null, the total stake amount will be returned. If only + delegator is null, all the stakes assigned to the forger will be summed. + If vrf and signKey are null, but delegator is defined, the method will fail. + consensusEpochStart and maxNumOfEpoch are optional: if both null, the data at the current consensus epoch is returned. + Be aware that following convention applies when we talk about 'null' values: for bytes parameters, as addresses or keys etc., a byte array of the expected length with all 0 values is interpreted as null, eg "0x0000000000000000000000000000000000000000" for addresses. + For consensusEpochStart and maxNumOfEpoch, it is 0. + Returned array contains also elements with 0 value. Returned values are ordered by epoch, and the array length may + be < maxNumOfEpoch if the current consensus epoch is < (consensusEpochStart + maxNumOfEpoch). + */ + function stakeTotal(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, address delegator, uint32 consensusEpochStart, uint32 maxNumOfEpoch) external view returns (uint256[] memory listOfStakes); + + /* + Return total sum paid to the forger reward_address at the end of one or more consensus epochs. + Returned array contains also elements with 0 value. Returned values are ordered by epoch, and the array length may + be < maxNumOfEpoch if the current consensus epoch is < (consensusEpochStart + maxNumOfEpoch). + */ + function rewardsReceived(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, uint32 consensusEpochStart, uint32 maxNumOfEpoch) external view returns (uint256[] memory listOfRewards); + + /* + Returns the first consensus epoch when a stake is present for a specific delegator. + signPubKey, vrf1, vrf2 and delegator parameters are mandatory. + If no stake has been found (the delegator never staked anything to this forger) the method returns -1 + */ + function stakeStart(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, address delegator) external view returns (int32 consensusEpochStart); + + /* + Returns the info of a specific registered forger. + */ + function getForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2) external view returns (ForgerInfo memory forgerInfo); + + /* + Returns the paginated list of all the registered forgers. + Each element of the list is the detail of a specific forger. + nextIndex will contain the index of the next element not returned yet. If no element is still present, next will be -1. + */ + function getPagedForgers(int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, ForgerInfo[] memory listOfForgerInfo); + + /* + Returns the paginated list of stakes delegated to a specific forger, grouped by delegator address. + Each element of the list is the total amount delegated by a specific address. + nextIndex will contain the index of the next element not returned yet. If no element is still present, next will be -1. + The returned array length may be less than pageSize even if there are still additional elements because stakes with 0 amount are filtered out. + */ + function getPagedForgersStakesByForger(bytes32 signPubKey, bytes32 vrf1, bytes1 vrf2, int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, StakeDataDelegator[] memory listOfDelegatorStakes); + + /* + Returns the paginated list of stakes delegated by a specific address, grouped by forger. + Each element of the list is the total amount delegated to a specific forger. + nextIndex will contain the index of the next element not returned yet. If no element is still present, next will be -1. + The returned array length may be less than pageSize even if there are still additional elements because stakes with 0 amount are filtered out. + */ + function getPagedForgersStakesByDelegator(address delegator, int32 startIndex, int32 pageSize) external view returns (int32 nextIndex, StakeDataForger[] memory listOfForgerStakes); + + /* + / Returns the current consensus epoch. + */ + function getCurrentConsensusEpoch() external view returns (uint32 epoch); + + function activate() external; +} \ No newline at end of file diff --git a/doc/nativesc/contracts/McAddrOwnership.md b/doc/nativesc/contracts/McAddrOwnership.md index 2f3ecb2..2ad9e2d 100644 --- a/doc/nativesc/contracts/McAddrOwnership.md +++ b/doc/nativesc/contracts/McAddrOwnership.md @@ -46,7 +46,7 @@ It allows to track associations between mainchain addresses and EON addresses, i function sendMultisigKeysOwnership(string memory mcMultisigAddress, string memory redeemScript, string[] memory mcSignatures) external returns (bytes32); Associate the specified mainchain multisig address to the EON address that invoked the method. - Redeem script and signatures must be provided as well. + Redeem script and signatures must be provided as well. The message to sign is obtained by concatenating the mainchain mcMultisigAddress and the EON address (msgTosSign = mcMultiSigAddress + scAddress). The EON address must be encoded in the EIP-155 Mixed-case checksum format as above. In case of success, the returned value is the id of the new ownership record created. - removeKeysOwnership diff --git a/doc/nativesc/index.md b/doc/nativesc/index.md index a168ec6..505d565 100644 --- a/doc/nativesc/index.md +++ b/doc/nativesc/index.md @@ -6,10 +6,13 @@ They can be invoked as any standard smart contract with rpc method calls or (sta Below a detailed list: [WithdrawalRequests](/doc/nativesc/contracts/WithdrawalRequests.md) -[ForgerStakes](/doc/nativesc/contracts/ForgerStakes.md) +[ForgerStakesV2](/doc/nativesc/contracts/ForgerStakesV2.md) [McAddrOwnerships](/doc/nativesc/contracts/McAddrOwnership.md) [CertKeyRotation](/doc/nativesc/contracts/CertKeyRotation.md) +Deprecated smart contracts: + +[ForgerStakes](/doc/nativesc/contracts/ForgerStakes.md) diff --git a/doc/release/1.0.0.md b/doc/release/1.0.0.md index 08528ca..6b10bf8 100644 --- a/doc/release/1.0.0.md +++ b/doc/release/1.0.0.md @@ -2,7 +2,7 @@ --- ## Notes about new/updated Features -The 1.0.0 version has been updated to the 0.8.0 SDK version, which introduces new configuration properties for handling the peers connections: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/tree/master/doc/release/0.8.0.md) for a detailed explanation. +The 1.0.0 version has been updated to the 0.8.0 SDK version, which introduces new configuration properties for handling the peers connections: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.8.0/doc/release/0.8.0.md) for a detailed explanation. This EON version introduces a new native smart contract to support decentralized governance workflow. @@ -16,7 +16,7 @@ A delay of 6 mainchain blocks for the inclusion of the mainchain blocks referen --- ## Update from previous version instructions -Also for the update instructions please refer to the section in the [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/tree/master/doc/release/0.8.0.md) +Also for the update instructions please refer to the section in the [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.8.0/doc/release/0.8.0.md) --- Full [Changelog](/CHANGELOG.md) available. \ No newline at end of file diff --git a/doc/release/1.1.0.md b/doc/release/1.1.0.md index 02406ea..b8c8f3a 100644 --- a/doc/release/1.1.0.md +++ b/doc/release/1.1.0.md @@ -2,7 +2,7 @@ --- ## Notes about new/updated Features -The 1.1.0 version has been updated to the 0.9.0 SDK version, which introduces the support to Native<>Real smart contract interoperability: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/tree/master/doc/release/0.9.0.md) for a detailed explanation. +The 1.1.0 version has been updated to the 0.9.0 SDK version, which introduces the support to Native<>Real smart contract interoperability: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.9.0/doc/release/0.9.0.md) for a detailed explanation. --- Full [Changelog](/CHANGELOG.md) available. \ No newline at end of file diff --git a/doc/release/1.2.0.md b/doc/release/1.2.0.md index 2745afb..0892923 100644 --- a/doc/release/1.2.0.md +++ b/doc/release/1.2.0.md @@ -2,7 +2,7 @@ --- ## Notes about new/updated Features -The 1.2.0 version has been updated to the 0.10.0 SDK version, which introduces several additions: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/tree/dev/doc/release/0.10.0.md) for a detailed explanation. +The 1.2.0 version has been updated to the 0.10.0 SDK version, which introduces several additions: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.10.0/doc/release/0.10.0.md) for a detailed explanation. --- Full [Changelog](/CHANGELOG.md) available. \ No newline at end of file diff --git a/doc/release/1.3.0.md b/doc/release/1.3.0.md index b7c28b5..b16d375 100644 --- a/doc/release/1.3.0.md +++ b/doc/release/1.3.0.md @@ -2,7 +2,7 @@ --- ## Notes about new/updated Features -The 1.3.0 version has been updated to the 0.11.0 SDK version, which introduces several additions: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/tree/dev/doc/release/0.11.0.md) for a detailed explanation. +The 1.3.0 version has been updated to the 0.11.0 SDK version, which introduces several additions: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.11.0/doc/release/0.11.0.md) for a detailed explanation. ### Forger reward address See SDK release notes for an explanation of the feature. diff --git a/doc/release/1.4.0.md b/doc/release/1.4.0.md new file mode 100644 index 0000000..7abe8f0 --- /dev/null +++ b/doc/release/1.4.0.md @@ -0,0 +1,15 @@ +# Release notes - version 1.4.0 +--- + +## Notes about new/updated Features +The 1.4.0 version has been updated to the 0.12.0 SDK version, which introduces several additions: refer to [SDK release notes](https://github.com/HorizenOfficial/Sidechains-SDK/blob/0.12.0/doc/release/0.12.0.md) for a detailed explanation. + + +### Metrics endpoint +If using docker, you can configure the following env properties to expose a metric endpoint: +- SCNODE_METRICS_ENABLED: set to true to enable metric endpoint (default: false) +- SCNODE_METRICS_PORT: specify the port where the endpoint will be exposed (default: 9088) +- SCNODE_METRICS_REST_PASSWORD: if set, the endpoint will be protected and require the BCrypt hash of the specified password (same basic authentication method used in rest-API) + +--- +Full [Changelog](/CHANGELOG.md) available. diff --git a/dockerfiles/evmapp/Dockerfile b/dockerfiles/evmapp/Dockerfile index a2e84cc..68886f5 100644 --- a/dockerfiles/evmapp/Dockerfile +++ b/dockerfiles/evmapp/Dockerfile @@ -124,9 +124,8 @@ RUN set -eEuo pipefail && mv /sidechain/entrypoint.sh /usr/local/bin/entrypoint. && DEBIAN_FRONTEND=noninteractive apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ && rm -rf /var/{lib/apt/lists/*,cache/apt/archives/*.deb} /tmp/* -VOLUME ["/sidechain/datadir/"] - -VOLUME ["/sidechain/logs/"] +# Define volumes for data directory, logs, and snark keys +VOLUME ["/sidechain/datadir/", "/sidechain/logs/", "/sidechain/snark_keys/"] ENTRYPOINT ["/usr/local/bin/tini", "--", "/usr/local/bin/entrypoint.sh"] diff --git a/dockerfiles/evmapp/entrypoint.sh b/dockerfiles/evmapp/entrypoint.sh index 2db9a8f..e25a2c1 100755 --- a/dockerfiles/evmapp/entrypoint.sh +++ b/dockerfiles/evmapp/entrypoint.sh @@ -27,32 +27,37 @@ LOG4J_CUSTOM_CONFIG="" SCNODE_REMOTE_KEY_MANAGER_ENABLED="${SCNODE_REMOTE_KEY_MANAGER_ENABLED:-false}" export SCNODE_REMOTE_KEY_MANAGER_ENABLED +SCNODE_METRICS_ENABLED="${SCNODE_METRICS_ENABLED:-false}" +SCNODE_METRICS_PORT="${SCNODE_METRICS_PORT:-9088}" +export SCNODE_METRICS_ENABLED SCNODE_METRICS_PORT +SCNODE_METRICS_APIKEYHASH="" + # Function(s) +fn_die() { + echo -e "$1" >&2 + sleep 5 + exit "${2:-1}" +} + detect_ext_ip() { local usage="Detect external IPv(4|6) address - usage: ${FUNCNAME[0]} {(4|6)}}" [ "${1:-}" = "usage" ] && echo "${usage}" && return - [ "$#" -ne 1 ] && { echo -e "${FUNCNAME[0]} error: function requires exactly one argument.\n\n${usage}"; exit 1;} + [ "$#" -ne 1 ] && { fn_die "${FUNCNAME[0]} error: function requires exactly one argument.\n\n${usage}"; } local ip_type="${1}" if ! [[ "${ip_type}" =~ ^(4|6)$ ]]; then - echo -e "${FUNCNAME[0]} error: function expects either '4' or '6' as an argument.\n\n${usage}" - exit 1 + fn_die "${FUNCNAME[0]} error: function expects either '4' or '6' as an argument.\n\n${usage}" fi ip_address="$(dig -"${ip_type}" +short +time=2 @resolver1.opendns.com ANY myip.opendns.com 2> /dev/null | grep -v ";" || true)" - if [ -z "${ip_address:-}" ]; then + if ! { ipv6calc -qim "${ip_address:-}" | grep 'TYPE' | grep -q 'global'; } 2>/dev/null; then ip_address="$(curl -s -"${ip_type}" icanhazip.com 2>/dev/null || true)" fi echo "${ip_address}" } -fn_die() { - echo -e "$1" >&2 - sleep 5 - exit "${2:-1}" -} if [ "$USER_ID" != "0" ]; then @@ -81,23 +86,20 @@ fi # Checking if external IP address is provided by the user via ENV var if [ -n "${SCNODE_NET_DECLAREDADDRESS:-}" ]; then # Checking user provided public IPv(4|6) address validity - if ! ipv6calc -qim "${SCNODE_NET_DECLAREDADDRESS}" | grep 'TYPE' | grep -q 'global' &>/dev/null; then + if ! { ipv6calc -qim "${SCNODE_NET_DECLAREDADDRESS}" | grep 'TYPE' | grep -q 'global'; } 2>/dev/null; then fn_die "Error: provided via environment variable IP address = ${SCNODE_NET_DECLAREDADDRESS} does not match a valid IPv4 or IPv6 format or is NOT a PUBLIC address.\nFix it before proceeding any further. Exiting ..." - else - SCNODE_NET_DECLAREDADDRESS="$(ipv6calc -qim "${SCNODE_NET_DECLAREDADDRESS}" | grep -E 'IPV(4|6)=' | cut -d '=' -f2)" fi else # Detecting IPv4 vs IPv6 address SCNODE_NET_DECLAREDADDRESS="$(detect_ext_ip 4)" - if [ -z "${SCNODE_NET_DECLAREDADDRESS:-}" ]; then + if ! { ipv6calc -qim "${SCNODE_NET_DECLAREDADDRESS:-}" | grep 'TYPE' | grep -q 'global'; } 2>/dev/null; then SCNODE_NET_DECLAREDADDRESS="$(detect_ext_ip 6)" fi # Falling over to internal IP - if [ -z "${SCNODE_NET_DECLAREDADDRESS:-}" ]; then + if ! { ipv6calc -qim "${SCNODE_NET_DECLAREDADDRESS:-}" | grep 'TYPE' | grep -q 'global'; } 2>/dev/null; then echo "Error: Failed to detect external IPv(4|6) address, using internal address." SCNODE_NET_DECLAREDADDRESS="$(hostname -I | cut -d ' ' -f1 || true)" - if [ -n "${SCNODE_NET_DECLAREDADDRESS}" ]; then SCNODE_NET_DECLAREDADDRESS="${SCNODE_NET_DECLAREDADDRESS%% }" else @@ -267,6 +269,14 @@ if [ -n "${SCNODE_REST_PASSWORD:-}" ]; then fi export SCNODE_REST_APIKEYHASH +# set Metrics API password hash +if [ "${SCNODE_METRICS_ENABLED}" = "true" ]; then + if [ -n "${SCNODE_METRICS_REST_PASSWORD:-}" ]; then + SCNODE_METRICS_APIKEYHASH="$(echo -en "\n apiKeyHash = \"$(htpasswd -nbBC 10 "" "${SCNODE_METRICS_REST_PASSWORD}" | tr -d ':\n')\"")" + fi +fi +export SCNODE_METRICS_APIKEYHASH + # setting maxIncomingConnections if provided if [ -n "${SCNODE_NET_MAX_IN_CONNECTIONS:-}" ]; then MAX_INCOMING_CONNECTIONS="$(echo -en "\n maxIncomingConnections = ${SCNODE_NET_MAX_IN_CONNECTIONS}")" @@ -372,7 +382,7 @@ SUBST='$SCNODE_CERT_MASTERS_PUBKEYS:$SCNODE_CERT_SIGNERS_MAXPKS:$SCNODE_CERT_SIG '$SCNODE_NET_DECLAREDADDRESS:$SCNODE_NET_KNOWNPEERS:$SCNODE_NET_MAGICBYTES:$SCNODE_NET_NODENAME:$SCNODE_NET_P2P_PORT:$SCNODE_NET_API_LIMITER_ENABLED:$SCNODE_NET_SLOW_MODE:$SCNODE_NET_REBROADCAST_TXS:$SCNODE_NET_HANDLING_TXS:'\ '$SCNODE_WALLET_GENESIS_SECRETS:$SCNODE_WALLET_MAXTX_FEE:$SCNODE_WALLET_SEED:$WS_ADDRESS:$MAX_INCOMING_CONNECTIONS:$MAX_OUTGOING_CONNECTIONS:$SCNODE_WS_SERVER_PORT:'\ '$SCNODE_WS_CLIENT_ENABLED:$SCNODE_WS_SERVER_ENABLED:$SCNODE_REMOTE_KEY_MANAGER_ENABLED:$SCNODE_REMOTE_KEY_MANAGER_ADDRESS:$SCNODE_LOG_FILE_LEVEL:$SCNODE_LOG_CONSOLE_LEVEL:$SCNODE_LOG_FILE_NAME:$REMOTE_KEY_MANAGER_REQUEST_TIMEOUT:$REMOTE_KEY_MANAGER_PARALLEL_REQUESTS:'\ -'$SCNODE_REST_APIKEYHASH:$SCNODE_REST_PORT:$ONLY_CONNECT_TO_KNOWN_PEERS:$FORGER_MAXCONNECTIONS:$FORGER_REWARD_ADDRESS'\ +'$SCNODE_REST_APIKEYHASH:$SCNODE_REST_PORT:$ONLY_CONNECT_TO_KNOWN_PEERS:$FORGER_MAXCONNECTIONS:$FORGER_REWARD_ADDRESS:$SCNODE_METRICS_ENABLED:$SCNODE_METRICS_PORT:$SCNODE_METRICS_APIKEYHASH'\ export SUBST envsubst "${SUBST}" < /sidechain/config/sc_settings.conf.tmpl > /sidechain/config/sc_settings.conf diff --git a/dockerfiles/evmapp/sc_settings.conf.tmpl b/dockerfiles/evmapp/sc_settings.conf.tmpl index 8d5f9c3..ef9669b 100644 --- a/dockerfiles/evmapp/sc_settings.conf.tmpl +++ b/dockerfiles/evmapp/sc_settings.conf.tmpl @@ -13,6 +13,11 @@ sparkz { bindAddress = "0.0.0.0:$SCNODE_REST_PORT"$SCNODE_REST_APIKEYHASH timeout = 5s } + + metricsApi { + enabled = $SCNODE_METRICS_ENABLED + bindAddress = "0.0.0.0:$SCNODE_METRICS_PORT"$SCNODE_METRICS_APIKEYHASH + } network { nodeName = "$SCNODE_NET_NODENAME" diff --git a/node/pom.xml b/node/pom.xml index 934c1ae..bb1be47 100644 --- a/node/pom.xml +++ b/node/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.horizen eon - 1.3.1 + 1.4.0 2023 UTF-8 @@ -11,7 +11,7 @@ 11 3.8.1 3.1.1 - 0.11.0 + 0.12.0 diff --git a/node/src/main/java/io/horizen/eon/EonForkConfigurator.java b/node/src/main/java/io/horizen/eon/EonForkConfigurator.java index 4e6fb27..6922be6 100644 --- a/node/src/main/java/io/horizen/eon/EonForkConfigurator.java +++ b/node/src/main/java/io/horizen/eon/EonForkConfigurator.java @@ -19,6 +19,7 @@ public EonForkConfigurator(Optional sidechainId) { optionalSidechainForks.addAll(new F3Fork(sidechainId).getPairs()); optionalSidechainForks.addAll(new F4Fork(sidechainId).getPairs()); optionalSidechainForks.addAll(new F5Fork(sidechainId).getPairs()); + optionalSidechainForks.addAll(new F6Fork(sidechainId).getPairs()); mandatorySidechainFork1 = new SidechainForkConsensusEpoch(0, 0, 0); } diff --git a/node/src/main/java/io/horizen/eon/forks/F6Fork.java b/node/src/main/java/io/horizen/eon/forks/F6Fork.java new file mode 100644 index 0000000..702c892 --- /dev/null +++ b/node/src/main/java/io/horizen/eon/forks/F6Fork.java @@ -0,0 +1,52 @@ +package io.horizen.eon.forks; + +import io.horizen.account.fork.Version1_3_0Fork; +import io.horizen.account.fork.Version1_4_0Fork; +import io.horizen.fork.OptionalSidechainFork; +import io.horizen.fork.SidechainForkConsensusEpoch; +import io.horizen.utils.Pair; + +import java.util.List; +import java.util.Optional; + +/** + * EON fork 6 (introduced in Version 1.4.0) + * New on-chain delegated staking reward mechanism, new handling of rewards from mainchain + */ +public class F6Fork extends EONFork { + public F6Fork(Optional sidechainId) { + super(sidechainId); + } + + @Override + protected int getActivationRegtest() { + return 7; + } + @Override + protected int getActivationTestnetPregobi() {return 2180;} //estimated start at TUE 21 May 2024 16:31 Milano time + + @Override + protected int getActivationTestnetGobi() { + return 2274; //estimated starts at MON 10 June 2024 11:21 Milano time + } + @Override + protected int getActivationTestnet() { return 2274; //not used + } + @Override + protected int getActivationMainnet() { + return 1593; //estimated starts at THU 27 June 2024 17:55 Milano time + } + + @Override + public List> getPairs() { + return List.of( + new Pair<>( + new SidechainForkConsensusEpoch( + getActivationRegtest(), + getActivationTestnet(sidechainId), + getActivationMainnet()), + new Version1_4_0Fork(true) + ) + ); + } +} diff --git a/pom.xml b/pom.xml index ae1f129..a7e0661 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.horizen eonproject - 1.3.1 + 1.4.0 pom 2023