Skip to content

Commit

Permalink
Add TransferSubnetOwnershipTx (#2178)
Browse files Browse the repository at this point in the history
Signed-off-by: Dhruba Basu <7675102+dhrubabasu@users.noreply.github.com>
  • Loading branch information
dhrubabasu committed Oct 25, 2023
1 parent 150ffae commit f020a05
Show file tree
Hide file tree
Showing 18 changed files with 1,006 additions and 1 deletion.
1 change: 1 addition & 0 deletions vms/platformvm/block/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func init() {
RegisterApricotBlockTypes(c),
txs.RegisterUnsignedTxsTypes(c),
RegisterBanffBlockTypes(c),
txs.RegisterDUnsignedTxsTypes(c),
)
}
errs.Add(
Expand Down
9 changes: 8 additions & 1 deletion vms/platformvm/metrics/tx_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type txMetrics struct {
numRemoveSubnetValidatorTxs,
numTransformSubnetTxs,
numAddPermissionlessValidatorTxs,
numAddPermissionlessDelegatorTxs prometheus.Counter
numAddPermissionlessDelegatorTxs,
numTransferSubnetOwnershipTxs prometheus.Counter
}

func newTxMetrics(
Expand All @@ -49,6 +50,7 @@ func newTxMetrics(
numTransformSubnetTxs: newTxMetric(namespace, "transform_subnet", registerer, &errs),
numAddPermissionlessValidatorTxs: newTxMetric(namespace, "add_permissionless_validator", registerer, &errs),
numAddPermissionlessDelegatorTxs: newTxMetric(namespace, "add_permissionless_delegator", registerer, &errs),
numTransferSubnetOwnershipTxs: newTxMetric(namespace, "transfer_subnet_ownership", registerer, &errs),
}
return m, errs.Err
}
Expand Down Expand Up @@ -132,3 +134,8 @@ func (m *txMetrics) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegator
m.numAddPermissionlessDelegatorTxs.Inc()
return nil
}

func (m *txMetrics) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
m.numTransferSubnetOwnershipTxs.Inc()
return nil
}
52 changes: 52 additions & 0 deletions vms/platformvm/txs/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,19 @@ type ProposalTxBuilder interface {
changeAddr ids.ShortID,
) (*txs.Tx, error)

// Creates a transaction that transfers ownership of [subnetID]
// threshold: [threshold] of [ownerAddrs] needed to manage this subnet
// ownerAddrs: control addresses for the new subnet
// keys: keys to use for modifying the subnet
// changeAddr: address to send change to, if there is any
NewTransferSubnetOwnershipTx(
subnetID ids.ID,
threshold uint32,
ownerAddrs []ids.ShortID,
keys []*secp256k1.PrivateKey,
changeAddr ids.ShortID,
) (*txs.Tx, error)

// newAdvanceTimeTx creates a new tx that, if it is accepted and followed by a
// Commit block, will set the chain's timestamp to [timestamp].
NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error)
Expand Down Expand Up @@ -609,3 +622,42 @@ func (b *builder) NewRewardValidatorTx(txID ids.ID) (*txs.Tx, error) {

return tx, tx.SyntacticVerify(b.ctx)
}

func (b *builder) NewTransferSubnetOwnershipTx(
subnetID ids.ID,
threshold uint32,
ownerAddrs []ids.ShortID,
keys []*secp256k1.PrivateKey,
changeAddr ids.ShortID,
) (*txs.Tx, error) {
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, b.cfg.TxFee, changeAddr)
if err != nil {
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
}

subnetAuth, subnetSigners, err := b.Authorize(b.state, subnetID, keys)
if err != nil {
return nil, fmt.Errorf("couldn't authorize tx's subnet restrictions: %w", err)
}
signers = append(signers, subnetSigners)

utx := &txs.TransferSubnetOwnershipTx{
BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
NetworkID: b.ctx.NetworkID,
BlockchainID: b.ctx.ChainID,
Ins: ins,
Outs: outs,
}},
Subnet: subnetID,
SubnetAuth: subnetAuth,
Owner: &secp256k1fx.OutputOwners{
Threshold: threshold,
Addrs: ownerAddrs,
},
}
tx, err := txs.NewSigned(utx, txs.Codec, signers)
if err != nil {
return nil, err
}
return tx, tx.SyntacticVerify(b.ctx)
}
15 changes: 15 additions & 0 deletions vms/platformvm/txs/builder/mock_builder.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions vms/platformvm/txs/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func init() {
c.SkipRegistrations(5)

errs.Add(RegisterUnsignedTxsTypes(c))

c.SkipRegistrations(4)

errs.Add(RegisterDUnsignedTxsTypes(c))
}
errs.Add(
Codec.RegisterCodec(Version, c),
Expand Down Expand Up @@ -97,3 +101,7 @@ func RegisterUnsignedTxsTypes(targetCodec linearcodec.Codec) error {
)
return errs.Err
}

func RegisterDUnsignedTxsTypes(targetCodec linearcodec.Codec) error {
return targetCodec.RegisterType(&TransferSubnetOwnershipTx{})
}
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 @@ -64,6 +64,10 @@ func (*AtomicTxExecutor) TransformSubnetTx(*txs.TransformSubnetTx) error {
return ErrWrongTxType
}

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

func (*AtomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
return ErrWrongTxType
}
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 @@ -97,6 +97,10 @@ func (*ProposalTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDe
return ErrWrongTxType
}

func (*ProposalTxExecutor) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) 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
48 changes: 48 additions & 0 deletions vms/platformvm/txs/executor/staker_tx_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
ErrDuplicateValidator = errors.New("duplicate validator")
ErrDelegateToPermissionedValidator = errors.New("delegation to permissioned validator")
ErrWrongStakedAssetID = errors.New("incorrect staked assetID")
ErrDUpgradeNotActive = errors.New("attempting to use a D-upgrade feature prior to activation")
)

