github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/add_validator_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package txs
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/ava-labs/avalanchego/ids"
    13  	"github.com/ava-labs/avalanchego/snow/snowtest"
    14  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    15  	"github.com/ava-labs/avalanchego/utils/timer/mockable"
    16  	"github.com/ava-labs/avalanchego/vms/components/avax"
    17  	"github.com/ava-labs/avalanchego/vms/platformvm/reward"
    18  	"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
    19  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    20  )
    21  
    22  func TestAddValidatorTxSyntacticVerify(t *testing.T) {
    23  	require := require.New(t)
    24  	clk := mockable.Clock{}
    25  	ctx := snowtest.Context(t, snowtest.PChainID)
    26  	signers := [][]*secp256k1.PrivateKey{preFundedKeys}
    27  
    28  	var (
    29  		stx            *Tx
    30  		addValidatorTx *AddValidatorTx
    31  		err            error
    32  	)
    33  
    34  	// Case : signed tx is nil
    35  	err = stx.SyntacticVerify(ctx)
    36  	require.ErrorIs(err, ErrNilSignedTx)
    37  
    38  	// Case : unsigned tx is nil
    39  	err = addValidatorTx.SyntacticVerify(ctx)
    40  	require.ErrorIs(err, ErrNilTx)
    41  
    42  	validatorWeight := uint64(2022)
    43  	rewardAddress := preFundedKeys[0].Address()
    44  	inputs := []*avax.TransferableInput{{
    45  		UTXOID: avax.UTXOID{
    46  			TxID:        ids.ID{'t', 'x', 'I', 'D'},
    47  			OutputIndex: 2,
    48  		},
    49  		Asset: avax.Asset{ID: ctx.AVAXAssetID},
    50  		In: &secp256k1fx.TransferInput{
    51  			Amt:   uint64(5678),
    52  			Input: secp256k1fx.Input{SigIndices: []uint32{0}},
    53  		},
    54  	}}
    55  	outputs := []*avax.TransferableOutput{{
    56  		Asset: avax.Asset{ID: ctx.AVAXAssetID},
    57  		Out: &secp256k1fx.TransferOutput{
    58  			Amt: uint64(1234),
    59  			OutputOwners: secp256k1fx.OutputOwners{
    60  				Threshold: 1,
    61  				Addrs:     []ids.ShortID{preFundedKeys[0].Address()},
    62  			},
    63  		},
    64  	}}
    65  	stakes := []*avax.TransferableOutput{{
    66  		Asset: avax.Asset{ID: ctx.AVAXAssetID},
    67  		Out: &stakeable.LockOut{
    68  			Locktime: uint64(clk.Time().Add(time.Second).Unix()),
    69  			TransferableOut: &secp256k1fx.TransferOutput{
    70  				Amt: validatorWeight,
    71  				OutputOwners: secp256k1fx.OutputOwners{
    72  					Threshold: 1,
    73  					Addrs:     []ids.ShortID{preFundedKeys[0].Address()},
    74  				},
    75  			},
    76  		},
    77  	}}
    78  	addValidatorTx = &AddValidatorTx{
    79  		BaseTx: BaseTx{BaseTx: avax.BaseTx{
    80  			NetworkID:    ctx.NetworkID,
    81  			BlockchainID: ctx.ChainID,
    82  			Ins:          inputs,
    83  			Outs:         outputs,
    84  		}},
    85  		Validator: Validator{
    86  			NodeID: ctx.NodeID,
    87  			Start:  uint64(clk.Time().Unix()),
    88  			End:    uint64(clk.Time().Add(time.Hour).Unix()),
    89  			Wght:   validatorWeight,
    90  		},
    91  		StakeOuts: stakes,
    92  		RewardsOwner: &secp256k1fx.OutputOwners{
    93  			Locktime:  0,
    94  			Threshold: 1,
    95  			Addrs:     []ids.ShortID{rewardAddress},
    96  		},
    97  		DelegationShares: reward.PercentDenominator,
    98  	}
    99  
   100  	// Case: valid tx
   101  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   102  	require.NoError(err)
   103  	require.NoError(stx.SyntacticVerify(ctx))
   104  
   105  	// Case: Wrong network ID
   106  	addValidatorTx.SyntacticallyVerified = false
   107  	addValidatorTx.NetworkID++
   108  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   109  	require.NoError(err)
   110  	err = stx.SyntacticVerify(ctx)
   111  	require.ErrorIs(err, avax.ErrWrongNetworkID)
   112  	addValidatorTx.NetworkID--
   113  
   114  	// Case: Stake owner has no addresses
   115  	addValidatorTx.SyntacticallyVerified = false
   116  	addValidatorTx.StakeOuts[0].
   117  		Out.(*stakeable.LockOut).
   118  		TransferableOut.(*secp256k1fx.TransferOutput).
   119  		Addrs = nil
   120  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   121  	require.NoError(err)
   122  	err = stx.SyntacticVerify(ctx)
   123  	require.ErrorIs(err, secp256k1fx.ErrOutputUnspendable)
   124  	addValidatorTx.StakeOuts = stakes
   125  
   126  	// Case: Rewards owner has no addresses
   127  	addValidatorTx.SyntacticallyVerified = false
   128  	addValidatorTx.RewardsOwner.(*secp256k1fx.OutputOwners).Addrs = nil
   129  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   130  	require.NoError(err)
   131  	err = stx.SyntacticVerify(ctx)
   132  	require.ErrorIs(err, secp256k1fx.ErrOutputUnspendable)
   133  	addValidatorTx.RewardsOwner.(*secp256k1fx.OutputOwners).Addrs = []ids.ShortID{rewardAddress}
   134  
   135  	// Case: Too many shares
   136  	addValidatorTx.SyntacticallyVerified = false
   137  	addValidatorTx.DelegationShares++ // 1 more than max amount
   138  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   139  	require.NoError(err)
   140  	err = stx.SyntacticVerify(ctx)
   141  	require.ErrorIs(err, errTooManyShares)
   142  	addValidatorTx.DelegationShares--
   143  }
   144  
   145  func TestAddValidatorTxSyntacticVerifyNotAVAX(t *testing.T) {
   146  	require := require.New(t)
   147  	clk := mockable.Clock{}
   148  	ctx := snowtest.Context(t, snowtest.PChainID)
   149  	signers := [][]*secp256k1.PrivateKey{preFundedKeys}
   150  
   151  	var (
   152  		stx            *Tx
   153  		addValidatorTx *AddValidatorTx
   154  		err            error
   155  	)
   156  
   157  	assetID := ids.GenerateTestID()
   158  	validatorWeight := uint64(2022)
   159  	rewardAddress := preFundedKeys[0].Address()
   160  	inputs := []*avax.TransferableInput{{
   161  		UTXOID: avax.UTXOID{
   162  			TxID:        ids.ID{'t', 'x', 'I', 'D'},
   163  			OutputIndex: 2,
   164  		},
   165  		Asset: avax.Asset{ID: assetID},
   166  		In: &secp256k1fx.TransferInput{
   167  			Amt:   uint64(5678),
   168  			Input: secp256k1fx.Input{SigIndices: []uint32{0}},
   169  		},
   170  	}}
   171  	outputs := []*avax.TransferableOutput{{
   172  		Asset: avax.Asset{ID: assetID},
   173  		Out: &secp256k1fx.TransferOutput{
   174  			Amt: uint64(1234),
   175  			OutputOwners: secp256k1fx.OutputOwners{
   176  				Threshold: 1,
   177  				Addrs:     []ids.ShortID{preFundedKeys[0].Address()},
   178  			},
   179  		},
   180  	}}
   181  	stakes := []*avax.TransferableOutput{{
   182  		Asset: avax.Asset{ID: assetID},
   183  		Out: &stakeable.LockOut{
   184  			Locktime: uint64(clk.Time().Add(time.Second).Unix()),
   185  			TransferableOut: &secp256k1fx.TransferOutput{
   186  				Amt: validatorWeight,
   187  				OutputOwners: secp256k1fx.OutputOwners{
   188  					Threshold: 1,
   189  					Addrs:     []ids.ShortID{preFundedKeys[0].Address()},
   190  				},
   191  			},
   192  		},
   193  	}}
   194  	addValidatorTx = &AddValidatorTx{
   195  		BaseTx: BaseTx{BaseTx: avax.BaseTx{
   196  			NetworkID:    ctx.NetworkID,
   197  			BlockchainID: ctx.ChainID,
   198  			Ins:          inputs,
   199  			Outs:         outputs,
   200  		}},
   201  		Validator: Validator{
   202  			NodeID: ctx.NodeID,
   203  			Start:  uint64(clk.Time().Unix()),
   204  			End:    uint64(clk.Time().Add(time.Hour).Unix()),
   205  			Wght:   validatorWeight,
   206  		},
   207  		StakeOuts: stakes,
   208  		RewardsOwner: &secp256k1fx.OutputOwners{
   209  			Locktime:  0,
   210  			Threshold: 1,
   211  			Addrs:     []ids.ShortID{rewardAddress},
   212  		},
   213  		DelegationShares: reward.PercentDenominator,
   214  	}
   215  
   216  	stx, err = NewSigned(addValidatorTx, Codec, signers)
   217  	require.NoError(err)
   218  
   219  	err = stx.SyntacticVerify(ctx)
   220  	require.ErrorIs(err, errStakeMustBeAVAX)
   221  }
   222  
   223  func TestAddValidatorTxNotDelegatorTx(t *testing.T) {
   224  	txIntf := any((*AddValidatorTx)(nil))
   225  	_, ok := txIntf.(DelegatorTx)
   226  	require.False(t, ok)
   227  }