Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACP-77: Implement SetSubnetValidatorWeightTx #3421

Draft
wants to merge 47 commits into
base: implement-acp-77-register-subnet-validator-tx
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
ef71d41
ACP-77: Implement SetSubnetValidatorWeightTx
StephenButtolph Sep 26, 2024
d881881
Refund balance
StephenButtolph Sep 30, 2024
7b33fd2
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 1, 2024
90aece6
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 1, 2024
ab6bb9d
Fix compilation
StephenButtolph Oct 1, 2024
91232d9
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 1, 2024
6e06c0c
Update setSubnetValidatorWeightTx
StephenButtolph Oct 1, 2024
59e291a
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 2, 2024
06657e1
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 2, 2024
904eeca
merged
StephenButtolph Oct 2, 2024
4eeb0fd
merged
StephenButtolph Oct 2, 2024
443b24b
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 2, 2024
b6cbfee
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 2, 2024
4bb66de
merged
StephenButtolph Oct 3, 2024
4a38f38
fix merge
StephenButtolph Oct 3, 2024
5feec6d
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 3, 2024
d106454
merged
StephenButtolph Oct 4, 2024
0ceeb16
Fix lint
StephenButtolph Oct 4, 2024
3178ec0
fix lint
StephenButtolph Oct 4, 2024
cf6e984
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 4, 2024
ea04117
Disallow removal of last validator
StephenButtolph Oct 7, 2024
73704d2
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 7, 2024
504fc7b
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
ad98012
Test L1 removal
StephenButtolph Oct 8, 2024
8453ee1
fix test
StephenButtolph Oct 8, 2024
665c499
nit
StephenButtolph Oct 8, 2024
a3fe57f
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
ad50b63
Update the weight
StephenButtolph Oct 8, 2024
f7119e0
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
21d732b
backport tests
StephenButtolph Oct 8, 2024
b5a4524
merged
StephenButtolph Oct 8, 2024
17f2d95
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
99c24ad
merge
StephenButtolph Oct 8, 2024
155b629
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
ae5c712
Add SetSubnetValidatorWeightTx complexity tests
StephenButtolph Oct 8, 2024
96ac986
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 8, 2024
cec1fd8
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 10, 2024
9720bdb
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 11, 2024
15e2767
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 14, 2024
9bb4359
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 15, 2024
fab2dac
merged
StephenButtolph Oct 19, 2024
d64794b
merged
StephenButtolph Oct 19, 2024
e33eff9
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 19, 2024
e1ebce9
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 20, 2024
81202b5
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 20, 2024
2d722fa
fix merge
StephenButtolph Oct 20, 2024
c4f5652
Merge branch 'implement-acp-77-register-subnet-validator-tx' into imp…
StephenButtolph Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions tests/e2e/p/permissionless_layer_one.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
genesisWeight = units.Schmeckle
genesisBalance = units.Avax
registerWeight = genesisWeight / 10
updatedWeight = 2 * registerWeight
registerBalance = 0
)

Expand Down Expand Up @@ -278,6 +279,7 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() {
registerWeight,
)
require.NoError(err)
registerValidationID := registerSubnetValidatorMessage.ValidationID()

tc.By("registering the validator", func() {
tc.By("creating the unsigned warp message")
Expand Down Expand Up @@ -346,6 +348,100 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() {
})
})

var nextNonce uint64
setWeight := func(validationID ids.ID, weight uint64) {
tc.By("creating the unsigned warp message")
unsignedSubnetValidatorWeight := must[*warp.UnsignedMessage](tc)(warp.NewUnsignedMessage(
networkID,
chainID,
must[*payload.AddressedCall](tc)(payload.NewAddressedCall(
address,
must[*warpmessage.SubnetValidatorWeight](tc)(warpmessage.NewSubnetValidatorWeight(
validationID,
nextNonce,
weight,
)).Bytes(),
)).Bytes(),
))

tc.By("sending the request to sign the warp message", func() {
setSubnetValidatorWeightRequest, err := wrapWarpSignatureRequest(
unsignedSubnetValidatorWeight,
nil,
)
require.NoError(err)

require.True(genesisPeer.Send(tc.DefaultContext(), setSubnetValidatorWeightRequest))
})

tc.By("getting the signature response")
setSubnetValidatorWeightSignature, ok, err := findMessage(genesisPeerMessages, unwrapWarpSignature)
require.NoError(err)
require.True(ok)

tc.By("creating the signed warp message to increase the weight of the validator")
signers := set.NewBits()
signers.Add(0) // [signers] has weight from the genesis peer

var sigBytes [bls.SignatureLen]byte
copy(sigBytes[:], bls.SignatureToBytes(setSubnetValidatorWeightSignature))
registerSubnetValidator, err := warp.NewMessage(
unsignedSubnetValidatorWeight,
&warp.BitSetSignature{
Signers: signers.Bytes(),
Signature: sigBytes,
},
)
require.NoError(err)

tc.By("issuing a SetSubnetValidatorWeightTx", func() {
_, err := pWallet.IssueSetSubnetValidatorWeightTx(
registerSubnetValidator.Bytes(),
)
require.NoError(err)
})

nextNonce++
}