// verifySubnetValidatorPrimaryNetworkRequirements verifies the primary
Expand Down Expand Up @@ -714,3 +715,50 @@ func verifyAddPermissionlessDelegatorTx(

return nil
}

// Returns an error if the given tx is invalid.
// The transaction is valid if:
// * [sTx]'s creds authorize it to spend the stated inputs.
// * [sTx]'s creds authorize it to transfer ownership of [tx.Subnet].
// * The flow checker passes.
func verifyTransferSubnetOwnershipTx(
backend *Backend,
chainState state.Chain,
sTx *txs.Tx,
tx *txs.TransferSubnetOwnershipTx,
) error {
if !backend.Config.IsDActivated(chainState.GetTimestamp()) {
return ErrDUpgradeNotActive
}

// Verify the tx is well-formed
if err := sTx.SyntacticVerify(backend.Ctx); err != nil {
return err
}

if !backend.Bootstrapped.Get() {
// Not bootstrapped yet -- don't need to do full verification.
return nil
}

baseTxCreds, err := verifySubnetAuthorization(backend, chainState, sTx, tx.Subnet, tx.SubnetAuth)
if err != nil {
return err
}

// Verify the flowcheck
if err := backend.FlowChecker.VerifySpend(
tx,
chainState,
tx.Ins,
tx.Outs,
baseTxCreds,
map[ids.ID]uint64{
backend.Ctx.AVAXAssetID: backend.Config.TxFee,
},
); err != nil {
return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err)
}

return nil
}
24 changes: 24 additions & 0 deletions vms/platformvm/txs/executor/standard_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,27 @@ func (e *StandardTxExecutor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionl

return nil
}

// Verifies a [*txs.TransferSubnetOwnershipTx] and, if it passes, executes it on
// [e.State]. For verification rules, see [verifyTransferSubnetOwnershipTx].
// This transaction will result in the ownership of [tx.Subnet] being transferred
// to [tx.Owner].
func (e *StandardTxExecutor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error {
err := verifyTransferSubnetOwnershipTx(
e.Backend,
e.State,
e.Tx,
tx,
)
if err != nil {
return err
}

e.State.SetSubnetOwner(tx.Subnet, tx.Owner)

txID := e.Tx.ID()
avax.Consume(e.State, tx.Ins)
avax.Produce(e.State, txID, tx.Outs)

return nil
}
4 changes: 4 additions & 0 deletions vms/platformvm/txs/executor/tx_mempool_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func (v *MempoolTxVerifier) AddPermissionlessDelegatorTx(tx *txs.AddPermissionle
return v.standardTx(tx)
}

func (v *MempoolTxVerifier) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error {
return v.standardTx(tx)
}

func (v *MempoolTxVerifier) standardTx(tx txs.UnsignedTx) error {
baseState, err := v.standardBaseState()
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions vms/platformvm/txs/mempool/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ func (i *issuer) TransformSubnetTx(*txs.TransformSubnetTx) error {
return nil
}

func (i *issuer) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
i.m.addDecisionTx(i.tx)
return nil
}

func (i *issuer) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
i.m.addStakerTx(i.tx)
return nil
Expand Down
5 changes: 5 additions & 0 deletions vms/platformvm/txs/mempool/remover.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (r *remover) TransformSubnetTx(*txs.TransformSubnetTx) error {
return nil
}

func (r *remover) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
r.m.removeDecisionTxs([]*txs.Tx{r.tx})
return nil
}

func (r *remover) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
r.m.removeStakerTx(r.tx)
return nil
Expand Down
65 changes: 65 additions & 0 deletions vms/platformvm/txs/transfer_subnet_ownership_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package txs

import (
"errors"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/vms/components/verify"
"github.com/ava-labs/avalanchego/vms/platformvm/fx"
)

var (
_ UnsignedTx = (*TransferSubnetOwnershipTx)(nil)

ErrTransferPermissionlessSubnet = errors.New("cannot transfer ownership of a permissionless subnet")
)

type TransferSubnetOwnershipTx struct {
// Metadata, inputs and outputs
BaseTx `serialize:"true"`
// ID of the subnet this tx is modifying
Subnet ids.ID `serialize:"true" json:"subnetID"`
// Proves that the issuer has the right to remove the node from the subnet.
SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"`
// Who is now authorized to manage this subnet
Owner fx.Owner `serialize:"true" json:"newOwner"`
}

// InitCtx sets the FxID fields in the inputs and outputs of this
// [TransferSubnetOwnershipTx]. Also sets the [ctx] to the given [vm.ctx] so
// that the addresses can be json marshalled into human readable format
func (tx *TransferSubnetOwnershipTx) InitCtx(ctx *snow.Context) {
tx.BaseTx.InitCtx(ctx)
tx.Owner.InitCtx(ctx)
}

func (tx *TransferSubnetOwnershipTx) SyntacticVerify(ctx *snow.Context) error {
switch {
case tx == nil:
return ErrNilTx
case tx.SyntacticallyVerified:
// already passed syntactic verification
return nil
case tx.Subnet == constants.PrimaryNetworkID:
return ErrTransferPermissionlessSubnet
}

if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
return err
}
if err := verify.All(tx.SubnetAuth, tx.Owner); err != nil {
return err
}

tx.SyntacticallyVerified = true
return nil
}

func (tx *TransferSubnetOwnershipTx) Visit(visitor Visitor) error {
return visitor.TransferSubnetOwnershipTx(tx)
}

0 comments on commit f020a05

Please sign in to comment.