github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/auth/ante.go (about) 1 package auth 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "math/big" 7 8 "github.com/gnolang/gno/tm2/pkg/amino" 9 "github.com/gnolang/gno/tm2/pkg/crypto" 10 "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" 11 "github.com/gnolang/gno/tm2/pkg/crypto/multisig" 12 "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" 13 "github.com/gnolang/gno/tm2/pkg/sdk" 14 "github.com/gnolang/gno/tm2/pkg/std" 15 "github.com/gnolang/gno/tm2/pkg/store" 16 ) 17 18 var ( 19 // simulation signature values used to estimate gas consumption 20 simSecp256k1Pubkey secp256k1.PubKeySecp256k1 21 simSecp256k1Sig [64]byte 22 ) 23 24 func init() { 25 // This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation 26 bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") 27 copy(simSecp256k1Pubkey[:], bz) 28 } 29 30 // SignatureVerificationGasConsumer is the type of function that is used to both consume gas when verifying signatures 31 // and also to accept or reject different types of PubKey's. This is where apps can define their own PubKey 32 type SignatureVerificationGasConsumer = func(meter store.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result 33 34 type AnteOptions struct { 35 // If verifyGenesisSignatures is false, does not check signatures when Height==0. 36 // This is useful for development, and maybe production chains. 37 // Always check your settings and inspect genesis transactions. 38 VerifyGenesisSignatures bool 39 } 40 41 // NewAnteHandler returns an AnteHandler that checks and increments sequence 42 // numbers, checks signatures & account numbers, and deducts fees from the first 43 // signer. 44 func NewAnteHandler(ak AccountKeeper, bank BankKeeperI, sigGasConsumer SignatureVerificationGasConsumer, opts AnteOptions) sdk.AnteHandler { 45 return func( 46 ctx sdk.Context, tx std.Tx, simulate bool, 47 ) (newCtx sdk.Context, res sdk.Result, abort bool) { 48 // Ensure that the gas wanted is not greater than the max allowed. 49 consParams := ctx.ConsensusParams() 50 if consParams.Block.MaxGas == -1 { 51 // no gas bounds (not recommended) 52 } else if consParams.Block.MaxGas < tx.Fee.GasWanted { 53 // tx gas-wanted too large. 54 res = abciResult(std.ErrInvalidGasWanted( 55 fmt.Sprintf( 56 "invalid gas-wanted; got: %d block-max-gas: %d", 57 tx.Fee.GasWanted, consParams.Block.MaxGas, 58 ), 59 )) 60 return ctx, res, true 61 } 62 63 // Ensure that the provided fees meet a minimum threshold for the validator, 64 // if this is a CheckTx. This is only for local mempool purposes, and thus 65 // is only run upon checktx. 66 if ctx.IsCheckTx() && !simulate { 67 res := EnsureSufficientMempoolFees(ctx, tx.Fee) 68 if !res.IsOK() { 69 return ctx, res, true 70 } 71 } 72 73 newCtx = SetGasMeter(simulate, ctx, tx.Fee.GasWanted) 74 75 // AnteHandlers must have their own defer/recover in order for the BaseApp 76 // to know how much gas was used! This is because the GasMeter is created in 77 // the AnteHandler, but if it panics the context won't be set properly in 78 // runTx's recover call. 79 defer func() { 80 if r := recover(); r != nil { 81 switch ex := r.(type) { 82 case store.OutOfGasException: 83 log := fmt.Sprintf( 84 "out of gas in location: %v; gasWanted: %d, gasUsed: %d", 85 ex.Descriptor, tx.Fee.GasWanted, newCtx.GasMeter().GasConsumed(), 86 ) 87 res = abciResult(std.ErrOutOfGas(log)) 88 89 res.GasWanted = tx.Fee.GasWanted 90 res.GasUsed = newCtx.GasMeter().GasConsumed() 91 abort = true 92 default: 93 panic(r) 94 } 95 } 96 }() 97 98 // Get params from context. 99 params := ctx.Value(AuthParamsContextKey{}).(Params) 100 if res := ValidateSigCount(tx, params); !res.IsOK() { 101 return newCtx, res, true 102 } 103 104 if err := tx.ValidateBasic(); err != nil { 105 return newCtx, abciResult(err), true 106 } 107 108 newCtx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*store.Gas(len(newCtx.TxBytes())), "txSize") 109 110 if res := ValidateMemo(tx, params); !res.IsOK() { 111 return newCtx, res, true 112 } 113 114 // stdSigs contains the sequence number, account number, and signatures. 115 // When simulating, this would just be a 0-length slice. 116 signerAddrs := tx.GetSigners() 117 signerAccs := make([]std.Account, len(signerAddrs)) 118 isGenesis := ctx.BlockHeight() == 0 119 120 // fetch first signer, who's going to pay the fees 121 signerAccs[0], res = GetSignerAcc(newCtx, ak, signerAddrs[0]) 122 if !res.IsOK() { 123 return newCtx, res, true 124 } 125 126 // deduct the fees 127 if !tx.Fee.GasFee.IsZero() { 128 res = DeductFees(bank, newCtx, signerAccs[0], std.Coins{tx.Fee.GasFee}) 129 if !res.IsOK() { 130 return newCtx, res, true 131 } 132 133 // reload the account as fees have been deducted 134 signerAccs[0] = ak.GetAccount(newCtx, signerAccs[0].GetAddress()) 135 } 136 137 // stdSigs contains the sequence number, account number, and signatures. 138 // When simulating, this would just be a 0-length slice. 139 stdSigs := tx.GetSignatures() 140 141 for i := 0; i < len(stdSigs); i++ { 142 // skip the fee payer, account is cached and fees were deducted already 143 if i != 0 { 144 signerAccs[i], res = GetSignerAcc(newCtx, ak, signerAddrs[i]) 145 if !res.IsOK() { 146 return newCtx, res, true 147 } 148 } 149 150 // check signature, return account with incremented nonce 151 sacc := signerAccs[i] 152 if isGenesis && !opts.VerifyGenesisSignatures { 153 // No signatures are needed for genesis. 154 } else { 155 // Check signature 156 signBytes, err := GetSignBytes(newCtx.ChainID(), tx, sacc, isGenesis) 157 if err != nil { 158 return newCtx, res, true 159 } 160 161 signerAccs[i], res = processSig(newCtx, sacc, stdSigs[i], signBytes, simulate, params, sigGasConsumer) 162 if !res.IsOK() { 163 return newCtx, res, true 164 } 165 } 166 ak.SetAccount(newCtx, signerAccs[i]) 167 } 168 169 // TODO: tx tags (?) 170 return newCtx, sdk.Result{GasWanted: tx.Fee.GasWanted}, false // continue... 171 } 172 } 173 174 // GetSignerAcc returns an account for a given address that is expected to sign 175 // a transaction. 176 func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr crypto.Address) (std.Account, sdk.Result) { 177 if acc := ak.GetAccount(ctx, addr); acc != nil { 178 return acc, sdk.Result{} 179 } 180 return nil, abciResult(std.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr))) 181 } 182 183 // ValidateSigCount validates that the transaction has a valid cumulative total 184 // amount of signatures. 185 func ValidateSigCount(tx std.Tx, params Params) sdk.Result { 186 stdSigs := tx.GetSignatures() 187 188 sigCount := 0 189 for i := 0; i < len(stdSigs); i++ { 190 sigCount += std.CountSubKeys(stdSigs[i].PubKey) 191 if int64(sigCount) > params.TxSigLimit { 192 return abciResult(std.ErrTooManySignatures( 193 fmt.Sprintf("signatures: %d, limit: %d", sigCount, params.TxSigLimit), 194 )) 195 } 196 } 197 198 return sdk.Result{} 199 } 200 201 // ValidateMemo validates the memo size. 202 func ValidateMemo(tx std.Tx, params Params) sdk.Result { 203 memoLength := len(tx.GetMemo()) 204 if int64(memoLength) > params.MaxMemoBytes { 205 return abciResult(std.ErrMemoTooLarge( 206 fmt.Sprintf( 207 "maximum number of bytes is %d but received %d bytes", 208 params.MaxMemoBytes, memoLength, 209 ), 210 )) 211 } 212 213 return sdk.Result{} 214 } 215 216 // verify the signature and increment the sequence. If the account doesn't 217 // have a pubkey, set it. 218 func processSig( 219 ctx sdk.Context, acc std.Account, sig std.Signature, signBytes []byte, simulate bool, params Params, 220 sigGasConsumer SignatureVerificationGasConsumer, 221 ) (updatedAcc std.Account, res sdk.Result) { 222 pubKey, res := ProcessPubKey(acc, sig, simulate) 223 if !res.IsOK() { 224 return nil, res 225 } 226 227 err := acc.SetPubKey(pubKey) 228 if err != nil { 229 return nil, abciResult(std.ErrInternal("setting PubKey on signer's account")) 230 } 231 232 if simulate { 233 // Simulated txs should not contain a signature and are not required to 234 // contain a pubkey, so we must account for tx size of including a 235 // std.Signature (Amino encoding) and simulate gas consumption 236 // (assuming a SECP256k1 simulation key). 237 consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params) 238 } 239 240 if res := sigGasConsumer(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() { 241 return nil, res 242 } 243 244 if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) { 245 return nil, abciResult(std.ErrUnauthorized("signature verification failed; verify correct account, sequence, and chain-id")) 246 } 247 248 if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { 249 panic(err) 250 } 251 252 return acc, res 253 } 254 255 func consumeSimSigGas(gasmeter store.GasMeter, pubkey crypto.PubKey, sig std.Signature, params Params) { 256 simSig := std.Signature{PubKey: pubkey} 257 if len(sig.Signature) == 0 { 258 simSig.Signature = simSecp256k1Sig[:] 259 } 260 261 sigBz := amino.MustMarshalSized(simSig) 262 cost := store.Gas(len(sigBz) + 6) 263 264 // If the pubkey is a multi-signature pubkey, then we estimate for the maximum 265 // number of signers. 266 if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok { 267 cost *= params.TxSigLimit 268 } 269 270 gasmeter.ConsumeGas(params.TxSizeCostPerByte*cost, "txSize") 271 } 272 273 // ProcessPubKey verifies that the given account address matches that of the 274 // std.Signature. In addition, it will set the public key of the account if it 275 // has not been set. 276 func ProcessPubKey(acc std.Account, sig std.Signature, simulate bool) (crypto.PubKey, sdk.Result) { 277 // If pubkey is not known for account, set it from the std.Signature. 278 pubKey := acc.GetPubKey() 279 if simulate { 280 // In simulate mode the transaction comes with no signatures, thus if the 281 // account's pubkey is nil, both signature verification and gasKVStore.Set() 282 // shall consume the largest amount, i.e. it takes more gas to verify 283 // secp256k1 keys than ed25519 ones. 284 if pubKey == nil { 285 return simSecp256k1Pubkey, sdk.Result{} 286 } 287 288 return pubKey, sdk.Result{} 289 } 290 291 if pubKey == nil { 292 pubKey = sig.PubKey 293 if pubKey == nil { 294 return nil, abciResult(std.ErrInvalidPubKey("PubKey not found")) 295 } 296 297 if pubKey.Address() != acc.GetAddress() { 298 return nil, abciResult(std.ErrInvalidPubKey( 299 fmt.Sprintf("PubKey does not match Signer address %s", acc.GetAddress()))) 300 } 301 } 302 303 return pubKey, sdk.Result{} 304 } 305 306 // DefaultSigVerificationGasConsumer is the default implementation of 307 // SignatureVerificationGasConsumer. It consumes gas for signature verification 308 // based upon the public key type. The cost is fetched from the given params 309 // and is matched by the concrete type. 310 func DefaultSigVerificationGasConsumer( 311 meter store.GasMeter, sig []byte, pubkey crypto.PubKey, params Params, 312 ) sdk.Result { 313 switch pubkey := pubkey.(type) { 314 case ed25519.PubKeyEd25519: 315 meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") 316 return abciResult(std.ErrInvalidPubKey("ED25519 public keys are unsupported")) 317 318 case secp256k1.PubKeySecp256k1: 319 meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") 320 return sdk.Result{} 321 322 case multisig.PubKeyMultisigThreshold: 323 var multisignature multisig.Multisignature 324 amino.MustUnmarshal(sig, &multisignature) 325 326 consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params) 327 return sdk.Result{} 328 329 default: 330 return abciResult(std.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %T", pubkey))) 331 } 332 } 333 334 func consumeMultisignatureVerificationGas(meter store.GasMeter, 335 sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold, 336 params Params, 337 ) { 338 size := sig.BitArray.Size() 339 sigIndex := 0 340 for i := 0; i < size; i++ { 341 if sig.BitArray.GetIndex(i) { 342 DefaultSigVerificationGasConsumer(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params) 343 sigIndex++ 344 } 345 } 346 } 347 348 // DeductFees deducts fees from the given account. 349 // 350 // NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because 351 // the CoinKeeper doesn't give us accounts), but it seems easier to do this. 352 func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coins) sdk.Result { 353 coins := acc.GetCoins() 354 355 if !fees.IsValid() { 356 return abciResult(std.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", fees))) 357 } 358 359 // verify the account has enough funds to pay for fees 360 diff := coins.SubUnsafe(fees) 361 if !diff.IsValid() { 362 return abciResult(std.ErrInsufficientFunds( 363 fmt.Sprintf("insufficient funds to pay for fees; %s < %s", coins, fees), 364 )) 365 } 366 367 err := bank.SendCoins(ctx, acc.GetAddress(), FeeCollectorAddress(), fees) 368 if err != nil { 369 return abciResult(err) 370 } 371 372 return sdk.Result{} 373 } 374 375 // EnsureSufficientMempoolFees verifies that the given transaction has supplied 376 // enough fees to cover a proposer's minimum fees. A result object is returned 377 // indicating success or failure. 378 // 379 // Contract: This should only be called during CheckTx as it cannot be part of 380 // consensus. 381 func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { 382 minGasPrices := ctx.MinGasPrices() 383 if len(minGasPrices) == 0 { 384 // no minimum gas price (not recommended) 385 // TODO: allow for selective filtering of 0 fee txs. 386 return sdk.Result{} 387 } else { 388 fgw := big.NewInt(fee.GasWanted) 389 fga := big.NewInt(fee.GasFee.Amount) 390 fgd := fee.GasFee.Denom 391 392 for _, gp := range minGasPrices { 393 gpg := big.NewInt(gp.Gas) 394 gpa := big.NewInt(gp.Price.Amount) 395 gpd := gp.Price.Denom 396 397 if fgd == gpd { 398 prod1 := big.NewInt(0).Mul(fga, gpg) // fee amount * price gas 399 prod2 := big.NewInt(0).Mul(fgw, gpa) // fee gas * price amount 400 // This is equivalent to checking 401 // That the Fee / GasWanted ratio is greater than or equal to the minimum GasPrice per gas. 402 // This approach helps us avoid dealing with configurations where the value of 403 // the minimum gas price is set to 0.00001ugnot/gas. 404 if prod1.Cmp(prod2) >= 0 { 405 return sdk.Result{} 406 } else { 407 return abciResult(std.ErrInsufficientFee( 408 fmt.Sprintf( 409 "insufficient fees; got: %q required: %q", fee.GasFee, gp, 410 ), 411 )) 412 } 413 } 414 } 415 } 416 417 return abciResult(std.ErrInsufficientFee( 418 fmt.Sprintf( 419 "insufficient fees; got: %q required (one of): %q", fee.GasFee, minGasPrices, 420 ), 421 )) 422 } 423 424 // SetGasMeter returns a new context with a gas meter set from a given context. 425 func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit int64) sdk.Context { 426 // In various cases such as simulation and during the genesis block, we do not 427 // meter any gas utilization. 428 if simulate || ctx.BlockHeight() == 0 { 429 return ctx.WithGasMeter(store.NewInfiniteGasMeter()) 430 } 431 432 return ctx.WithGasMeter(store.NewGasMeter(gasLimit)) 433 } 434 435 // GetSignBytes returns a slice of bytes to sign over for a given transaction 436 // and an account. 437 func GetSignBytes(chainID string, tx std.Tx, acc std.Account, genesis bool) ([]byte, error) { 438 var accNum uint64 439 if !genesis { 440 accNum = acc.GetAccountNumber() 441 } 442 443 return std.GetSignaturePayload( 444 std.SignDoc{ 445 ChainID: chainID, 446 AccountNumber: accNum, 447 Sequence: acc.GetSequence(), 448 Fee: tx.Fee, 449 Msgs: tx.Msgs, 450 Memo: tx.Memo, 451 }, 452 ) 453 } 454 455 func abciResult(err error) sdk.Result { 456 return sdk.ABCIResultFromError(err) 457 }