Registering PoS Validators
Learn how validators are registered on the Avalanche network post-etna.
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
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 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
Teleporter
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 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
Notably, this function will:
- Lock the native tokens in the contract
validationID
: bytes32 hash of an encodedRegisterSubnetValidatorMessage
messageID
: bytes32 hash of the warp message- Store some data in the contract:
- Emit
ValidationPeriodCreated(validationID, nodeID, messageID, weight, registrationExpiry)
- The
sendWarpMessage
precompile will emit a log withsender
,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.
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.
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:
And that’s it!
This doc is a WIP and we will eventually cover all the flows and add some diagrams.