From bf6bface0499a3ccbae0e8caff7ef2aa9dc9bf5f Mon Sep 17 00:00:00 2001 From: meaghanfitzgerald Date: Tue, 15 Oct 2024 11:51:27 -0300 Subject: [PATCH 1/3] rename txns --- ACPs/77-reinventing-subnets/README.md | 271 +++++++++++++------------- 1 file changed, 138 insertions(+), 133 deletions(-) diff --git a/ACPs/77-reinventing-subnets/README.md b/ACPs/77-reinventing-subnets/README.md index 2a05c65..8f8c14b 100644 --- a/ACPs/77-reinventing-subnets/README.md +++ b/ACPs/77-reinventing-subnets/README.md @@ -1,10 +1,10 @@ -| ACP | 77 | -| :--- | :--- | -| **Title** | Reinventing Subnets | -| **Author(s)** | Dhruba Basu ([@dhrubabasu](https://github.com/dhrubabasu)) | -| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/78)) | -| **Track** | Standards | -| **Replaces** | [ACP-13](../13-subnet-only-validators/README.md) | +| ACP | 77 | +| :------------ | :----------------------------------------------------------------------------------- | +| **Title** | Reinventing Subnets | +| **Author(s)** | Dhruba Basu ([@dhrubabasu](https://github.com/dhrubabasu)) | +| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/78)) | +| **Track** | Standards | +| **Replaces** | [ACP-13](../13-subnet-only-validators/README.md) | ## Abstract @@ -16,6 +16,8 @@ Overhaul Subnet creation and management to unlock increased flexibility for Subn This ACP supersedes [ACP-13](../13-subnet-only-validators/README.md) and borrows some of its language. +See the [Open Questions](#open-questions) section for info on the renaming of Subnets -> Avalanche Layer 1s. + ## Motivation Each node operator must stake at least 2000 $AVAX ($70k at time of writing) to first become a Primary Network Validator before they qualify to become a Subnet Validator. Most Subnets aim to launch with at least 8 Subnet Validators, which requires staking 16000 $AVAX ($560k at time of writing). All Subnet Validators, to satisfy their role as Primary Network Validators, must also [allocate 8 AWS vCPU, 16 GB RAM, and 1 TB storage](https://github.com/ava-labs/avalanchego/blob/master/README.md#installation) to sync the entire Primary Network (X-Chain, P-Chain, and C-Chain) and participate in its consensus, in addition to whatever resources are required for each Subnet they are validating. @@ -43,8 +45,8 @@ For messages produced by the P-Chain for a given Subnet, only that Subnet's vali The following Warp message payloads are introduced on the P-Chain: - `SubnetConversionMessage` -- `RegisterSubnetValidatorMessage` -- `SubnetValidatorRegistrationMessage` +- `RegisterL1ValidatorMessage` +- `L1ValidatorRegistrationMessage` - `SubnetValidatorWeightMessage` The method of requesting signatures for these messages is left unspecified. A viable option for supporting this functionality is laid out in [ACP-118](../118-warp-signature-request/README.md) with the `SignatureRequest` message. @@ -59,24 +61,23 @@ The P-Chain can produce a `SubnetConversionMessage` for consumers (i.e. validato The following serialization is defined as a `ValidatorData`: -| Field | Type | Size | -| -: | -: | -: | -| `nodeID` | `[]byte` | 4 + len(`nodeID`) bytes | -| `blsPublicKey` | `[48]byte` | 48 bytes | -| `weight` | `uint64` | 8 bytes | -| | | 60 + len(`nodeID`) bytes | - +| Field | Type | Size | +| -------------: | ---------: | -----------------------: | +| `nodeID` | `[]byte` | 4 + len(`nodeID`) bytes | +| `blsPublicKey` | `[48]byte` | 48 bytes | +| `weight` | `uint64` | 8 bytes | +| | | 60 + len(`nodeID`) bytes | The following serialization is defined as the `SubnetConversionData`: -| Field | Type | Size | -| -: | -: | -: | -| `codecID` | `uint16` | 2 bytes | -| `subnetID` | `[32]byte` | 32 bytes | -| `managerChainID` | `[32]byte` | 32 bytes | -| `managerAddress` | `[]byte` | 4 + len(`managerAddress`) bytes | -| `validators` | `[]ValidatorData` | 4 + sum(`validatorLengths`) bytes | -| | | 74 + len(`managerAddress`) + len(`validatorLengths`) bytes | +| Field | Type | Size | +| ---------------: | ----------------: | ---------------------------------------------------------: | +| `codecID` | `uint16` | 2 bytes | +| `subnetID` | `[32]byte` | 32 bytes | +| `managerChainID` | `[32]byte` | 32 bytes | +| `managerAddress` | `[]byte` | 4 + len(`managerAddress`) bytes | +| `validators` | `[]ValidatorData` | 4 + sum(`validatorLengths`) bytes | +| | | 74 + len(`managerAddress`) + len(`validatorLengths`) bytes | - `codecID` is the codec version used to serialize the payload, and is hardcoded to `0x0000` - `sum(validatorLengths)` is the sum of the lengths of `ValidatorData` serializations included in `validators`. @@ -86,70 +87,70 @@ The following serialization is defined as the `SubnetConversionData`: The `SubnetConversionMessage` is specified as an `AddressedCall` with `sourceChainID` set to the P-Chain ID, the `sourceAddress` set to an empty byte array, and a payload of: -| Field | Type | Size | -| -: | -: | -: | -| `codecID` | `uint16` | 2 bytes | -| `typeID` | `uint32` | 4 bytes | +| Field | Type | Size | +| -------------------: | ---------: | -------: | +| `codecID` | `uint16` | 2 bytes | +| `typeID` | `uint32` | 4 bytes | | `subnetConversionID` | `[32]byte` | 32 bytes | -| | | 38 bytes | +| | | 38 bytes | - `codecID` is the codec version used to serialize the payload, and is hardcoded to `0x0000` - `typeID` is the payload type identifier and is `0x00000000` for this message -- `subnetConversionID` is the SHA256 hash of the `SubnetConversionData` from a given `ConvertSubnetTx` +- `subnetConversionID` is the SHA256 hash of the `SubnetConversionData` from a given `ConvertL1Tx` -#### `RegisterSubnetValidatorMessage` +#### `RegisterL1ValidatorMessage` -The P-Chain can consume a `RegisterSubnetValidatorMessage` from validator managers through a `RegisterSubnetValidatorTx` to register an addition to the Subnet's validator set. +The P-Chain can consume a `RegisterL1ValidatorMessage` from validator managers through a `RegisterL1ValidatorTx` to register an addition to the Subnet's validator set. The following is the serialization of a `PChainOwner`: -| Field | Type | Size | -| -: | -: | -: | -| `threshold` | `uint32` | 4 bytes | -| `addresses` | `[][20]byte` | 4 + len(`addresses`) * 20 bytes | -| | | 8 + len(`addresses`) * 20 bytes | +| Field | Type | Size | +| ----------: | -----------: | -------------------------------: | +| `threshold` | `uint32` | 4 bytes | +| `addresses` | `[][20]byte` | 4 + len(`addresses`) \* 20 bytes | +| | | 8 + len(`addresses`) \* 20 bytes | - `threshold` is the number of `addresses` that must provide a signature for the `PChainOwner` to authorize an action. - Validation criteria: - - If `threshold` is `0`, `addresses` must be empty - - `threshold` <= len(`addresses`) - - Entries of `addresses` must be unique and sorted in ascending order - -The `RegisterSubnetValidatorMessage` is specified as an `AddressedCall` with a payload of: - -| Field | Type | Size | -| -: | -: | -: | -| `codecID` | `uint16` | 2 bytes | -| `typeID` | `uint32` | 4 bytes | -| `subnetID` | `[32]byte` | 32 bytes | -| `nodeID` | `[]byte` | 4 + len(`nodeID`) bytes | -| `blsPublicKey` | `[48]byte` | 48 bytes | -| `expiry` | `uint64` | 8 bytes | -| `remainingBalanceOwner` | `PChainOwner` | 8 + len(`addresses`) * 20 bytes | -| `disableOwner` | `PChainOwner` | 8 + len(`addresses`) * 20 bytes | -| `weight` | `uint64` | 8 bytes | -| | | 122 + len(`nodeID`) + (len(`addresses1`) + len(`addresses2`)) * 20 bytes | + - If `threshold` is `0`, `addresses` must be empty + - `threshold` <= len(`addresses`) + - Entries of `addresses` must be unique and sorted in ascending order + +The `RegisterL1ValidatorMessage` is specified as an `AddressedCall` with a payload of: + +| Field | Type | Size | +| ----------------------: | ------------: | ------------------------------------------------------------------------: | +| `codecID` | `uint16` | 2 bytes | +| `typeID` | `uint32` | 4 bytes | +| `subnetID` | `[32]byte` | 32 bytes | +| `nodeID` | `[]byte` | 4 + len(`nodeID`) bytes | +| `blsPublicKey` | `[48]byte` | 48 bytes | +| `expiry` | `uint64` | 8 bytes | +| `remainingBalanceOwner` | `PChainOwner` | 8 + len(`addresses`) \* 20 bytes | +| `disableOwner` | `PChainOwner` | 8 + len(`addresses`) \* 20 bytes | +| `weight` | `uint64` | 8 bytes | +| | | 122 + len(`nodeID`) + (len(`addresses1`) + len(`addresses2`)) \* 20 bytes | - `codecID` is the codec version used to serialize the payload, and is hardcoded to `0x0000` - `typeID` is the payload type identifier and is `0x00000001` for this payload - `subnetID`, `nodeID`, `weight`, and `blsPublicKey` are for the Subnet Validator being added - `expiry` is the time at which this message becomes invalid. As of a P-Chain timestamp `>= expiry`, this Avalanche Warp Message can no longer be used to add the `nodeID` to the validator set of `subnetID` - `remainingBalanceOwner` is the P-Chain owner where leftover $AVAX from the Subnet Validator's Balance will be issued to when this validator it is removed from the validator set. -- `disableOwner` is the only P-Chain owner allowed to disable the validator using `DisableSubnetValidatorTx`, specified below. +- `disableOwner` is the only P-Chain owner allowed to disable the validator using `DisableL1ValidatorTx`, specified below. -#### `SubnetValidatorRegistrationMessage` +#### `L1ValidatorRegistrationMessage` -The P-Chain can produce a `SubnetValidatorRegistrationMessage` for consumers to verify that a validation period has either begun or has been invalidated. +The P-Chain can produce a `L1ValidatorRegistrationMessage` for consumers to verify that a validation period has either begun or has been invalidated. -The `SubnetValidatorRegistrationMessage` is specified as an `AddressedCall` with `sourceChainID` set to the P-Chain ID, the `sourceAddress` set to an empty byte array, and a payload of: +The `L1ValidatorRegistrationMessage` is specified as an `AddressedCall` with `sourceChainID` set to the P-Chain ID, the `sourceAddress` set to an empty byte array, and a payload of: -| Field | Type | Size | -| -: | -: | -: | -| `codecID` | `uint16` | 2 bytes | -| `typeID` | `uint32` | 4 bytes | +| Field | Type | Size | +| -------------: | ---------: | -------: | +| `codecID` | `uint16` | 2 bytes | +| `typeID` | `uint32` | 4 bytes | | `validationID` | `[32]byte` | 32 bytes | -| `registered` | `bool` | 1 byte | -| | | 39 bytes | +| `registered` | `bool` | 1 byte | +| | | 39 bytes | - `codecID` is the codec version used to serialize the payload, and is hardcoded to `0x0000` - `typeID` is the payload type identifier and is `0x00000002` for this message @@ -158,18 +159,18 @@ The `SubnetValidatorRegistrationMessage` is specified as an `AddressedCall` with #### `SubnetValidatorWeightMessage` -The P-Chain can consume a `SubnetValidatorWeightMessage` through a `SetSubnetValidatorWeightTx` to update the weight of an existing validator. The P-Chain can also produce a `SubnetValidatorWeightMessage` for consumers to verify that the validator weight update has been effectuated. +The P-Chain can consume a `SubnetValidatorWeightMessage` through a `SetL1ValidatorWeightTx` to update the weight of an existing validator. The P-Chain can also produce a `SubnetValidatorWeightMessage` for consumers to verify that the validator weight update has been effectuated. The `SubnetValidatorWeightMessage` is specified as an `AddressedCall` with the following payload. When sent from the P-Chain, the `sourceChainID` is set to the P-Chain ID, and the `sourceAddress` is set to an empty byte array. -| Field | Type | Size | -| -: | -: | -: | -| `codecID` | `uint16` | 2 bytes | -| `typeID` | `uint32` | 4 bytes | +| Field | Type | Size | +| -------------: | ---------: | -------: | +| `codecID` | `uint16` | 2 bytes | +| `typeID` | `uint32` | 4 bytes | | `validationID` | `[32]byte` | 32 bytes | -| `nonce` | `uint64` | 8 bytes | -| `weight` | `uint64` | 8 bytes | -| | | 54 bytes | +| `nonce` | `uint64` | 8 bytes | +| `weight` | `uint64` | 8 bytes | +| | | 54 bytes | - `codecID` is the codec version used to serialize the payload, and is hardcoded to `0x0000` - `typeID` is the payload type identifier and is `0x00000003` for this message @@ -179,24 +180,26 @@ The `SubnetValidatorWeightMessage` is specified as an `AddressedCall` with the f ### New P-Chain Transaction Types -Both before and after this ACP, to create a Subnet, a `CreateSubnetTx` must be issued on the P-Chain. This transaction includes an `Owner` field which defines the key that today can be used to authorize any validator set additions (`AddSubnetValidatorTx`) or removals (`RemoveSubnetValidatorTx`). +Both before and after this ACP, to create a Subnet, a `CreateSubnetTx` must be issued on the P-Chain. This transaction includes an `Owner` field which defines the key that today can be used to authorize any validator set additions (`AddSubnetValidatorTx`) or removals (`RemoveSubnetValidatorTx`). To be a Permissionless Subnet: + - This `Owner` key must no longer have the ability to modify the Subnet's validator set - New transaction types must support modification of a Subnet's validator set via Warp messages. The following new transaction types are introduced on the P-Chain to support this functionality. -- `ConvertSubnetTx` -- `RegisterSubnetValidatorTx` -- `SetSubnetValidatorWeightTx` -- `DisableSubnetValidatorTx` -- `IncreaseSubnetValidatorBalanceTx` -#### `ConvertSubnetTx` +- `ConvertL1Tx` +- `RegisterL1ValidatorTx` +- `SetL1ValidatorWeightTx` +- `DisableL1ValidatorTx` +- `IncreaseL1ValidatorBalanceTx` + +#### `ConvertL1Tx` -To convert a Subnet from permissioned to permissionless, a `ConvertSubnetTx` must be issued to set the `(chainID, address)` pair that will manage the Subnet's validator set going forward. The `Owner` key defined in `CreateSubnetTx` must provide a signature to authorize this conversion. +To convert a Subnet from permissioned to permissionless, a `ConvertL1Tx` must be issued to set the `(chainID, address)` pair that will manage the Subnet's validator set going forward. The `Owner` key defined in `CreateSubnetTx` must provide a signature to authorize this conversion. -The `ConvertSubnetTx` specification is: +The `ConvertL1Tx` specification is: ```golang type PChainOwner struct { @@ -230,7 +233,7 @@ type SubnetValidator struct { DisableOwner PChainOwner `json:"disableOwner"` } -type ConvertSubnetTx struct { +type ConvertL1Tx struct { // Metadata, inputs and outputs BaseTx // ID of the Subnet to transform @@ -248,18 +251,18 @@ type ConvertSubnetTx struct { } ``` -After this transaction is accepted, `CreateChainTx` and `AddSubnetValidatorTx` are disabled on the Subnet. The only action that the `Owner` key is able to take is removing Subnet validators that were added using `AddSubnetValidatorTx` previously via `RemoveSubnetValidatorTx`. Unless removed by the `Owner` key, any Subnet Validators added previously with an `AddSubnetValidatorTx` will continue to validate the Subnet until their [`End`](https://github.com/ava-labs/avalanchego/blob/a1721541754f8ee23502b456af86fea8c766352a/vms/platformvm/txs/validator.go#L27) time is reached. Once all Subnet Validators added with `AddSubnetValidatorTx` are no longer in the validator set, the `Owner` key is powerless. `RegisterSubnetValidatorTx` and `SetSubnetValidatorWeightTx` must be used to manage the Subnet's validator set going forward. +After this transaction is accepted, `CreateChainTx` and `AddSubnetValidatorTx` are disabled on the Subnet. The only action that the `Owner` key is able to take is removing Subnet validators that were added using `AddSubnetValidatorTx` previously via `RemoveSubnetValidatorTx`. Unless removed by the `Owner` key, any Subnet Validators added previously with an `AddSubnetValidatorTx` will continue to validate the Subnet until their [`End`](https://github.com/ava-labs/avalanchego/blob/a1721541754f8ee23502b456af86fea8c766352a/vms/platformvm/txs/validator.go#L27) time is reached. Once all Subnet Validators added with `AddSubnetValidatorTx` are no longer in the validator set, the `Owner` key is powerless. `RegisterL1ValidatorTx` and `SetL1ValidatorWeightTx` must be used to manage the Subnet's validator set going forward. -The `validationID` for validators added through `ConvertSubnetTx` is defined as the SHA256 hash of the 36 bytes resulting from concatenating the 32 byte `subnetID` with the 4 byte `validatorIndex` (index in the `Validators` array within the transaction). +The `validationID` for validators added through `ConvertL1Tx` is defined as the SHA256 hash of the 36 bytes resulting from concatenating the 32 byte `subnetID` with the 4 byte `validatorIndex` (index in the `Validators` array within the transaction). -Once this transaction is accepted, the P-Chain must be willing sign a `SubnetConversionMessage` with a `subnetConversionID` corresponding to `SubnetConversionData` populated with the values from this transaction. +Once this transaction is accepted, the P-Chain must be willing sign a `SubnetConversionMessage` with a `subnetConversionID` corresponding to `SubnetConversionData` populated with the values from this transaction. -#### `RegisterSubnetValidatorTx` +#### `RegisterL1ValidatorTx` -After a `ConvertSubnetTx` has been accepted for a Subnet, new validators for the Subnet must be added by using a `RegisterSubnetValidatorTx`. The specification of this transaction is: +After a `ConvertL1Tx` has been accepted for a Subnet, new validators for the Subnet must be added by using a `RegisterL1ValidatorTx`. The specification of this transaction is: ```golang -type RegisterSubnetValidatorTx struct { +type RegisterL1ValidatorTx struct { // Metadata, inputs and outputs BaseTx // Balance <= sum($AVAX inputs) - sum($AVAX outputs) - TxFee. @@ -270,16 +273,16 @@ type RegisterSubnetValidatorTx struct { // This means that validators can share a key if they so choose. // However, a NodeID does uniquely map to a BLS key Signer [96]byte `json:"signer"` - // A RegisterSubnetValidatorMessage payload + // A RegisterL1ValidatorMessage payload Message warp.Message `json:"message"` } ``` -The `validationID` of validators added via `RegisterSubnetValidatorTx` is defined as the SHA256 hash of the `Payload` of the `AddressedCall` in `Message`. +The `validationID` of validators added via `RegisterL1ValidatorTx` is defined as the SHA256 hash of the `Payload` of the `AddressedCall` in `Message`. -When a `RegisterSubnetValidatorTx` is accepted on the P-Chain, the Subnet Validator is added to the Subnet's validator set. A `minNonce` field corresponding to the `validationID` will be stored on addition to the validator set (initially set to `0`). This field will be used when validating the `SetSubnetValidatorWeightTx` defined below. +When a `RegisterL1ValidatorTx` is accepted on the P-Chain, the Subnet Validator is added to the Subnet's validator set. A `minNonce` field corresponding to the `validationID` will be stored on addition to the validator set (initially set to `0`). This field will be used when validating the `SetL1ValidatorWeightTx` defined below. -This `validationID` will be used for replay protection. Used `validationID`s will be stored on the P-Chain. If a `RegisterSubnetValidatorTx`'s `validationID` has already been used, the transaction will be considered invalid. To prevent storing an unbounded number of `validationID`s, the `expiry` of the `RegisterSubnetValidatorMessage` is required to be no more than 48 hours in the future of the time the transaction is issued on the P-Chain. Any `validationIDs` corresponding to an expired timestamp can be flushed from the P-Chain's state. +This `validationID` will be used for replay protection. Used `validationID`s will be stored on the P-Chain. If a `RegisterL1ValidatorTx`'s `validationID` has already been used, the transaction will be considered invalid. To prevent storing an unbounded number of `validationID`s, the `expiry` of the `RegisterL1ValidatorMessage` is required to be no more than 48 hours in the future of the time the transaction is issued on the P-Chain. Any `validationIDs` corresponding to an expired timestamp can be flushed from the P-Chain's state. Subnets are responsible for defining the procedure on how to retrieve the above information from prospective validators. @@ -289,18 +292,18 @@ An EVM Subnet may choose to implement this step like so: - Require the user to submit an on-chain transaction with their validator information - Generate the Warp message -For a `RegisterSubnetValidatorTx` to be valid, `Signer` must be a valid proof-of-possession of the `blsPublicKey` defined in the `RegisterSubnetValidatorMessage` contained in the transaction. +For a `RegisterL1ValidatorTx` to be valid, `Signer` must be a valid proof-of-possession of the `blsPublicKey` defined in the `RegisterL1ValidatorMessage` contained in the transaction. -After a `RegisterSubnetValidatorTx` is accepted, the P-Chain must be willing to sign a `SubnetValidatorRegistrationMessage` for the given `validationID` with `registered` set to `true`. This remains the case until the time at which the Subnet Validator is removed from the validator set using a `SetSubnetValidatorWeightTx`, as described below. +After a `RegisterL1ValidatorTx` is accepted, the P-Chain must be willing to sign a `L1ValidatorRegistrationMessage` for the given `validationID` with `registered` set to `true`. This remains the case until the time at which the Subnet Validator is removed from the validator set using a `SetL1ValidatorWeightTx`, as described below. -When it is known that a given `validationID` *is not and never will be* an existing validation period for a Subnet, the P-Chain must be willing to sign a `SubnetValidatorRegistrationMessage` for the `validationID` with `registered` set to `false`. This could be the case if the `expiry` time of the message has passed prior to the message being delivered in a `RegisterSubnetValidatorTx`, or if the Subnet Validator was successfully registered and then later removed. This enables the P-Chain to prove to validator managers that a validator has been removed or never added. The P-Chain must refuse to sign any `SubnetValidatorRegistrationMessage` where the `validationID` does not correspond to an active validator and the `expiry` is in the future. +When it is known that a given `validationID` _is not and never will be_ an existing validation period for a Subnet, the P-Chain must be willing to sign a `L1ValidatorRegistrationMessage` for the `validationID` with `registered` set to `false`. This could be the case if the `expiry` time of the message has passed prior to the message being delivered in a `RegisterL1ValidatorTx`, or if the Subnet Validator was successfully registered and then later removed. This enables the P-Chain to prove to validator managers that a validator has been removed or never added. The P-Chain must refuse to sign any `L1ValidatorRegistrationMessage` where the `validationID` does not correspond to an active validator and the `expiry` is in the future. -#### `SetSubnetValidatorWeightTx` +#### `SetL1ValidatorWeightTx` -`SetSubnetValidatorWeightTx` is used to modify the voting weight of a Subnet Validator. The specification of this transaction is: +`SetL1ValidatorWeightTx` is used to modify the voting weight of a Subnet Validator. The specification of this transaction is: ```golang -type SetSubnetValidatorWeightTx struct { +type SetL1ValidatorWeightTx struct { // Metadata, inputs and outputs BaseTx // A SubnetValidatorWeightMessage payload @@ -316,22 +319,23 @@ Applications of this transaction could include: - Remove an inactive Subnet Validator The validation criteria for `SubnetValidatorWeightMessage` is: + - `nonce >= minNonce`. Note that `nonce` is not required to be incremented by `1` with each successive validator weight update. - When `minNonce == MaxUint64`, `nonce` must be `MaxUint64` and `weight` must be `0`. This prevents Subnets from being unable to remove `nodeID` in a subsequent transaction. -- If `weight == 0`, the Subnet Validator being removed must not be the last one in the set. If all Subnet Validators are removed, there are no valid Warp messages that can be produced to register new Subnet Validators through `RegisterSubnetValidatorMessage`. With no validators, block production will halt and the Subnet is unrecoverable. This validation criteria serves as a guardrail against this situation. A future ACP can remove this guardrail as users get more familiar with the new Subnet mechanics and tooling matures to fork a Subnet. +- If `weight == 0`, the Subnet Validator being removed must not be the last one in the set. If all Subnet Validators are removed, there are no valid Warp messages that can be produced to register new Subnet Validators through `RegisterL1ValidatorMessage`. With no validators, block production will halt and the Subnet is unrecoverable. This validation criteria serves as a guardrail against this situation. A future ACP can remove this guardrail as users get more familiar with the new Subnet mechanics and tooling matures to fork a Subnet. When `weight != 0`, the weight of the Subnet Validator is updated to `weight` and `minNonce` is updated to `nonce + 1`. -When `weight == 0`, the Subnet Validator is removed from the validator set. All state related to the Subnet Validator, including the `minNonce` and `validationID`, are reaped from the P-Chain state. Tracking these post-removal is not required since `validationID` can never be re-initialized due to the replay protection provided by `expiry` in `RegisterSubnetValidatorTx`. Any unspent $AVAX in the Subnet Validator's `Balance` will be issued in a single UTXO to the `RemainingBalanceOwner` for this validator. Recall that `RemainingBalanceOwner` is specified when the validator is first added to the Subnet's validator set (in either `ConvertSubnetTx` or `RegisterSubnetValidatorTx`). +When `weight == 0`, the Subnet Validator is removed from the validator set. All state related to the Subnet Validator, including the `minNonce` and `validationID`, are reaped from the P-Chain state. Tracking these post-removal is not required since `validationID` can never be re-initialized due to the replay protection provided by `expiry` in `RegisterL1ValidatorTx`. Any unspent $AVAX in the Subnet Validator's `Balance` will be issued in a single UTXO to the `RemainingBalanceOwner` for this validator. Recall that `RemainingBalanceOwner` is specified when the validator is first added to the Subnet's validator set (in either `ConvertL1Tx` or `RegisterL1ValidatorTx`). -Note: There is no explicit `EndTime` for Subnet validators added in a `ConvertSubnetTx` or `RegisterSubnetValidatorTx`. The only time when Subnet validators are removed from the Subnet's validator set is through this transaction when `weight == 0`. +Note: There is no explicit `EndTime` for Subnet validators added in a `ConvertL1Tx` or `RegisterL1ValidatorTx`. The only time when Subnet validators are removed from the Subnet's validator set is through this transaction when `weight == 0`. -#### `DisableSubnetValidatorTx` +#### `DisableL1ValidatorTx` -Subnet Validators can use `DisableSubnetValidatorTx` to mark their validator as inactive. The specification of this transaction is: +Subnet Validators can use `DisableL1ValidatorTx` to mark their validator as inactive. The specification of this transaction is: ```golang -type DisableSubnetValidatorTx struct { +type DisableL1ValidatorTx struct { // Metadata, inputs and outputs BaseTx // ID corresponding to the validator @@ -341,20 +345,20 @@ type DisableSubnetValidatorTx struct { } ``` -The `DisableOwner` specified for this validator must sign the transaction. Any unspent $AVAX in the Subnet Validator's `Balance` will be issued in a single UTXO to the `RemainingBalanceOwner` for this validator. Recall that both `DisableOwner` and `RemainingBalanceOwner` are specified when the validator is first added to the Subnet's validator set (in either `ConvertSubnetTx` or `RegisterSubnetValidatorTx`). +The `DisableOwner` specified for this validator must sign the transaction. Any unspent $AVAX in the Subnet Validator's `Balance` will be issued in a single UTXO to the `RemainingBalanceOwner` for this validator. Recall that both `DisableOwner` and `RemainingBalanceOwner` are specified when the validator is first added to the Subnet's validator set (in either `ConvertL1Tx` or `RegisterL1ValidatorTx`). -For full removal from a Subnet's validator set, a `SetSubnetValidatorWeightTx` must be issued with weight `0`. To do so, a Warp message is required from the Subnet's manager. However, to support the ability to claim the unspent `Balance` for a Subnet Validator without authorization is critical for failed Subnets. +For full removal from a Subnet's validator set, a `SetL1ValidatorWeightTx` must be issued with weight `0`. To do so, a Warp message is required from the Subnet's manager. However, to support the ability to claim the unspent `Balance` for a Subnet Validator without authorization is critical for failed Subnets. -Note that this does not modify a Subnet's total staking weight. This transaction marks the Subnet Validator as inactive, but does not remove it from the Subnet's validator set. Inactive Subnet Validators can re-activate at any time by increasing their balance with an `IncreaseSubnetValidatorBalanceTx`. +Note that this does not modify a Subnet's total staking weight. This transaction marks the Subnet Validator as inactive, but does not remove it from the Subnet's validator set. Inactive Subnet Validators can re-activate at any time by increasing their balance with an `IncreaseL1ValidatorBalanceTx`. Subnet creators should be aware that there is no notion of `MinStakeDuration` that is enforced by the P-Chain. It is expected that Subnets who choose to enforce a `MinStakeDuration` will lock the validator's Stake for the Subnet's desired `MinStakeDuration`. -#### `IncreaseSubnetValidatorBalanceTx` +#### `IncreaseL1ValidatorBalanceTx` -Subnet Validators are required to maintain a non-zero balance used to pay the continuous fee on the P-Chain in order to be considered active. The `IncreaseSubnetValidatorBalanceTx` can be used by anybody to add additional $AVAX to the `Balance` to a Subnet Validator. The specification of this transaction is: +Subnet Validators are required to maintain a non-zero balance used to pay the continuous fee on the P-Chain in order to be considered active. The `IncreaseL1ValidatorBalanceTx` can be used by anybody to add additional $AVAX to the `Balance` to a Subnet Validator. The specification of this transaction is: ```golang -type IncreaseSubnetValidatorBalanceTx struct { +type IncreaseL1ValidatorBalanceTx struct { // Metadata, inputs and outputs BaseTx // ID corresponding to the validator @@ -364,9 +368,9 @@ type IncreaseSubnetValidatorBalanceTx struct { } ``` -If the Subnet Validator corresponding to `ValidationID` is currently inactive (`Balance` was exhausted or `DisableSubnetValidatorTx` was issued), this transaction will move them back to the active validator set. +If the Subnet Validator corresponding to `ValidationID` is currently inactive (`Balance` was exhausted or `DisableL1ValidatorTx` was issued), this transaction will move them back to the active validator set. -Note: The $AVAX added to `Balance` can be claimed at any time by the Subnet Validator using `DisableSubnetValidatorTx`. +Note: The $AVAX added to `Balance` can be claimed at any time by the Subnet Validator using `DisableL1ValidatorTx`. ### Bootstrapping Subnet Nodes @@ -382,7 +386,7 @@ By separating Subnet Validators from Primary Network Validators, a list of valid After this ACP is activated, the P-Chain will no longer support staking of any assets other than $AVAX for the Primary Network. The P-Chain will no longer support distribution of staking rewards for Subnets. All staking-related operations for Subnet Validation must be managed by the Subnet's validator manager. The P-Chain simply requires a continuous fee per Subnet Validator. If a Subnet would like to manage their Validator's balances on the P-Chain, it can cover the cost for all Subnet Validators by posting the $AVAX balance on the P-Chain. Subnets can implement any mechanism they want to pay the continuous fee charged by the P-Chain for its participants. -By moving ownership of the Subnet's validator set from the P-Chain to the Subnet, Subnet creators have no restrictions on what requirements they have to join their Subnet as a validator. Any stake that is required to join the Subnet's validator set is locked on the Subnet. If a validator is removed from the Subnet's validator set via a `SetSubnetValidatorWeightTx` with weight `0`, the stake on the Subnet will continue to be locked. How each Subnet handles stake associated with the Subnet Validator is entirely left up to the Subnet and can be treated independently to what happens on the P-Chain. +By moving ownership of the Subnet's validator set from the P-Chain to the Subnet, Subnet creators have no restrictions on what requirements they have to join their Subnet as a validator. Any stake that is required to join the Subnet's validator set is locked on the Subnet. If a validator is removed from the Subnet's validator set via a `SetL1ValidatorWeightTx` with weight `0`, the stake on the Subnet will continue to be locked. How each Subnet handles stake associated with the Subnet Validator is entirely left up to the Subnet and can be treated independently to what happens on the P-Chain. This new relationship between the P-Chain and Subnets provides a dynamic where Subnets can use the P-Chain as an impartial judge to modify parameters (in addition to its existing role of helping to validate incoming Avalanche Warp Messages). If a Validator is misbehaving, the Subnet Validators can collectively generate a BLS multisig to reduce the voting weight of a misbehaving validator. This operation is fully secured by the Avalanche Primary Network (225M $AVAX or $8.325B at the time of writing). @@ -392,7 +396,7 @@ Follow-up ACPs could extend the P-Chain <> Subnet relationship to include parame Every additional Subnet Validator on the P-Chain adds persistent load to the Avalanche Network. When a validator transaction is issued on the P-Chain, it is charged for the computational cost of the transaction itself but is not charged for the cost of an active Subnet Validator over the time they are validating on the network (which may be indefinitely). This is a common problem in blockchains, spawning many state rent proposals in the broader blockchain space to address it. The following fee mechanism takes advantage of the fact that each Subnet Validator uses the same amount of computation and charges each Subnet Validator the dynamic base fee for every discrete unit of time it is active. -To charge each Subnet Validator, the notion of a `Balance` is introduced. The `Balance` of a Subnet Validator will be continuously charged during the time they are active to cover the cost of storing the associated validator properties (BLS key, weight, nonce) in memory and to track IPs (in addition to other services provided by the Primary Network). This `Balance` is initialized with the `RegisterSubnetValidatorTx` that added them to the active validator set. `Balance` can be increased at any time using the `IncreaseSubnetValidatorBalanceTx`. When this `Balance` reaches `0`, the Subnet Validator will be considered "inactive" and will no longer participate in validating the Subnet. Inactive Subnet Validators can be moved back to the active validator set at any time using the same `IncreaseSubnetValidatorBalanceTx`. Once a Subnet Validator is considered inactive, the P-Chain will remove these properties from memory and only retain them on disk. All messages from that validator will be considered invalid until it is revived using the `IncreaseSubnetValidatorBalanceTx`. Subnets can reduce the amount of inactive weight by removing inactive validators with the `SetSubnetValidatorWeightTx` (`Weight` = 0). +To charge each Subnet Validator, the notion of a `Balance` is introduced. The `Balance` of a Subnet Validator will be continuously charged during the time they are active to cover the cost of storing the associated validator properties (BLS key, weight, nonce) in memory and to track IPs (in addition to other services provided by the Primary Network). This `Balance` is initialized with the `RegisterL1ValidatorTx` that added them to the active validator set. `Balance` can be increased at any time using the `IncreaseL1ValidatorBalanceTx`. When this `Balance` reaches `0`, the Subnet Validator will be considered "inactive" and will no longer participate in validating the Subnet. Inactive Subnet Validators can be moved back to the active validator set at any time using the same `IncreaseL1ValidatorBalanceTx`. Once a Subnet Validator is considered inactive, the P-Chain will remove these properties from memory and only retain them on disk. All messages from that validator will be considered invalid until it is revived using the `IncreaseL1ValidatorBalanceTx`. Subnets can reduce the amount of inactive weight by removing inactive validators with the `SetL1ValidatorWeightTx` (`Weight` = 0). Since each Subnet Validator is charged the same amount at each point in time, tracking the fees for the entire validator set is straight-forward. The accumulated dynamic base fee for the entire network is tracked in a single uint. This accumulated value should be equal to the fee charged if a Subnet Validator was active from the time the accumulator was instantiated. The validator set is maintained in a priority queue. A pseudocode implementation of the continuous fee mechanism is provided below. @@ -403,11 +407,11 @@ class ValidatorQueue: self.acc = 0 self.queue = PriorityQueue() self.fee_getter = fee_getter - - # At each time period, increment the accumulator and - # pop all validators from the top of the queue that + + # At each time period, increment the accumulator and + # pop all validators from the top of the queue that # ran out of funds. - + # Note: The amount of work done in a single block # should be bounded to prevent a large number of # validator operations from happening at the same @@ -505,12 +509,12 @@ def cost_over_time(V:int, T:int, x:int, Δt: int) -> int: The parameters at activation are: -| Parameter | Value | -| - | - | -| $T$ | `TODO` Subnet Validators | -| $C$ | `TODO` Subnet Validators | -| $M$ | `TODO` nAVAX/s (`TODO` AVAX/day) | -| $K$ | `TODO` | +| Parameter | Value | +| --------- | -------------------------------- | +| $T$ | `TODO` Subnet Validators | +| $C$ | `TODO` Subnet Validators | +| $M$ | `TODO` nAVAX/s (`TODO` AVAX/day) | +| $K$ | `TODO` | A future ACP can adjust the parameters to increase $T$, reduce $M$, and/or modify $K$. @@ -531,6 +535,7 @@ Any state execution changes must be coordinated through a mandatory upgrade. Imp ### Deactivated Transactions - P-Chain + - `TransformSubnetTx` After this ACP is activated, Elastic Subnets will be disabled. `TransformSubnetTx` will not be accepted post-activation. As there are no Mainnet Elastic Subnets, there should be no production impact with this deactivation. @@ -538,11 +543,11 @@ Any state execution changes must be coordinated through a mandatory upgrade. Imp ### New Transactions - P-Chain - - `ConvertSubnetTx` - - `RegisterSubnetValidatorTx` - - `SetSubnetValidatorWeightTx` - - `DisableSubnetValidatorTx` - - `IncreaseSubnetValidatorBalanceTx` + - `ConvertL1Tx` + - `RegisterL1ValidatorTx` + - `SetL1ValidatorWeightTx` + - `DisableL1ValidatorTx` + - `IncreaseL1ValidatorBalanceTx` ## Reference Implementation @@ -552,9 +557,9 @@ A full reference implementation has not been provided yet. It will be provided o This ACP significantly reduces the cost of becoming a Subnet Validator. This can lead to a large increase in the number of Subnet Validators going forward. Each additional Subnet Validator adds consistent RAM usage to the P-Chain. However, this should be appropriately metered by the continuous fee mechanism outlined above. -With the additional sovereignty Subnets gain from the P-Chain, Subnet staking tokens are no longer locked on the P-Chain for Permissionless Subnets. This poses a new security consideration for Subnet Validators: Malicious Subnets can choose to remove validators at will and take any funds that the Subnet Validator has on the Subnet. The P-Chain only provides the guarantee that Subnet Validators can retrieve the remaining $AVAX Balance for their Validator via a `DisableSubnetValidatorTx`. Any assets on the Subnet is entirely under the purview of the Subnet. The onus is now on Subnet Validators to vet the Subnet's security. +With the additional sovereignty Subnets gain from the P-Chain, Subnet staking tokens are no longer locked on the P-Chain for Permissionless Subnets. This poses a new security consideration for Subnet Validators: Malicious Subnets can choose to remove validators at will and take any funds that the Subnet Validator has on the Subnet. The P-Chain only provides the guarantee that Subnet Validators can retrieve the remaining $AVAX Balance for their Validator via a `DisableL1ValidatorTx`. Any assets on the Subnet is entirely under the purview of the Subnet. The onus is now on Subnet Validators to vet the Subnet's security. -With a long window of expiry (48 hours) for the Warp message in `RegisterSubnetValidatorTx`, spam of Subnet Validator registration could lead to high memory pressure on the P-Chain. A future ACP can reduce the window of expiry if 48 hours proves to be a problem. +With a long window of expiry (48 hours) for the Warp message in `RegisterL1ValidatorTx`, spam of Subnet Validator registration could lead to high memory pressure on the P-Chain. A future ACP can reduce the window of expiry if 48 hours proves to be a problem. NodeIDs can be added to a Subnet's validator set involuntarily. However, it is important to note that any stake/rewards are _not_ at risk. For a node operator who was added to a validator set involuntarily, they would only need to generate a new NodeID via key rotation as there is no lock-up of any stake to create a NodeID. This is an explicit tradeoff for easier on-boarding of NodeIDs. This mirrors the Primary Network Validators guarantee of no stake/rewards at risk. @@ -564,7 +569,7 @@ The continuous fee mechanism outlined above does not apply to inactive Subnet Va ### Should they still be called Subnets? -Through this ACP, Subnets have far more sovereignty than they did before. The Avalanche Community could take this opportunity to re-brand Subnets. +Going forward, Subnets should be referred to as L1s. Unlike layer 2 blockchains, layer 1 blockchains operate independently without relying on other systems for their core functions. They manage their own consensus mechanisms, transaction processing, and security protocols directly within their own networks. Subnets have always operated this way. With the added functionality introduced in this ACP, they are clearly standalone networks, or L1s. ## Acknowledgements From 1e0fa842566e810a60dc2d19ebdd54ba506a5b50 Mon Sep 17 00:00:00 2001 From: meaghanfitzgerald Date: Tue, 15 Oct 2024 11:58:03 -0300 Subject: [PATCH 2/3] conversion-message weight-message --- ACPs/77-reinventing-subnets/README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ACPs/77-reinventing-subnets/README.md b/ACPs/77-reinventing-subnets/README.md index 8f8c14b..85da932 100644 --- a/ACPs/77-reinventing-subnets/README.md +++ b/ACPs/77-reinventing-subnets/README.md @@ -44,10 +44,10 @@ For messages produced by the P-Chain for a given Subnet, only that Subnet's vali The following Warp message payloads are introduced on the P-Chain: -- `SubnetConversionMessage` +- `L1ConversionMessage` - `RegisterL1ValidatorMessage` - `L1ValidatorRegistrationMessage` -- `SubnetValidatorWeightMessage` +- `L1ValidatorWeightMessage` The method of requesting signatures for these messages is left unspecified. A viable option for supporting this functionality is laid out in [ACP-118](../118-warp-signature-request/README.md) with the `SignatureRequest` message. @@ -55,9 +55,9 @@ All node IDs contained within the message specifications are represented as vari The serialization of each of these messages is as follows. -#### `SubnetConversionMessage` +#### `L1ConversionMessage` -The P-Chain can produce a `SubnetConversionMessage` for consumers (i.e. validator managers) to be aware of the initial validator set. +The P-Chain can produce a `L1ConversionMessage` for consumers (i.e. validator managers) to be aware of the initial validator set. The following serialization is defined as a `ValidatorData`: @@ -85,7 +85,7 @@ The following serialization is defined as the `SubnetConversionData`: - `managerChainID` and `managerAddress` identify the validator manager for the given Subnet. This is the (blockchain ID, adress) tuple allowed to send Warp messages to modify the Subnet's validator set. - `validators` are the initial pay-as-you-go validators for the given Subnet. -The `SubnetConversionMessage` is specified as an `AddressedCall` with `sourceChainID` set to the P-Chain ID, the `sourceAddress` set to an empty byte array, and a payload of: +The `L1ConversionMessage` is specified as an `AddressedCall` with `sourceChainID` set to the P-Chain ID, the `sourceAddress` set to an empty byte array, and a payload of: | Field | Type | Size | | -------------------: | ---------: | -------: | @@ -157,11 +157,11 @@ The `L1ValidatorRegistrationMessage` is specified as an `AddressedCall` with `so - `validationID` identifies the validator for the message - `registered` is a boolean representing the status of the `validationID`. If true, the `validationID` corresponds to a validator in the current validator set. If false, the `validationID` does not correspond to a validator in the current validator set, and never will in the future. -#### `SubnetValidatorWeightMessage` +#### `L1ValidatorWeightMessage` -The P-Chain can consume a `SubnetValidatorWeightMessage` through a `SetL1ValidatorWeightTx` to update the weight of an existing validator. The P-Chain can also produce a `SubnetValidatorWeightMessage` for consumers to verify that the validator weight update has been effectuated. +The P-Chain can consume a `L1ValidatorWeightMessage` through a `SetL1ValidatorWeightTx` to update the weight of an existing validator. The P-Chain can also produce a `L1ValidatorWeightMessage` for consumers to verify that the validator weight update has been effectuated. -The `SubnetValidatorWeightMessage` is specified as an `AddressedCall` with the following payload. When sent from the P-Chain, the `sourceChainID` is set to the P-Chain ID, and the `sourceAddress` is set to an empty byte array. +The `L1ValidatorWeightMessage` is specified as an `AddressedCall` with the following payload. When sent from the P-Chain, the `sourceChainID` is set to the P-Chain ID, and the `sourceAddress` is set to an empty byte array. | Field | Type | Size | | -------------: | ---------: | -------: | @@ -255,7 +255,7 @@ After this transaction is accepted, `CreateChainTx` and `AddSubnetValidatorTx` a The `validationID` for validators added through `ConvertL1Tx` is defined as the SHA256 hash of the 36 bytes resulting from concatenating the 32 byte `subnetID` with the 4 byte `validatorIndex` (index in the `Validators` array within the transaction). -Once this transaction is accepted, the P-Chain must be willing sign a `SubnetConversionMessage` with a `subnetConversionID` corresponding to `SubnetConversionData` populated with the values from this transaction. +Once this transaction is accepted, the P-Chain must be willing sign a `L1ConversionMessage` with a `subnetConversionID` corresponding to `SubnetConversionData` populated with the values from this transaction. #### `RegisterL1ValidatorTx` @@ -306,7 +306,7 @@ When it is known that a given `validationID` _is not and never will be_ an exist type SetL1ValidatorWeightTx struct { // Metadata, inputs and outputs BaseTx - // A SubnetValidatorWeightMessage payload + // A L1ValidatorWeightMessage payload Message warp.Message `json:"message"` } ``` @@ -318,7 +318,7 @@ Applications of this transaction could include: - Decrease the voting weight of a misbehaving Subnet Validator - Remove an inactive Subnet Validator -The validation criteria for `SubnetValidatorWeightMessage` is: +The validation criteria for `L1ValidatorWeightMessage` is: - `nonce >= minNonce`. Note that `nonce` is not required to be incremented by `1` with each successive validator weight update. - When `minNonce == MaxUint64`, `nonce` must be `MaxUint64` and `weight` must be `0`. This prevents Subnets from being unable to remove `nodeID` in a subsequent transaction. From d81724c5dcffef72bc838e00f4ede51559321658 Mon Sep 17 00:00:00 2001 From: meaghanfitzgerald Date: Tue, 15 Oct 2024 12:00:49 -0300 Subject: [PATCH 3/3] validator struct --- ACPs/77-reinventing-subnets/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ACPs/77-reinventing-subnets/README.md b/ACPs/77-reinventing-subnets/README.md index 85da932..772ecef 100644 --- a/ACPs/77-reinventing-subnets/README.md +++ b/ACPs/77-reinventing-subnets/README.md @@ -214,7 +214,7 @@ type PChainOwner struct { Addresses []ids.ShortID `json:"addresses"` } -type SubnetValidator struct { +type L1Validator struct { // NodeID of this validator NodeID []byte `json:"nodeID"` // Weight of this validator used when sampling @@ -245,7 +245,7 @@ type ConvertL1Tx struct { // Address of the Subnet manager Address []byte `json:"address"` // Initial pay-as-you-go validators for the Subnet - Validators []SubnetValidator `json:"validators"` + Validators []L1Validator `json:"validators"` // Authorizes this conversion SubnetAuth verify.Verifiable `json:"subnetAuthorization"` }