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 }