github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/fee/complexity.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package fee 5 6 import ( 7 "errors" 8 9 "github.com/ava-labs/avalanchego/codec" 10 "github.com/ava-labs/avalanchego/ids" 11 "github.com/ava-labs/avalanchego/utils/crypto/bls" 12 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" 13 "github.com/ava-labs/avalanchego/utils/math" 14 "github.com/ava-labs/avalanchego/utils/wrappers" 15 "github.com/ava-labs/avalanchego/vms/components/avax" 16 "github.com/ava-labs/avalanchego/vms/components/gas" 17 "github.com/ava-labs/avalanchego/vms/components/verify" 18 "github.com/ava-labs/avalanchego/vms/platformvm/fx" 19 "github.com/ava-labs/avalanchego/vms/platformvm/signer" 20 "github.com/ava-labs/avalanchego/vms/platformvm/stakeable" 21 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 22 "github.com/ava-labs/avalanchego/vms/secp256k1fx" 23 ) 24 25 const ( 26 intrinsicValidatorBandwidth = ids.NodeIDLen + // nodeID 27 wrappers.LongLen + // start 28 wrappers.LongLen + // end 29 wrappers.LongLen // weight 30 31 intrinsicSubnetValidatorBandwidth = intrinsicValidatorBandwidth + // validator 32 ids.IDLen // subnetID 33 34 intrinsicOutputBandwidth = ids.IDLen + // assetID 35 wrappers.IntLen // output typeID 36 37 intrinsicStakeableLockedOutputBandwidth = wrappers.LongLen + // locktime 38 wrappers.IntLen // output typeID 39 40 intrinsicSECP256k1FxOutputOwnersBandwidth = wrappers.LongLen + // locktime 41 wrappers.IntLen + // threshold 42 wrappers.IntLen // num addresses 43 44 intrinsicSECP256k1FxOutputBandwidth = wrappers.LongLen + // amount 45 intrinsicSECP256k1FxOutputOwnersBandwidth 46 47 intrinsicInputBandwidth = ids.IDLen + // txID 48 wrappers.IntLen + // output index 49 ids.IDLen + // assetID 50 wrappers.IntLen + // input typeID 51 wrappers.IntLen // credential typeID 52 53 intrinsicStakeableLockedInputBandwidth = wrappers.LongLen + // locktime 54 wrappers.IntLen // input typeID 55 56 intrinsicSECP256k1FxInputBandwidth = wrappers.IntLen + // num indices 57 wrappers.IntLen // num signatures 58 59 intrinsicSECP256k1FxTransferableInputBandwidth = wrappers.LongLen + // amount 60 intrinsicSECP256k1FxInputBandwidth 61 62 intrinsicSECP256k1FxSignatureBandwidth = wrappers.IntLen + // signature index 63 secp256k1.SignatureLen // signature length 64 65 intrinsicPoPBandwidth = bls.PublicKeyLen + // public key 66 bls.SignatureLen // signature 67 68 intrinsicInputDBRead = 1 69 70 intrinsicInputDBWrite = 1 71 intrinsicOutputDBWrite = 1 72 ) 73 74 var ( 75 _ txs.Visitor = (*complexityVisitor)(nil) 76 77 IntrinsicAddPermissionlessValidatorTxComplexities = gas.Dimensions{ 78 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 79 intrinsicValidatorBandwidth + // validator 80 ids.IDLen + // subnetID 81 wrappers.IntLen + // signer typeID 82 wrappers.IntLen + // num stake outs 83 wrappers.IntLen + // validator rewards typeID 84 wrappers.IntLen + // delegator rewards typeID 85 wrappers.IntLen, // delegation shares 86 gas.DBRead: 1, 87 gas.DBWrite: 1, 88 gas.Compute: 0, 89 } 90 IntrinsicAddPermissionlessDelegatorTxComplexities = gas.Dimensions{ 91 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 92 intrinsicValidatorBandwidth + // validator 93 ids.IDLen + // subnetID 94 wrappers.IntLen + // num stake outs 95 wrappers.IntLen, // delegator rewards typeID 96 gas.DBRead: 1, 97 gas.DBWrite: 1, 98 gas.Compute: 0, 99 } 100 IntrinsicAddSubnetValidatorTxComplexities = gas.Dimensions{ 101 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 102 intrinsicSubnetValidatorBandwidth + // subnetValidator 103 wrappers.IntLen + // subnetAuth typeID 104 wrappers.IntLen, // subnetAuthCredential typeID 105 gas.DBRead: 2, 106 gas.DBWrite: 1, 107 gas.Compute: 0, 108 } 109 IntrinsicBaseTxComplexities = gas.Dimensions{ 110 gas.Bandwidth: codec.VersionSize + // codecVersion 111 wrappers.IntLen + // typeID 112 wrappers.IntLen + // networkID 113 ids.IDLen + // blockchainID 114 wrappers.IntLen + // number of outputs 115 wrappers.IntLen + // number of inputs 116 wrappers.IntLen + // length of memo 117 wrappers.IntLen, // number of credentials 118 gas.DBRead: 0, 119 gas.DBWrite: 0, 120 gas.Compute: 0, 121 } 122 IntrinsicCreateChainTxComplexities = gas.Dimensions{ 123 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 124 ids.IDLen + // subnetID 125 wrappers.ShortLen + // chainName length 126 ids.IDLen + // vmID 127 wrappers.IntLen + // num fxIDs 128 wrappers.IntLen + // genesis length 129 wrappers.IntLen + // subnetAuth typeID 130 wrappers.IntLen, // subnetAuthCredential typeID 131 gas.DBRead: 1, 132 gas.DBWrite: 1, 133 gas.Compute: 0, 134 } 135 IntrinsicCreateSubnetTxComplexities = gas.Dimensions{ 136 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 137 wrappers.IntLen, // owner typeID 138 gas.DBRead: 0, 139 gas.DBWrite: 1, 140 gas.Compute: 0, 141 } 142 IntrinsicExportTxComplexities = gas.Dimensions{ 143 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 144 ids.IDLen + // destination chainID 145 wrappers.IntLen, // num exported outputs 146 gas.DBRead: 0, 147 gas.DBWrite: 0, 148 gas.Compute: 0, 149 } 150 IntrinsicImportTxComplexities = gas.Dimensions{ 151 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 152 ids.IDLen + // source chainID 153 wrappers.IntLen, // num importing inputs 154 gas.DBRead: 0, 155 gas.DBWrite: 0, 156 gas.Compute: 0, 157 } 158 IntrinsicRemoveSubnetValidatorTxComplexities = gas.Dimensions{ 159 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 160 ids.NodeIDLen + // nodeID 161 ids.IDLen + // subnetID 162 wrappers.IntLen + // subnetAuth typeID 163 wrappers.IntLen, // subnetAuthCredential typeID 164 gas.DBRead: 2, 165 gas.DBWrite: 1, 166 gas.Compute: 0, 167 } 168 IntrinsicTransferSubnetOwnershipTxComplexities = gas.Dimensions{ 169 gas.Bandwidth: IntrinsicBaseTxComplexities[gas.Bandwidth] + 170 ids.IDLen + // subnetID 171 wrappers.IntLen + // subnetAuth typeID 172 wrappers.IntLen + // owner typeID 173 wrappers.IntLen, // subnetAuthCredential typeID 174 gas.DBRead: 1, 175 gas.DBWrite: 1, 176 gas.Compute: 0, 177 } 178 179 errUnsupportedOutput = errors.New("unsupported output type") 180 errUnsupportedInput = errors.New("unsupported input type") 181 errUnsupportedOwner = errors.New("unsupported owner type") 182 errUnsupportedAuth = errors.New("unsupported auth type") 183 errUnsupportedSigner = errors.New("unsupported signer type") 184 ) 185 186 func TxComplexity(txs ...txs.UnsignedTx) (gas.Dimensions, error) { 187 var ( 188 c complexityVisitor 189 complexity gas.Dimensions 190 ) 191 for _, tx := range txs { 192 c = complexityVisitor{} 193 err := tx.Visit(&c) 194 if err != nil { 195 return gas.Dimensions{}, err 196 } 197 198 complexity, err = complexity.Add(&c.output) 199 if err != nil { 200 return gas.Dimensions{}, err 201 } 202 } 203 return complexity, nil 204 } 205 206 // OutputComplexity returns the complexity outputs add to a transaction. 207 func OutputComplexity(outs ...*avax.TransferableOutput) (gas.Dimensions, error) { 208 var complexity gas.Dimensions 209 for _, out := range outs { 210 outputComplexity, err := outputComplexity(out) 211 if err != nil { 212 return gas.Dimensions{}, err 213 } 214 215 complexity, err = complexity.Add(&outputComplexity) 216 if err != nil { 217 return gas.Dimensions{}, err 218 } 219 } 220 return complexity, nil 221 } 222 223 func outputComplexity(out *avax.TransferableOutput) (gas.Dimensions, error) { 224 complexity := gas.Dimensions{ 225 gas.Bandwidth: intrinsicOutputBandwidth + intrinsicSECP256k1FxOutputBandwidth, 226 gas.DBRead: 0, 227 gas.DBWrite: intrinsicOutputDBWrite, 228 gas.Compute: 0, 229 } 230 231 outIntf := out.Out 232 if stakeableOut, ok := outIntf.(*stakeable.LockOut); ok { 233 complexity[gas.Bandwidth] += intrinsicStakeableLockedOutputBandwidth 234 outIntf = stakeableOut.TransferableOut 235 } 236 237 secp256k1Out, ok := outIntf.(*secp256k1fx.TransferOutput) 238 if !ok { 239 return gas.Dimensions{}, errUnsupportedOutput 240 } 241 242 numAddresses := uint64(len(secp256k1Out.Addrs)) 243 addressBandwidth, err := math.Mul(numAddresses, ids.ShortIDLen) 244 if err != nil { 245 return gas.Dimensions{}, err 246 } 247 complexity[gas.Bandwidth], err = math.Add(complexity[gas.Bandwidth], addressBandwidth) 248 return complexity, err 249 } 250 251 // InputComplexity returns the complexity inputs add to a transaction. 252 // It includes the complexity that the corresponding credentials will add. 253 func InputComplexity(ins ...*avax.TransferableInput) (gas.Dimensions, error) { 254 var complexity gas.Dimensions 255 for _, in := range ins { 256 inputComplexity, err := inputComplexity(in) 257 if err != nil { 258 return gas.Dimensions{}, err 259 } 260 261 complexity, err = complexity.Add(&inputComplexity) 262 if err != nil { 263 return gas.Dimensions{}, err 264 } 265 } 266 return complexity, nil 267 } 268 269 func inputComplexity(in *avax.TransferableInput) (gas.Dimensions, error) { 270 complexity := gas.Dimensions{ 271 gas.Bandwidth: intrinsicInputBandwidth + intrinsicSECP256k1FxTransferableInputBandwidth, 272 gas.DBRead: intrinsicInputDBRead, 273 gas.DBWrite: intrinsicInputDBWrite, 274 gas.Compute: 0, // TODO: Add compute complexity 275 } 276 277 inIntf := in.In 278 if stakeableIn, ok := inIntf.(*stakeable.LockIn); ok { 279 complexity[gas.Bandwidth] += intrinsicStakeableLockedInputBandwidth 280 inIntf = stakeableIn.TransferableIn 281 } 282 283 secp256k1In, ok := inIntf.(*secp256k1fx.TransferInput) 284 if !ok { 285 return gas.Dimensions{}, errUnsupportedInput 286 } 287 288 numSignatures := uint64(len(secp256k1In.SigIndices)) 289 signatureBandwidth, err := math.Mul(numSignatures, intrinsicSECP256k1FxSignatureBandwidth) 290 if err != nil { 291 return gas.Dimensions{}, err 292 } 293 complexity[gas.Bandwidth], err = math.Add(complexity[gas.Bandwidth], signatureBandwidth) 294 return complexity, err 295 } 296 297 // OwnerComplexity returns the complexity an owner adds to a transaction. 298 // It does not include the typeID of the owner. 299 func OwnerComplexity(ownerIntf fx.Owner) (gas.Dimensions, error) { 300 owner, ok := ownerIntf.(*secp256k1fx.OutputOwners) 301 if !ok { 302 return gas.Dimensions{}, errUnsupportedOwner 303 } 304 305 numAddresses := uint64(len(owner.Addrs)) 306 addressBandwidth, err := math.Mul(numAddresses, ids.ShortIDLen) 307 if err != nil { 308 return gas.Dimensions{}, err 309 } 310 311 bandwidth, err := math.Add(addressBandwidth, intrinsicSECP256k1FxOutputOwnersBandwidth) 312 if err != nil { 313 return gas.Dimensions{}, err 314 } 315 316 return gas.Dimensions{ 317 gas.Bandwidth: bandwidth, 318 gas.DBRead: 0, 319 gas.DBWrite: 0, 320 gas.Compute: 0, 321 }, nil 322 } 323 324 // AuthComplexity returns the complexity an authorization adds to a transaction. 325 // It does not include the typeID of the authorization. 326 // It does includes the complexity that the corresponding credential will add. 327 // It does not include the typeID of the credential. 328 func AuthComplexity(authIntf verify.Verifiable) (gas.Dimensions, error) { 329 auth, ok := authIntf.(*secp256k1fx.Input) 330 if !ok { 331 return gas.Dimensions{}, errUnsupportedAuth 332 } 333 334 numSignatures := uint64(len(auth.SigIndices)) 335 signatureBandwidth, err := math.Mul(numSignatures, intrinsicSECP256k1FxSignatureBandwidth) 336 if err != nil { 337 return gas.Dimensions{}, err 338 } 339 340 bandwidth, err := math.Add(signatureBandwidth, intrinsicSECP256k1FxInputBandwidth) 341 if err != nil { 342 return gas.Dimensions{}, err 343 } 344 345 return gas.Dimensions{ 346 gas.Bandwidth: bandwidth, 347 gas.DBRead: 0, 348 gas.DBWrite: 0, 349 gas.Compute: 0, // TODO: Add compute complexity 350 }, nil 351 } 352 353 // SignerComplexity returns the complexity a signer adds to a transaction. 354 // It does not include the typeID of the signer. 355 func SignerComplexity(s signer.Signer) (gas.Dimensions, error) { 356 switch s.(type) { 357 case *signer.Empty: 358 return gas.Dimensions{}, nil 359 case *signer.ProofOfPossession: 360 return gas.Dimensions{ 361 gas.Bandwidth: intrinsicPoPBandwidth, 362 gas.DBRead: 0, 363 gas.DBWrite: 0, 364 gas.Compute: 0, // TODO: Add compute complexity 365 }, nil 366 default: 367 return gas.Dimensions{}, errUnsupportedSigner 368 } 369 } 370 371 type complexityVisitor struct { 372 output gas.Dimensions 373 } 374 375 func (*complexityVisitor) AddDelegatorTx(*txs.AddDelegatorTx) error { 376 return ErrUnsupportedTx 377 } 378 379 func (*complexityVisitor) AddValidatorTx(*txs.AddValidatorTx) error { 380 return ErrUnsupportedTx 381 } 382 383 func (*complexityVisitor) AdvanceTimeTx(*txs.AdvanceTimeTx) error { 384 return ErrUnsupportedTx 385 } 386 387 func (*complexityVisitor) RewardValidatorTx(*txs.RewardValidatorTx) error { 388 return ErrUnsupportedTx 389 } 390 391 func (*complexityVisitor) TransformSubnetTx(*txs.TransformSubnetTx) error { 392 return ErrUnsupportedTx 393 } 394 395 func (c *complexityVisitor) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { 396 // TODO: Should we include additional complexity for subnets? 397 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 398 if err != nil { 399 return err 400 } 401 signerComplexity, err := SignerComplexity(tx.Signer) 402 if err != nil { 403 return err 404 } 405 outputsComplexity, err := OutputComplexity(tx.StakeOuts...) 406 if err != nil { 407 return err 408 } 409 validatorOwnerComplexity, err := OwnerComplexity(tx.ValidatorRewardsOwner) 410 if err != nil { 411 return err 412 } 413 delegatorOwnerComplexity, err := OwnerComplexity(tx.DelegatorRewardsOwner) 414 if err != nil { 415 return err 416 } 417 c.output, err = IntrinsicAddPermissionlessValidatorTxComplexities.Add( 418 &baseTxComplexity, 419 &signerComplexity, 420 &outputsComplexity, 421 &validatorOwnerComplexity, 422 &delegatorOwnerComplexity, 423 ) 424 return err 425 } 426 427 func (c *complexityVisitor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { 428 // TODO: Should we include additional complexity for subnets? 429 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 430 if err != nil { 431 return err 432 } 433 ownerComplexity, err := OwnerComplexity(tx.DelegationRewardsOwner) 434 if err != nil { 435 return err 436 } 437 outputsComplexity, err := OutputComplexity(tx.StakeOuts...) 438 if err != nil { 439 return err 440 } 441 c.output, err = IntrinsicAddPermissionlessDelegatorTxComplexities.Add( 442 &baseTxComplexity, 443 &ownerComplexity, 444 &outputsComplexity, 445 ) 446 return err 447 } 448 449 func (c *complexityVisitor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { 450 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 451 if err != nil { 452 return err 453 } 454 authComplexity, err := AuthComplexity(tx.SubnetAuth) 455 if err != nil { 456 return err 457 } 458 c.output, err = IntrinsicAddSubnetValidatorTxComplexities.Add( 459 &baseTxComplexity, 460 &authComplexity, 461 ) 462 return err 463 } 464 465 func (c *complexityVisitor) BaseTx(tx *txs.BaseTx) error { 466 baseTxComplexity, err := baseTxComplexity(tx) 467 if err != nil { 468 return err 469 } 470 c.output, err = IntrinsicBaseTxComplexities.Add(&baseTxComplexity) 471 return err 472 } 473 474 func (c *complexityVisitor) CreateChainTx(tx *txs.CreateChainTx) error { 475 bandwidth, err := math.Mul(uint64(len(tx.FxIDs)), ids.IDLen) 476 if err != nil { 477 return err 478 } 479 bandwidth, err = math.Add(bandwidth, uint64(len(tx.ChainName))) 480 if err != nil { 481 return err 482 } 483 bandwidth, err = math.Add(bandwidth, uint64(len(tx.GenesisData))) 484 if err != nil { 485 return err 486 } 487 dynamicComplexity := gas.Dimensions{ 488 gas.Bandwidth: bandwidth, 489 gas.DBRead: 0, 490 gas.DBWrite: 0, 491 gas.Compute: 0, 492 } 493 494 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 495 if err != nil { 496 return err 497 } 498 authComplexity, err := AuthComplexity(tx.SubnetAuth) 499 if err != nil { 500 return err 501 } 502 c.output, err = IntrinsicCreateChainTxComplexities.Add( 503 &dynamicComplexity, 504 &baseTxComplexity, 505 &authComplexity, 506 ) 507 return err 508 } 509 510 func (c *complexityVisitor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { 511 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 512 if err != nil { 513 return err 514 } 515 ownerComplexity, err := OwnerComplexity(tx.Owner) 516 if err != nil { 517 return err 518 } 519 c.output, err = IntrinsicCreateSubnetTxComplexities.Add( 520 &baseTxComplexity, 521 &ownerComplexity, 522 ) 523 return err 524 } 525 526 func (c *complexityVisitor) ExportTx(tx *txs.ExportTx) error { 527 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 528 if err != nil { 529 return err 530 } 531 // TODO: Should exported outputs be more complex? 532 outputsComplexity, err := OutputComplexity(tx.ExportedOutputs...) 533 if err != nil { 534 return err 535 } 536 c.output, err = IntrinsicExportTxComplexities.Add( 537 &baseTxComplexity, 538 &outputsComplexity, 539 ) 540 return err 541 } 542 543 func (c *complexityVisitor) ImportTx(tx *txs.ImportTx) error { 544 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 545 if err != nil { 546 return err 547 } 548 // TODO: Should imported inputs be more complex? 549 inputsComplexity, err := InputComplexity(tx.ImportedInputs...) 550 if err != nil { 551 return err 552 } 553 c.output, err = IntrinsicImportTxComplexities.Add( 554 &baseTxComplexity, 555 &inputsComplexity, 556 ) 557 return err 558 } 559 560 func (c *complexityVisitor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error { 561 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 562 if err != nil { 563 return err 564 } 565 authComplexity, err := AuthComplexity(tx.SubnetAuth) 566 if err != nil { 567 return err 568 } 569 c.output, err = IntrinsicRemoveSubnetValidatorTxComplexities.Add( 570 &baseTxComplexity, 571 &authComplexity, 572 ) 573 return err 574 } 575 576 func (c *complexityVisitor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error { 577 baseTxComplexity, err := baseTxComplexity(&tx.BaseTx) 578 if err != nil { 579 return err 580 } 581 authComplexity, err := AuthComplexity(tx.SubnetAuth) 582 if err != nil { 583 return err 584 } 585 ownerComplexity, err := OwnerComplexity(tx.Owner) 586 if err != nil { 587 return err 588 } 589 c.output, err = IntrinsicTransferSubnetOwnershipTxComplexities.Add( 590 &baseTxComplexity, 591 &authComplexity, 592 &ownerComplexity, 593 ) 594 return err 595 } 596 597 func baseTxComplexity(tx *txs.BaseTx) (gas.Dimensions, error) { 598 outputsComplexity, err := OutputComplexity(tx.Outs...) 599 if err != nil { 600 return gas.Dimensions{}, err 601 } 602 inputsComplexity, err := InputComplexity(tx.Ins...) 603 if err != nil { 604 return gas.Dimensions{}, err 605 } 606 complexity, err := outputsComplexity.Add(&inputsComplexity) 607 if err != nil { 608 return gas.Dimensions{}, err 609 } 610 complexity[gas.Bandwidth], err = math.Add( 611 complexity[gas.Bandwidth], 612 uint64(len(tx.Memo)), 613 ) 614 return complexity, err 615 }