github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/executor/create_chain_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 executor 5 6 import ( 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/ava-labs/avalanchego/database" 13 "github.com/ava-labs/avalanchego/ids" 14 "github.com/ava-labs/avalanchego/upgrade/upgradetest" 15 "github.com/ava-labs/avalanchego/utils/constants" 16 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 17 "github.com/ava-labs/avalanchego/utils/hashing" 18 "github.com/ava-labs/avalanchego/utils/set" 19 "github.com/ava-labs/avalanchego/utils/units" 20 "github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest" 21 "github.com/ava-labs/avalanchego/vms/platformvm/state" 22 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 23 "github.com/ava-labs/avalanchego/vms/platformvm/utxo" 24 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 25 ) 26 27 // Ensure Execute fails when there are not enough control sigs 28 func TestCreateChainTxInsufficientControlSigs(t *testing.T) { 29 require := require.New(t) 30 env := newEnvironment(t, upgradetest.Banff) 31 env.ctx.Lock.Lock() 32 defer env.ctx.Lock.Unlock() 33 34 subnetID := testSubnet1.ID() 35 wallet := newWallet(t, env, walletConfig{ 36 subnetIDs: []ids.ID{subnetID}, 37 }) 38 39 tx, err := wallet.IssueCreateChainTx( 40 subnetID, 41 nil, 42 constants.AVMID, 43 nil, 44 "chain name", 45 ) 46 require.NoError(err) 47 48 // Remove a signature 49 tx.Creds[0].(*secp256k1fx.Credential).Sigs = tx.Creds[0].(*secp256k1fx.Credential).Sigs[1:] 50 51 stateDiff, err := state.NewDiff(lastAcceptedID, env) 52 require.NoError(err) 53 54 feeCalculator := state.PickFeeCalculator(env.config, stateDiff) 55 executor := StandardTxExecutor{ 56 Backend: &env.backend, 57 FeeCalculator: feeCalculator, 58 State: stateDiff, 59 Tx: tx, 60 } 61 err = tx.Unsigned.Visit(&executor) 62 require.ErrorIs(err, errUnauthorizedSubnetModification) 63 } 64 65 // Ensure Execute fails when an incorrect control signature is given 66 func TestCreateChainTxWrongControlSig(t *testing.T) { 67 require := require.New(t) 68 env := newEnvironment(t, upgradetest.Banff) 69 env.ctx.Lock.Lock() 70 defer env.ctx.Lock.Unlock() 71 72 subnetID := testSubnet1.ID() 73 wallet := newWallet(t, env, walletConfig{ 74 subnetIDs: []ids.ID{subnetID}, 75 }) 76 77 tx, err := wallet.IssueCreateChainTx( 78 subnetID, 79 nil, 80 constants.AVMID, 81 nil, 82 "chain name", 83 ) 84 require.NoError(err) 85 86 // Generate new, random key to sign tx with 87 key, err := secp256k1.NewPrivateKey() 88 require.NoError(err) 89 90 // Replace a valid signature with one from another key 91 sig, err := key.SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes())) 92 require.NoError(err) 93 copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig) 94 95 stateDiff, err := state.NewDiff(lastAcceptedID, env) 96 require.NoError(err) 97 98 feeCalculator := state.PickFeeCalculator(env.config, stateDiff) 99 executor := StandardTxExecutor{ 100 Backend: &env.backend, 101 FeeCalculator: feeCalculator, 102 State: stateDiff, 103 Tx: tx, 104 } 105 err = tx.Unsigned.Visit(&executor) 106 require.ErrorIs(err, errUnauthorizedSubnetModification) 107 } 108 109 // Ensure Execute fails when the Subnet the blockchain specifies as 110 // its validator set doesn't exist 111 func TestCreateChainTxNoSuchSubnet(t *testing.T) { 112 require := require.New(t) 113 env := newEnvironment(t, upgradetest.Banff) 114 env.ctx.Lock.Lock() 115 defer env.ctx.Lock.Unlock() 116 117 subnetID := testSubnet1.ID() 118 wallet := newWallet(t, env, walletConfig{ 119 subnetIDs: []ids.ID{subnetID}, 120 }) 121 122 tx, err := wallet.IssueCreateChainTx( 123 subnetID, 124 nil, 125 constants.AVMID, 126 nil, 127 "chain name", 128 ) 129 require.NoError(err) 130 131 tx.Unsigned.(*txs.CreateChainTx).SubnetID = ids.GenerateTestID() 132 133 stateDiff, err := state.NewDiff(lastAcceptedID, env) 134 require.NoError(err) 135 136 builderDiff, err := state.NewDiffOn(stateDiff) 137 require.NoError(err) 138 139 feeCalculator := state.PickFeeCalculator(env.config, builderDiff) 140 executor := StandardTxExecutor{ 141 Backend: &env.backend, 142 FeeCalculator: feeCalculator, 143 State: stateDiff, 144 Tx: tx, 145 } 146 err = tx.Unsigned.Visit(&executor) 147 require.ErrorIs(err, database.ErrNotFound) 148 } 149 150 // Ensure valid tx passes semanticVerify 151 func TestCreateChainTxValid(t *testing.T) { 152 require := require.New(t) 153 env := newEnvironment(t, upgradetest.Banff) 154 env.ctx.Lock.Lock() 155 defer env.ctx.Lock.Unlock() 156 157 subnetID := testSubnet1.ID() 158 wallet := newWallet(t, env, walletConfig{ 159 subnetIDs: []ids.ID{subnetID}, 160 }) 161 162 tx, err := wallet.IssueCreateChainTx( 163 subnetID, 164 nil, 165 constants.AVMID, 166 nil, 167 "chain name", 168 ) 169 require.NoError(err) 170 171 stateDiff, err := state.NewDiff(lastAcceptedID, env) 172 require.NoError(err) 173 174 builderDiff, err := state.NewDiffOn(stateDiff) 175 require.NoError(err) 176 177 feeCalculator := state.PickFeeCalculator(env.config, builderDiff) 178 executor := StandardTxExecutor{ 179 Backend: &env.backend, 180 FeeCalculator: feeCalculator, 181 State: stateDiff, 182 Tx: tx, 183 } 184 require.NoError(tx.Unsigned.Visit(&executor)) 185 } 186 187 func TestCreateChainTxAP3FeeChange(t *testing.T) { 188 ap3Time := genesistest.DefaultValidatorStartTime.Add(time.Hour) 189 tests := []struct { 190 name string 191 time time.Time 192 fee uint64 193 expectedError error 194 }{ 195 { 196 name: "pre-fork - correctly priced", 197 time: genesistest.DefaultValidatorStartTime, 198 fee: 0, 199 expectedError: nil, 200 }, 201 { 202 name: "post-fork - incorrectly priced", 203 time: ap3Time, 204 fee: 100*defaultTxFee - 1*units.NanoAvax, 205 expectedError: utxo.ErrInsufficientUnlockedFunds, 206 }, 207 { 208 name: "post-fork - correctly priced", 209 time: ap3Time, 210 fee: 100 * defaultTxFee, 211 expectedError: nil, 212 }, 213 } 214 for _, test := range tests { 215 t.Run(test.name, func(t *testing.T) { 216 require := require.New(t) 217 218 env := newEnvironment(t, upgradetest.Banff) 219 env.config.UpgradeConfig.ApricotPhase3Time = ap3Time 220 221 addrs := set.NewSet[ids.ShortID](len(genesistest.DefaultFundedKeys)) 222 for _, key := range genesistest.DefaultFundedKeys { 223 addrs.Add(key.Address()) 224 } 225 226 env.state.SetTimestamp(test.time) // to duly set fee 227 228 config := *env.config 229 config.StaticFeeConfig.CreateBlockchainTxFee = test.fee 230 subnetID := testSubnet1.ID() 231 wallet := newWallet(t, env, walletConfig{ 232 config: &config, 233 subnetIDs: []ids.ID{subnetID}, 234 }) 235 236 tx, err := wallet.IssueCreateChainTx( 237 subnetID, 238 nil, 239 ids.GenerateTestID(), 240 nil, 241 "", 242 ) 243 require.NoError(err) 244 245 stateDiff, err := state.NewDiff(lastAcceptedID, env) 246 require.NoError(err) 247 248 stateDiff.SetTimestamp(test.time) 249 250 feeCalculator := state.PickFeeCalculator(env.config, stateDiff) 251 executor := StandardTxExecutor{ 252 Backend: &env.backend, 253 FeeCalculator: feeCalculator, 254 State: stateDiff, 255 Tx: tx, 256 } 257 err = tx.Unsigned.Visit(&executor) 258 require.ErrorIs(err, test.expectedError) 259 }) 260 } 261 } 262 263 func TestEtnaCreateChainTxInvalidWithManagedSubnet(t *testing.T) { 264 require := require.New(t) 265 env := newEnvironment(t, upgradetest.Etna) 266 env.ctx.Lock.Lock() 267 defer env.ctx.Lock.Unlock() 268 269 subnetID := testSubnet1.ID() 270 wallet := newWallet(t, env, walletConfig{ 271 subnetIDs: []ids.ID{subnetID}, 272 }) 273 274 tx, err := wallet.IssueCreateChainTx( 275 subnetID, 276 nil, 277 constants.AVMID, 278 nil, 279 "chain name", 280 ) 281 require.NoError(err) 282 283 stateDiff, err := state.NewDiff(lastAcceptedID, env) 284 require.NoError(err) 285 286 builderDiff, err := state.NewDiffOn(stateDiff) 287 require.NoError(err) 288 289 stateDiff.SetSubnetManager(subnetID, ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'}) 290 291 feeCalculator := state.PickFeeCalculator(env.config, builderDiff) 292 executor := StandardTxExecutor{ 293 Backend: &env.backend, 294 FeeCalculator: feeCalculator, 295 State: stateDiff, 296 Tx: tx, 297 } 298 err = tx.Unsigned.Visit(&executor) 299 require.ErrorIs(err, errIsImmutable) 300 }