Skip to content

Commit f020a05

Browse files
authored
Add TransferSubnetOwnershipTx (#2178)
Signed-off-by: Dhruba Basu <7675102+dhrubabasu@users.noreply.github.com>
1 parent 150ffae commit f020a05

18 files changed

+1006
-1
lines changed

vms/platformvm/block/codec.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func init() {
3636
RegisterApricotBlockTypes(c),
3737
txs.RegisterUnsignedTxsTypes(c),
3838
RegisterBanffBlockTypes(c),
39+
txs.RegisterDUnsignedTxsTypes(c),
3940
)
4041
}
4142
errs.Add(

vms/platformvm/metrics/tx_metrics.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ type txMetrics struct {
2727
numRemoveSubnetValidatorTxs,
2828
numTransformSubnetTxs,
2929
numAddPermissionlessValidatorTxs,
30-
numAddPermissionlessDelegatorTxs prometheus.Counter
30+
numAddPermissionlessDelegatorTxs,
31+
numTransferSubnetOwnershipTxs prometheus.Counter
3132
}
3233

3334
func newTxMetrics(
@@ -49,6 +50,7 @@ func newTxMetrics(
4950
numTransformSubnetTxs: newTxMetric(namespace, "transform_subnet", registerer, &errs),
5051
numAddPermissionlessValidatorTxs: newTxMetric(namespace, "add_permissionless_validator", registerer, &errs),
5152
numAddPermissionlessDelegatorTxs: newTxMetric(namespace, "add_permissionless_delegator", registerer, &errs),
53+
numTransferSubnetOwnershipTxs: newTxMetric(namespace, "transfer_subnet_ownership", registerer, &errs),
5254
}
5355
return m, errs.Err
5456
}
@@ -132,3 +134,8 @@ func (m *txMetrics) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegator
132134
m.numAddPermissionlessDelegatorTxs.Inc()
133135
return nil
134136
}
137+
138+
func (m *txMetrics) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
139+
m.numTransferSubnetOwnershipTxs.Inc()
140+
return nil
141+
}

vms/platformvm/txs/builder/builder.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,19 @@ type ProposalTxBuilder interface {
159159
changeAddr ids.ShortID,
160160
) (*txs.Tx, error)
161161

162+
// Creates a transaction that transfers ownership of [subnetID]
163+
// threshold: [threshold] of [ownerAddrs] needed to manage this subnet
164+
// ownerAddrs: control addresses for the new subnet
165+
// keys: keys to use for modifying the subnet
166+
// changeAddr: address to send change to, if there is any
167+
NewTransferSubnetOwnershipTx(
168+
subnetID ids.ID,
169+
threshold uint32,
170+
ownerAddrs []ids.ShortID,
171+
keys []*secp256k1.PrivateKey,
172+
changeAddr ids.ShortID,
173+
) (*txs.Tx, error)
174+
162175
// newAdvanceTimeTx creates a new tx that, if it is accepted and followed by a
163176
// Commit block, will set the chain's timestamp to [timestamp].
164177
NewAdvanceTimeTx(timestamp time.Time) (*txs.Tx, error)
@@ -609,3 +622,42 @@ func (b *builder) NewRewardValidatorTx(txID ids.ID) (*txs.Tx, error) {
609622

610623
return tx, tx.SyntacticVerify(b.ctx)
611624
}
625+
626+
func (b *builder) NewTransferSubnetOwnershipTx(
627+
subnetID ids.ID,
628+
threshold uint32,
629+
ownerAddrs []ids.ShortID,
630+
keys []*secp256k1.PrivateKey,
631+
changeAddr ids.ShortID,
632+
) (*txs.Tx, error) {
633+
ins, outs, _, signers, err := b.Spend(b.state, keys, 0, b.cfg.TxFee, changeAddr)
634+
if err != nil {
635+
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
636+
}
637+
638+
subnetAuth, subnetSigners, err := b.Authorize(b.state, subnetID, keys)
639+
if err != nil {
640+
return nil, fmt.Errorf("couldn't authorize tx's subnet restrictions: %w", err)
641+
}
642+
signers = append(signers, subnetSigners)
643+
644+
utx := &txs.TransferSubnetOwnershipTx{
645+
BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
646+
NetworkID: b.ctx.NetworkID,
647+
BlockchainID: b.ctx.ChainID,
648+
Ins: ins,
649+
Outs: outs,
650+
}},
651+
Subnet: subnetID,
652+
SubnetAuth: subnetAuth,
653+
Owner: &secp256k1fx.OutputOwners{
654+
Threshold: threshold,
655+
Addrs: ownerAddrs,
656+
},
657+
}
658+
tx, err := txs.NewSigned(utx, txs.Codec, signers)
659+
if err != nil {
660+
return nil, err
661+
}
662+
return tx, tx.SyntacticVerify(b.ctx)
663+
}