tc.By("increasing the weight of the validator", func() {
setWeight(registerValidationID, updatedWeight)
})

tc.By("verifying the validator weight was increased", func() {
tc.By("verifying the validator set was updated", func() {
verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{
subnetGenesisNode.NodeID: {
NodeID: subnetGenesisNode.NodeID,
PublicKey: genesisNodePK,
Weight: genesisWeight,
},
ids.EmptyNodeID: { // The validator is not active
NodeID: ids.EmptyNodeID,
Weight: updatedWeight,
},
})
})
})

tc.By("advancing the proposervm P-chain height", advanceProposerVMPChainHeight)

tc.By("removing the registered validator", func() {
setWeight(registerValidationID, 0)
})

tc.By("verifying the validator was removed", func() {
tc.By("verifying the validator set was updated", func() {
verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{
subnetGenesisNode.NodeID: {
NodeID: subnetGenesisNode.NodeID,
PublicKey: genesisNodePK,
Weight: genesisWeight,
},
})
})
})

_ = e2e.CheckBootstrapIsPossible(tc, env.GetNetwork())
})
})
Expand Down
7 changes: 7 additions & 0 deletions vms/platformvm/metrics/tx_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,10 @@ func (m *txMetrics) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidatorTx) er
}).Inc()
return nil
}

func (m *txMetrics) SetSubnetValidatorWeightTx(*txs.SetSubnetValidatorWeightTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "set_subnet_validator_weight",
}).Inc()
return nil
}
1 change: 1 addition & 0 deletions vms/platformvm/txs/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,6 @@ func RegisterEtnaTypes(targetCodec linearcodec.Codec) error {
return errors.Join(
targetCodec.RegisterType(&ConvertSubnetTx{}),
targetCodec.RegisterType(&RegisterSubnetValidatorTx{}),
targetCodec.RegisterType(&SetSubnetValidatorWeightTx{}),
)
}
4 changes: 4 additions & 0 deletions vms/platformvm/txs/executor/atomic_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ func (*AtomicTxExecutor) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidatorT
return ErrWrongTxType
}

func (*AtomicTxExecutor) SetSubnetValidatorWeightTx(*txs.SetSubnetValidatorWeightTx) error {
return ErrWrongTxType
}

func (e *AtomicTxExecutor) ImportTx(tx *txs.ImportTx) error {
return e.atomicTx(tx)
}
Expand Down
4 changes: 4 additions & 0 deletions vms/platformvm/txs/executor/proposal_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ func (*ProposalTxExecutor) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidato
return ErrWrongTxType
}

func (*ProposalTxExecutor) SetSubnetValidatorWeightTx(*txs.SetSubnetValidatorWeightTx) error {
return ErrWrongTxType
}

