Skip to content

Commit

Permalink
Restrict Owner usage after a Subnet manager is set (#3147)
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 authored Aug 19, 2024
1 parent 4b4ec8a commit ac87871
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
37 changes: 37 additions & 0 deletions vms/platformvm/txs/executor/create_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,40 @@ func TestCreateChainTxAP3FeeChange(t *testing.T) {
})
}
}

func TestEtnaCreateChainTxInvalidWithManagedSubnet(t *testing.T) {
require := require.New(t)
env := newEnvironment(t, upgradetest.Etna)
env.ctx.Lock.Lock()
defer env.ctx.Lock.Unlock()

builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
utx, err := builder.NewCreateChainTx(
testSubnet1.ID(),
nil,
constants.AVMID,
nil,
"chain name",
)
require.NoError(err)
tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
require.NoError(err)

stateDiff, err := state.NewDiff(lastAcceptedID, env)
require.NoError(err)

builderDiff, err := state.NewDiffOn(stateDiff)
require.NoError(err)

stateDiff.SetSubnetManager(testSubnet1.ID(), ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'})

feeCalculator := state.PickFeeCalculator(env.config, builderDiff)
executor := StandardTxExecutor{
Backend: &env.backend,
FeeCalculator: feeCalculator,
State: stateDiff,
Tx: tx,
}
err = tx.Unsigned.Visit(&executor)
require.ErrorIs(err, errIsImmutable)
}
11 changes: 11 additions & 0 deletions vms/platformvm/txs/executor/staker_tx_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var (
ErrDurangoUpgradeNotActive = errors.New("attempting to use a Durango-upgrade feature prior to activation")
ErrAddValidatorTxPostDurango = errors.New("AddValidatorTx is not permitted post-Durango")
ErrAddDelegatorTxPostDurango = errors.New("AddDelegatorTx is not permitted post-Durango")
ErrRemoveValidatorManagedSubnet = errors.New("RemoveSubnetValidatorTx cannot be used to remove a validator from a Subnet with a manager")
)

// verifySubnetValidatorPrimaryNetworkRequirements verifies the primary
Expand Down Expand Up @@ -306,6 +307,16 @@ func verifyRemoveSubnetValidatorTx(
return nil, false, err
}

if backend.Config.UpgradeConfig.IsEtnaActivated(currentTimestamp) {
_, _, err := chainState.GetSubnetManager(tx.Subnet)
if err == nil {
return nil, false, fmt.Errorf("%w: %q", ErrRemoveValidatorManagedSubnet, tx.Subnet)
}
if err != database.ErrNotFound {
return nil, false, err
}
}

isCurrentValidator := true
vdr, err := chainState.GetCurrentValidator(tx.Subnet, tx.NodeID)
if err == database.ErrNotFound {
Expand Down
101 changes: 101 additions & 0 deletions vms/platformvm/txs/executor/standard_tx_executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,44 @@ func TestApricotStandardTxExecutorAddSubnetValidator(t *testing.T) {
}
}

func TestEtnaStandardTxExecutorAddSubnetValidator(t *testing.T) {
require := require.New(t)
env := newEnvironment(t, upgradetest.Etna)
env.ctx.Lock.Lock()
defer env.ctx.Lock.Unlock()

nodeID := genesisNodeIDs[0]

builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
utx, err := builder.NewAddSubnetValidatorTx(
&txs.SubnetValidator{
Validator: txs.Validator{
NodeID: nodeID,
Start: uint64(defaultValidateStartTime.Unix() + 1),
End: uint64(defaultValidateEndTime.Unix()),
Wght: defaultWeight,
},
Subnet: testSubnet1.ID(),
},
)
require.NoError(err)
tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
require.NoError(err)

onAcceptState, err := state.NewDiff(lastAcceptedID, env)
require.NoError(err)

onAcceptState.SetSubnetManager(testSubnet1.ID(), ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'})

executor := StandardTxExecutor{
Backend: &env.backend,
State: onAcceptState,
Tx: tx,
}
err = tx.Unsigned.Visit(&executor)
require.ErrorIs(err, errIsImmutable)
}

func TestBanffStandardTxExecutorAddValidator(t *testing.T) {
require := require.New(t)
env := newEnvironment(t, upgradetest.Banff)
Expand Down Expand Up @@ -1984,6 +2022,32 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) {
},
expectedErr: ErrFlowCheckFailed,
},
{
name: "attempted to remove subnet validator after subnet manager is set",
newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) {
env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl)
env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'}, nil).AnyTimes()
env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes()

cfg := &config.Config{
UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Etna, env.latestForkTime),
}
e := &StandardTxExecutor{
Backend: &Backend{
Config: cfg,
Bootstrapped: &utils.Atomic[bool]{},
Fx: env.fx,
FlowChecker: env.flowChecker,
Ctx: &snow.Context{},
},
Tx: env.tx,
State: env.state,
}
e.Bootstrapped.Set(true)
return env.unsignedTx, e
},
expectedErr: ErrRemoveValidatorManagedSubnet,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -2213,6 +2277,7 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) {
subnetOwner := fx.NewMockOwner(ctrl)
env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes()
env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil)
env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.Empty, nil, database.ErrNotFound).Times(1)
env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1)
env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil)
env.flowChecker.EXPECT().VerifySpend(
Expand Down Expand Up @@ -2242,6 +2307,41 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) {
},
err: ErrFlowCheckFailed,
},
{
name: "invalid if subnet manager is set",
newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
env := newValidTransformSubnetTxVerifyEnv(t, ctrl)

// Set dependency expectations.
subnetOwner := fx.NewMockOwner(ctrl)
env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes()
env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1)
env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.GenerateTestID(), make([]byte, 20), nil)
env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1)
env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1)

cfg := &config.Config{
UpgradeConfig: upgradetest.GetConfigWithUpgradeTime(upgradetest.Durango, env.latestForkTime),
MaxStakeDuration: math.MaxInt64,
}
feeCalculator := state.PickFeeCalculator(cfg, env.state)
e := &StandardTxExecutor{
Backend: &Backend{
Config: cfg,
Bootstrapped: &utils.Atomic[bool]{},
Fx: env.fx,
FlowChecker: env.flowChecker,
Ctx: &snow.Context{},
},
FeeCalculator: feeCalculator,
Tx: env.tx,
State: env.state,
}
e.Bootstrapped.Set(true)
return env.unsignedTx, e
},
err: errIsImmutable,
},
{
name: "valid tx",
newExecutor: func(ctrl *gomock.Controller) (*txs.TransformSubnetTx, *StandardTxExecutor) {
Expand All @@ -2251,6 +2351,7 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) {
subnetOwner := fx.NewMockOwner(ctrl)
env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).AnyTimes()
env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1)
env.state.EXPECT().GetSubnetManager(env.unsignedTx.Subnet).Return(ids.Empty, nil, database.ErrNotFound).Times(1)
env.state.EXPECT().GetSubnetTransformation(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound).Times(1)
env.fx.EXPECT().VerifyPermission(env.unsignedTx, env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(nil).Times(1)
env.flowChecker.EXPECT().VerifySpend(
Expand Down
8 changes: 8 additions & 0 deletions vms/platformvm/txs/executor/subnet_tx_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ func verifyPoASubnetAuthorization(
return nil, err
}

_, _, err = chainState.GetSubnetManager(subnetID)
if err == nil {
return nil, fmt.Errorf("%q %w", subnetID, errIsImmutable)
}
if err != database.ErrNotFound {
return nil, err
}

return creds, nil
}

Expand Down

0 comments on commit ac87871

Please sign in to comment.