vms/platformvm/txs/builder/mock_builder.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vms/platformvm/txs/codec.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func init() {
4141
c.SkipRegistrations(5)
4242

4343
errs.Add(RegisterUnsignedTxsTypes(c))
44+
45+
c.SkipRegistrations(4)
46+
47+
errs.Add(RegisterDUnsignedTxsTypes(c))
4448
}
4549
errs.Add(
4650
Codec.RegisterCodec(Version, c),
@@ -97,3 +101,7 @@ func RegisterUnsignedTxsTypes(targetCodec linearcodec.Codec) error {
97101
)
98102
return errs.Err
99103
}
104+
105+
func RegisterDUnsignedTxsTypes(targetCodec linearcodec.Codec) error {
106+
return targetCodec.RegisterType(&TransferSubnetOwnershipTx{})
107+
}

vms/platformvm/txs/executor/atomic_tx_executor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ func (*AtomicTxExecutor) TransformSubnetTx(*txs.TransformSubnetTx) error {
6464
return ErrWrongTxType
6565
}
6666

67+
func (*AtomicTxExecutor) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
68+
return ErrWrongTxType
69+
}
70+
6771
func (*AtomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
6872
return ErrWrongTxType
6973
}

vms/platformvm/txs/executor/proposal_tx_executor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ func (*ProposalTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDe
9797
return ErrWrongTxType
9898
}
9999

100+
func (*ProposalTxExecutor) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
101+
return ErrWrongTxType
102+
}
103+
100104
func (e *ProposalTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error {
101105
// AddValidatorTx is a proposal transaction until the Banff fork
102106
// activation. Following the activation, AddValidatorTxs must be issued into

vms/platformvm/txs/executor/staker_tx_verification.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var (
3737
ErrDuplicateValidator = errors.New("duplicate validator")
3838
ErrDelegateToPermissionedValidator = errors.New("delegation to permissioned validator")
3939
ErrWrongStakedAssetID = errors.New("incorrect staked assetID")
40+
ErrDUpgradeNotActive = errors.New("attempting to use a D-upgrade feature prior to activation")
4041
)
4142

4243
// verifySubnetValidatorPrimaryNetworkRequirements verifies the primary
@@ -714,3 +715,50 @@ func verifyAddPermissionlessDelegatorTx(
714715

715716
return nil
716717
}
718+
719+
// Returns an error if the given tx is invalid.
720+
// The transaction is valid if:
721+
// * [sTx]'s creds authorize it to spend the stated inputs.
722+
// * [sTx]'s creds authorize it to transfer ownership of [tx.Subnet].
723+
// * The flow checker passes.
724+
func verifyTransferSubnetOwnershipTx(
725+
backend *Backend,
726+
chainState state.Chain,
727+
sTx *txs.Tx,
728+
tx *txs.TransferSubnetOwnershipTx,
729+
) error {
730+
if !backend.Config.IsDActivated(chainState.GetTimestamp()) {
731+
return ErrDUpgradeNotActive
732+
}
733+
734+
// Verify the tx is well-formed
735+
if err := sTx.SyntacticVerify(backend.Ctx); err != nil {
736+
return err
737+
}
738+
739+
if !backend.Bootstrapped.Get() {
740+
// Not bootstrapped yet -- don't need to do full verification.
741+
return nil
742+
}
743+
744+
baseTxCreds, err := verifySubnetAuthorization(backend, chainState, sTx, tx.Subnet, tx.SubnetAuth)
745+
if err != nil {
746+
return err
747+
}
748+
749+
// Verify the flowcheck
750+
if err := backend.FlowChecker.VerifySpend(
751+
tx,
752+
chainState,
753+
tx.Ins,
754+
tx.Outs,
755+
baseTxCreds,
756+
map[ids.ID]uint64{
757+
backend.Ctx.AVAXAssetID: backend.Config.TxFee,
758+
},
759+
); err != nil {
760+
return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err)
761+
}
762+
763+
return nil
764+
}

vms/platformvm/txs/executor/standard_tx_executor.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,27 @@ func (e *StandardTxExecutor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionl
490490

491491
return nil
492492
}
493+
494+
// Verifies a [*txs.TransferSubnetOwnershipTx] and, if it passes, executes it on
495+
// [e.State]. For verification rules, see [verifyTransferSubnetOwnershipTx].
496+
// This transaction will result in the ownership of [tx.Subnet] being transferred
497+
// to [tx.Owner].
498+
func (e *StandardTxExecutor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error {
499+
err := verifyTransferSubnetOwnershipTx(
500+
e.Backend,
501+
e.State,
502+
e.Tx,
503+
tx,
504+
)
505+
if err != nil {
506+
return err
507+
}
508+
509+
e.State.SetSubnetOwner(tx.Subnet, tx.Owner)
510+
511+
txID := e.Tx.ID()
512+
avax.Consume(e.State, tx.Ins)
513+
avax.Produce(e.State, txID, tx.Outs)
514+
515+
return nil
516+
}

vms/platformvm/txs/executor/tx_mempool_verifier.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func (v *MempoolTxVerifier) AddPermissionlessDelegatorTx(tx *txs.AddPermissionle
7474
return v.standardTx(tx)
7575
}
7676

77+
func (v *MempoolTxVerifier) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error {
78+
return v.standardTx(tx)
79+
}
80+
7781
func (v *MempoolTxVerifier) standardTx(tx txs.UnsignedTx) error {
7882
baseState, err := v.standardBaseState()
7983
if err != nil {

0 commit comments

Comments
 (0)