func (e *ProposalTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error {
// AddValidatorTx is a proposal transaction until the Banff fork
// activation. Following the activation, AddValidatorTxs must be issued into
Expand Down
157 changes: 151 additions & 6 deletions vms/platformvm/txs/executor/standard_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/math"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/components/gas"
Expand All @@ -28,6 +27,9 @@ import (
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/message"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"

safemath "github.com/ava-labs/avalanchego/utils/math"
)

const (
Expand All @@ -47,6 +49,9 @@ var (
errEtnaUpgradeNotActive = errors.New("attempting to use an Etna-upgrade feature prior to activation")
errTransformSubnetTxPostEtna = errors.New("TransformSubnetTx is not permitted post-Etna")
errMaxNumActiveValidators = errors.New("already at the max number of active validators")
errRemovingLastValidator = errors.New("attempting to remove the last SoV from a converted subnet")

errStateCorruption = errors.New("state corruption")
)

type StandardTxExecutor struct {
Expand Down Expand Up @@ -585,12 +590,12 @@ func (e *StandardTxExecutor) ConvertSubnetTx(tx *txs.ConvertSubnetTx) error {
return errMaxNumActiveValidators
}

sov.EndAccumulatedFee, err = math.Add(vdr.Balance, currentFees)
sov.EndAccumulatedFee, err = safemath.Add(vdr.Balance, currentFees)
if err != nil {
return err
}

fee, err = math.Add(fee, vdr.Balance)
fee, err = safemath.Add(fee, vdr.Balance)
if err != nil {
return err
}
Expand Down Expand Up @@ -664,7 +669,7 @@ func (e *StandardTxExecutor) RegisterSubnetValidatorTx(tx *txs.RegisterSubnetVal
if err != nil {
return err
}
fee, err = math.Add(fee, tx.Balance)
fee, err = safemath.Add(fee, tx.Balance)
if err != nil {
return err
}
Expand Down Expand Up @@ -718,7 +723,7 @@ func (e *StandardTxExecutor) RegisterSubnetValidatorTx(tx *txs.RegisterSubnetVal
if msg.Expiry <= currentTimestampUnix {
return fmt.Errorf("expected expiry to be after %d but got %d", currentTimestampUnix, msg.Expiry)
}
maxAllowedExpiry, err := math.Add(currentTimestampUnix, RegisterSubnetValidatorTxExpiryWindow)
maxAllowedExpiry, err := safemath.Add(currentTimestampUnix, RegisterSubnetValidatorTxExpiryWindow)
if err != nil {
// This should never happen, as it would imply that either
// currentTimestampUnix or RegisterSubnetValidatorTxExpiryWindow is
Expand Down Expand Up @@ -784,7 +789,7 @@ func (e *StandardTxExecutor) RegisterSubnetValidatorTx(tx *txs.RegisterSubnetVal
}

currentFees := e.State.GetAccruedFees()
sov.EndAccumulatedFee, err = math.Add(tx.Balance, currentFees)
sov.EndAccumulatedFee, err = safemath.Add(tx.Balance, currentFees)
if err != nil {
return err
}
Expand All @@ -805,6 +810,146 @@ func (e *StandardTxExecutor) RegisterSubnetValidatorTx(tx *txs.RegisterSubnetVal
return nil
}

func (e *StandardTxExecutor) SetSubnetValidatorWeightTx(tx *txs.SetSubnetValidatorWeightTx) error {
var (
currentTimestamp = e.State.GetTimestamp()
upgrades = e.Backend.Config.UpgradeConfig
)
if !upgrades.IsEtnaActivated(currentTimestamp) {
return errEtnaUpgradeNotActive
}

if err := e.Tx.SyntacticVerify(e.Ctx); err != nil {
return err
}

if err := avax.VerifyMemoFieldLength(tx.Memo, true /*=isDurangoActive*/); err != nil {
return err
}

// Verify the flowcheck
fee, err := e.FeeCalculator.CalculateFee(tx)
if err != nil {
return err
}

if err := e.Backend.FlowChecker.VerifySpend(
tx,
e.State,
tx.Ins,
tx.Outs,
e.Tx.Creds,
map[ids.ID]uint64{
e.Ctx.AVAXAssetID: fee,
},
); err != nil {
return err
}

warpMessage, err := warp.ParseMessage(tx.Message)
if err != nil {
return err
}
if warpMessage.NetworkID != e.Ctx.NetworkID {
return fmt.Errorf("expected networkID %d but got %d", e.Ctx.NetworkID, warpMessage.NetworkID)
}

addressedCall, err := payload.ParseAddressedCall(warpMessage.Payload)
if err != nil {
return err
}

msg, err := message.ParseSubnetValidatorWeight(addressedCall.Payload)
if err != nil {
return err
}
if err := msg.Verify(); err != nil {
return err
}

sov, err := e.State.GetSubnetOnlyValidator(msg.ValidationID)
if err != nil {
return err
}
if msg.Nonce < sov.MinNonce {
return fmt.Errorf("expected nonce to be at least %d but got %d", sov.MinNonce, msg.Nonce)
}

subnetConversion, err := e.State.GetSubnetConversion(sov.SubnetID)
if err != nil {
return err
}
if warpMessage.SourceChainID != subnetConversion.ChainID {
return fmt.Errorf("expected chainID %s but got %s", subnetConversion.ChainID, warpMessage.SourceChainID)
}
if !bytes.Equal(addressedCall.SourceAddress, subnetConversion.Addr) {
return fmt.Errorf("expected address %s but got %s", subnetConversion.Addr, addressedCall.SourceAddress)
}

txID := e.Tx.ID()

// We are removing the validator
if msg.Weight == 0 {
weight, err := e.State.WeightOfSubnetOnlyValidators(sov.SubnetID)
if err != nil {
return err
}
if weight == sov.Weight {
return errRemovingLastValidator
}

// The validator is currently active, we need to refund the remaining
// balance.
if sov.EndAccumulatedFee != 0 {
var remainingBalanceOwner message.PChainOwner
if _, err := txs.Codec.Unmarshal(sov.RemainingBalanceOwner, &remainingBalanceOwner); err != nil {
return err
}

accruedFees := e.State.GetAccruedFees()
if sov.EndAccumulatedFee <= accruedFees {
// This check should be unreachable. However, including it ensures
// that AVAX can't get minted out of thin air due to state
// corruption.
return fmt.Errorf("%w: validator should have already been disabled", errStateCorruption)
}
remainingBalance := sov.EndAccumulatedFee - accruedFees

utxo := &avax.UTXO{
UTXOID: avax.UTXOID{
TxID: txID,
OutputIndex: uint32(len(tx.Outs)),
},
Asset: avax.Asset{
ID: e.Ctx.AVAXAssetID,
},
Out: &secp256k1fx.TransferOutput{
Amt: remainingBalance,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: remainingBalanceOwner.Threshold,
Addrs: remainingBalanceOwner.Addresses,
},
},
}
e.State.AddUTXO(utxo)
}
}

// If the weight is being set to 0, the validator is being removed and the
// nonce doesn't matter.
sov.MinNonce = msg.Nonce + 1
sov.Weight = msg.Weight
if err := e.State.PutSubnetOnlyValidator(sov); err != nil {
return err
}

// Consume the UTXOS
avax.Consume(e.State, tx.Ins)
// Produce the UTXOS
avax.Produce(e.State, txID, tx.Outs)
return nil
}

func (e *StandardTxExecutor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error {
if err := verifyAddPermissionlessValidatorTx(
e.Backend,
Expand Down
12 changes: 12 additions & 0 deletions vms/platformvm/txs/fee/calculator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,17 @@ var (
},
expectedDynamicFee: 151_000,
},
{
name: "SetSubnetValidatorWeightTx",
tx: "00000000002500003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b5100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001389c41b6ed301e4c118bd23673268fd2054b772efcf25685a117b74bab7ae5e400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b552a000000010000000000000000000000d7000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000044000000000001000000000000003600000000000338e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000001000000000000000500000000000000010187f4bb2c42869c56f023a1ca81045aff034acd490b8f15b5069025f982e605e077007fc588f7d56369a65df7574df3b70ff028ea173739c789525ab7eebfcb5c115b13cca8f02b362104b700c75bc95234109f3f1360ddcb4ec3caf6b0e821cb0000000100000009000000010a29f3c86d52908bf2efbc3f918a363df704c429d66c8d6615712a2a584a2a5f264a9e7b107c07122a06f31cadc2f51285884d36fe8df909a07467417f1d64cf00",
expectedStaticFeeErr: ErrUnsupportedTx,
expectedComplexity: gas.Dimensions{
gas.Bandwidth: 518, // The length of the tx in bytes
gas.DBRead: IntrinsicSetSubnetValidatorWeightTxComplexities[gas.DBRead] + intrinsicInputDBRead,
gas.DBWrite: IntrinsicSetSubnetValidatorWeightTxComplexities[gas.DBWrite] + intrinsicInputDBWrite + intrinsicOutputDBWrite,
gas.Compute: 0, // TODO: implement
},
expectedDynamicFee: 131_800,
},
}
)
Loading
Loading