From 07f67126cccec7e0e21defb30852f7cc39edc543 Mon Sep 17 00:00:00 2001 From: owenwahlgren Date: Mon, 14 Oct 2024 15:48:53 -0400 Subject: [PATCH] add sections --- .../registering-validator.mdx | 130 ++++++++++++++++++ content/course/interchain-messaging/meta.json | 4 +- .../04-staking/04-register-validators.mdx | 130 ++++++++++++++++++ 3 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 content/course/interchain-messaging/13-registering-validator/registering-validator.mdx create mode 100644 content/course/l1-tokenomics/04-staking/04-register-validators.mdx diff --git a/content/course/interchain-messaging/13-registering-validator/registering-validator.mdx b/content/course/interchain-messaging/13-registering-validator/registering-validator.mdx new file mode 100644 index 00000000..0fe172cc --- /dev/null +++ b/content/course/interchain-messaging/13-registering-validator/registering-validator.mdx @@ -0,0 +1,130 @@ +--- +title: Registering Validators +description: Learn how validators are registered on the Avalanche network post-etna. +updated: 2024-10-14 +authors: [owenwahlgren] +icon: Terminal +--- + +> How does it all work? I mean *actually* work. + +### Sending Warp Messages + +On the EVM, a warp message is simply an event log with `messageID = sha256(messagePayload)` Nothing is ever actually “sent” anywhere, which trips people up when first learning how Warp Messaging works. + +The log event emitted is from the Warp precompile address (`0x0200000000000000000000000000000000000005`) The topic of the message will be `0x56600c567728a800c0aa927500f831cb451df66a7af570eb4df4dfbf4674887d` which is the output of +```bash +cast keccak "SendWarpMessage(address,bytes32,bytes)" +``` + +To find a specific warp messageID, one must know the block number or block hash to retrieve the logs, or be monitoring all events, looking for and indexing messages. + +When a block is [accepted](https://github.com/ava-labs/coreth/blob/7242ae2d235593d2bb14e2496b6ed102aab14c3d/precompile/contracts/warp/config.go#L130) by the network, a precompile hook is called, and the EVM saves the warp message to a local database. The validator node will then be willing to sign any messages in that database upon request, typically via an `AppRequest` message over the p2p network. + +### Receiving Warp Messages + +On the EVM, any smart contract can call the precompile + +which returns the warp message and verifies the attached signature. But what is `index`? Where does it get the warp message from? The answer is, you! When you send a tx to any contract that eventually calls `getVerifiedWarpMessage`, you **must** pass in the signed warp message as a “predicate”. It’s called an AccessList, and typically this is an optional field that can be used by the EVM to pre-fetch storage slots that will be accessed, for efficiency and reducing gas costs. But Warp cleverly (ab)uses this feature of the EVM to verify the signature of the warp message, and then make the warp message available during the transactions lifetime, by calling `getVerifiedWarpMessage`. You can pass multiple warp messages which is where the `index` comes in. (All of this is abstracted away by Teleporter for EVM to EVM comms) + +Details: [Warp README](https://github.com/ava-labs/coreth/blob/7242ae2d235593d2bb14e2496b6ed102aab14c3d/precompile/contracts/warp/README.md) + +### Teleporter + +[Teleporter](https://github.com/ava-labs/teleporter) is a set of smart contracts that implements a rich messaging protocol on top of the Warp Messaging primitive, allowing for verified message passing between EVM chains. + + +**Only (currently) supports EVM→EVM comms. Not used for EVM→P-Chain** + + +### Relayer + +[Relayer](https://github.com/ava-labs/awm-relayer/blob/main/relayer/README.md) is an off-chain program that listens for warp messages on a source chain, and delivers them to a destination chain. Messages can choose to be relayed by anyone, or by only privileged relayers. Messages can also pay fees to incentivize relayers and cover gas costs. + +## Registering ValidatorManager + +ACP-77 introduces L1-only Validators, that stake no AVAX, and do not validate the X/C chains. They must connect to the Primary Network and track the P-Chain, and also validate their own chain, and pay a small continuous fee in AVAX to the network. + +User submits EVM tx to smart contract [NativeTokenStakingManager.sol](https://github.com/ava-labs/teleporter/blob/acp-77-updates/contracts/validator-manager/NativeTokenStakingManager.sol) + +```solidity +function initializeValidatorRegistration( + ValidatorRegistrationInput calldata registrationInput, + uint16 delegationFeeBips, + uint64 minStakeDuration) payable returns (bytes32 validationID) +``` +Notably, this function will: + +- Lock the native tokens in the contract +- `validationID`: bytes32 hash of an encoded `RegisterSubnetValidatorMessage` +- `messageID`: bytes32 hash of the warp message +- Store some data in the contract: + + +```solidity +$._pendingRegisterValidationMessages[validationID] = registerSubnetValidatorMessage; +$._registeredValidators[input.nodeID] = validationID; +bytes32 messageID = WARP_MESSENGER.sendWarpMessage(registerSubnetValidatorMessage); +$._validationPeriods[validationID] = Validator({ + status: ValidatorStatus.PendingAdded, + nodeID: input.nodeID, + startingWeight: weight, + messageNonce: 0, + weight: weight, + startedAt: 0, // The validation period only starts once the registration is acknowledged. + endedAt: 0 +}); +``` + +- Emit `ValidationPeriodCreated(validationID, nodeID, messageID, weight, registrationExpiry)` +- The `sendWarpMessage` precompile will emit a log with `sender`, `messageID`, `message` + +At this point, we have submitted a transaction on the blockchain (C-Chain or L1, wherever the staking contract is deployed). All that has happened is we have stored some state in our contract and emitted some events. No changes to validators has occurred. + + +Nothing else will happen UNLESS an off-chain actor continues the process. + + + +In our case, someone must be running a relayer that is configured with our staking contract as the source and the P-Chain as the destination. + + +At the moment, the relayer does not support the P-Chain, so messages must be signed and sent manually. The relayer will be updated soon to support the P-Chain. + + + +So, to sign and send the message ourselves, we can use the Signature Aggregator server in the relayer project. This will use the p2p layer of the Avalanche network to connect to validators and send `AppRequest` messages to request each validator to sign a message. +```bash +curl --location 'http://localhost:8080/aggregate-signatures' \ +--json '{"message": ""}' +``` +This will give us an aggregated BLS signature from at least 67% of the stake weighted validator set. Now we need to `POST` this to the P-Chain via the `RegisterSubnetValidatorTx` tx. + +The P-Chain, if it accepts the tx, will “send” a Warp Message `SubnetValidatorRegistration` which acknowledges the change. Note that nothing is actually “sent”, instead the validator node will be willing to sign the unsigned message upon request. + +So the next step is to use the Signature Aggregator service again, and provide the unsigned `SubnetValidatorRegistration` warp message, which the service will then ask validators to sign. + + +If your Validator Manager contract is on your L1 (instead of C-Chain), as an optimization, you only need to ask 67% of the stake weighted validators of your L1, not the entire P-Chain validator set. + + + +Once an aggregate BLS signature is created, we need to submit the signed message to the C-Chain (or L1) by calling a function on the `ValidatorManager` contract. + +```solidity +function completeValidatorRegistration(uint32 messageIndex) +``` + +Remember, this involves calling the function via `eth_sendTransaction` and encoding the signed warp message into the `AccessList`. (If you encode an array of 1 warp message, then `messageIndex` would be 0) + +The function will adjust the state in the contract, including these bits: +```solidity +delete $._pendingRegisterValidationMessages[validationID]; +$._validationPeriods[validationID].status = ValidatorStatus.Active; +$._validationPeriods[validationID].startedAt = uint64(block.timestamp); +``` +And that’s it! + + +This doc is a WIP and we will eventually cover all the flows and add some diagrams. + diff --git a/content/course/interchain-messaging/meta.json b/content/course/interchain-messaging/meta.json index 02ba750e..bd5c19bf 100644 --- a/content/course/interchain-messaging/meta.json +++ b/content/course/interchain-messaging/meta.json @@ -24,6 +24,8 @@ "---Restricting-the-relayer---", "...11-restricting-the-relayer", "---Incentivizing a Relayer---", - "...12-incentivizing-a-relayer" + "...12-incentivizing-a-relayer", + "---P-Chain Messaging---", + "...13-registering-validator" ] } diff --git a/content/course/l1-tokenomics/04-staking/04-register-validators.mdx b/content/course/l1-tokenomics/04-staking/04-register-validators.mdx new file mode 100644 index 00000000..5fa76658 --- /dev/null +++ b/content/course/l1-tokenomics/04-staking/04-register-validators.mdx @@ -0,0 +1,130 @@ +--- +title: Registering PoS Validators +description: Learn how validators are registered on the Avalanche network post-etna. +updated: 2024-10-14 +authors: [owenwahlgren] +icon: Terminal +--- + +> How does it all work? I mean *actually* work. + +### Sending Warp Messages + +On the EVM, a warp message is simply an event log with `messageID = sha256(messagePayload)` Nothing is ever actually “sent” anywhere, which trips people up when first learning how Warp Messaging works. + +The log event emitted is from the Warp precompile address (`0x0200000000000000000000000000000000000005`) The topic of the message will be `0x56600c567728a800c0aa927500f831cb451df66a7af570eb4df4dfbf4674887d` which is the output of +```bash +cast keccak "SendWarpMessage(address,bytes32,bytes)" +``` + +To find a specific warp messageID, one must know the block number or block hash to retrieve the logs, or be monitoring all events, looking for and indexing messages. + +When a block is [accepted](https://github.com/ava-labs/coreth/blob/7242ae2d235593d2bb14e2496b6ed102aab14c3d/precompile/contracts/warp/config.go#L130) by the network, a precompile hook is called, and the EVM saves the warp message to a local database. The validator node will then be willing to sign any messages in that database upon request, typically via an `AppRequest` message over the p2p network. + +### Receiving Warp Messages + +On the EVM, any smart contract can call the precompile + +which returns the warp message and verifies the attached signature. But what is `index`? Where does it get the warp message from? The answer is, you! When you send a tx to any contract that eventually calls `getVerifiedWarpMessage`, you **must** pass in the signed warp message as a “predicate”. It’s called an AccessList, and typically this is an optional field that can be used by the EVM to pre-fetch storage slots that will be accessed, for efficiency and reducing gas costs. But Warp cleverly (ab)uses this feature of the EVM to verify the signature of the warp message, and then make the warp message available during the transactions lifetime, by calling `getVerifiedWarpMessage`. You can pass multiple warp messages which is where the `index` comes in. (All of this is abstracted away by Teleporter for EVM to EVM comms) + +Details: [Warp README](https://github.com/ava-labs/coreth/blob/7242ae2d235593d2bb14e2496b6ed102aab14c3d/precompile/contracts/warp/README.md) + +### Teleporter + +[Teleporter](https://github.com/ava-labs/teleporter) is a set of smart contracts that implements a rich messaging protocol on top of the Warp Messaging primitive, allowing for verified message passing between EVM chains. + + +**Only (currently) supports EVM→EVM comms. Not used for EVM→P-Chain** + + +### Relayer + +[Relayer](https://github.com/ava-labs/awm-relayer/blob/main/relayer/README.md) is an off-chain program that listens for warp messages on a source chain, and delivers them to a destination chain. Messages can choose to be relayed by anyone, or by only privileged relayers. Messages can also pay fees to incentivize relayers and cover gas costs. + +## Registering ValidatorManager + +ACP-77 introduces L1-only Validators, that stake no AVAX, and do not validate the X/C chains. They must connect to the Primary Network and track the P-Chain, and also validate their own chain, and pay a small continuous fee in AVAX to the network. + +User submits EVM tx to smart contract [NativeTokenStakingManager.sol](https://github.com/ava-labs/teleporter/blob/acp-77-updates/contracts/validator-manager/NativeTokenStakingManager.sol) + +```solidity +function initializeValidatorRegistration( + ValidatorRegistrationInput calldata registrationInput, + uint16 delegationFeeBips, + uint64 minStakeDuration) payable returns (bytes32 validationID) +``` +Notably, this function will: + +- Lock the native tokens in the contract +- `validationID`: bytes32 hash of an encoded `RegisterSubnetValidatorMessage` +- `messageID`: bytes32 hash of the warp message +- Store some data in the contract: + + +```solidity +$._pendingRegisterValidationMessages[validationID] = registerSubnetValidatorMessage; +$._registeredValidators[input.nodeID] = validationID; +bytes32 messageID = WARP_MESSENGER.sendWarpMessage(registerSubnetValidatorMessage); +$._validationPeriods[validationID] = Validator({ + status: ValidatorStatus.PendingAdded, + nodeID: input.nodeID, + startingWeight: weight, + messageNonce: 0, + weight: weight, + startedAt: 0, // The validation period only starts once the registration is acknowledged. + endedAt: 0 +}); +``` + +- Emit `ValidationPeriodCreated(validationID, nodeID, messageID, weight, registrationExpiry)` +- The `sendWarpMessage` precompile will emit a log with `sender`, `messageID`, `message` + +At this point, we have submitted a transaction on the blockchain (C-Chain or L1, wherever the staking contract is deployed). All that has happened is we have stored some state in our contract and emitted some events. No changes to validators has occurred. + + +Nothing else will happen UNLESS an off-chain actor continues the process. + + + +In our case, someone must be running a relayer that is configured with our staking contract as the source and the P-Chain as the destination. + + +At the moment, the relayer does not support the P-Chain, so messages must be signed and sent manually. The relayer will be updated soon to support the P-Chain. + + + +So, to sign and send the message ourselves, we can use the Signature Aggregator server in the relayer project. This will use the p2p layer of the Avalanche network to connect to validators and send `AppRequest` messages to request each validator to sign a message. +```bash +curl --location 'http://localhost:8080/aggregate-signatures' \ +--json '{"message": ""}' +``` +This will give us an aggregated BLS signature from at least 67% of the stake weighted validator set. Now we need to `POST` this to the P-Chain via the `RegisterSubnetValidatorTx` tx. + +The P-Chain, if it accepts the tx, will “send” a Warp Message `SubnetValidatorRegistration` which acknowledges the change. Note that nothing is actually “sent”, instead the validator node will be willing to sign the unsigned message upon request. + +So the next step is to use the Signature Aggregator service again, and provide the unsigned `SubnetValidatorRegistration` warp message, which the service will then ask validators to sign. + + +If your Validator Manager contract is on your L1 (instead of C-Chain), as an optimization, you only need to ask 67% of the stake weighted validators of your L1, not the entire P-Chain validator set. + + + +Once an aggregate BLS signature is created, we need to submit the signed message to the C-Chain (or L1) by calling a function on the `ValidatorManager` contract. + +```solidity +function completeValidatorRegistration(uint32 messageIndex) +``` + +Remember, this involves calling the function via `eth_sendTransaction` and encoding the signed warp message into the `AccessList`. (If you encode an array of 1 warp message, then `messageIndex` would be 0) + +The function will adjust the state in the contract, including these bits: +```solidity +delete $._pendingRegisterValidationMessages[validationID]; +$._validationPeriods[validationID].status = ValidatorStatus.Active; +$._validationPeriods[validationID].startedAt = uint64(block.timestamp); +``` +And that’s it! + + +This doc is a WIP and we will eventually cover all the flows and add some diagrams. +