github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/ante/account.go (about) 1 package ante 2 3 import ( 4 "bytes" 5 "math/big" 6 "strconv" 7 "strings" 8 9 "github.com/ethereum/go-ethereum/common" 10 ethcore "github.com/ethereum/go-ethereum/core" 11 ethtypes "github.com/ethereum/go-ethereum/core/types" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp" 13 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 14 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/innertx" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 19 evmtypes "github.com/fibonacci-chain/fbc/x/evm/types" 20 ) 21 22 type accountKeeperInterface interface { 23 SetAccount(ctx sdk.Context, acc exported.Account) 24 } 25 26 type AccountAnteDecorator struct { 27 ak auth.AccountKeeper 28 sk types.SupplyKeeper 29 evmKeeper EVMKeeper 30 } 31 32 // NewAccountVerificationDecorator creates a new AccountVerificationDecorator 33 func NewAccountAnteDecorator(ak auth.AccountKeeper, ek EVMKeeper, sk types.SupplyKeeper) AccountAnteDecorator { 34 return AccountAnteDecorator{ 35 ak: ak, 36 sk: sk, 37 evmKeeper: ek, 38 } 39 } 40 41 func accountVerification(ctx *sdk.Context, acc exported.Account, tx *evmtypes.MsgEthereumTx) error { 42 if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 { 43 return sdkerrors.Wrapf( 44 sdkerrors.ErrInvalidSequence, 45 "invalid account number for height zero (got %d)", acc.GetAccountNumber(), 46 ) 47 } 48 49 const evmDenom = sdk.DefaultBondDenom 50 51 feeInts := feeIntsPool.Get().(*[2]big.Int) 52 defer feeIntsPool.Put(feeInts) 53 54 // validate sender has enough funds to pay for gas cost 55 balance := acc.GetCoins().AmountOf(evmDenom) 56 if balance.Int.Cmp(tx.CalcCostTo(&feeInts[0])) < 0 { 57 return sdkerrors.Wrapf( 58 sdkerrors.ErrInsufficientFunds, 59 "sender balance < tx gas cost (%s%s < %s%s)", balance.String(), evmDenom, sdk.NewDecFromBigIntWithPrec(tx.Cost(), sdk.Precision).String(), evmDenom, 60 ) 61 } 62 return nil 63 } 64 65 func nonceVerificationInCheckTx(ctx sdk.Context, seq uint64, msgEthTx *evmtypes.MsgEthereumTx, isReCheckTx bool) error { 66 if isReCheckTx { 67 // recheckTx mode 68 // sequence must strictly increasing 69 if msgEthTx.Data.AccountNonce != seq { 70 return sdkerrors.Wrapf( 71 sdkerrors.ErrInvalidSequence, 72 "invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq, 73 ) 74 } 75 } else { 76 if baseapp.IsMempoolEnablePendingPool() { 77 if msgEthTx.Data.AccountNonce < seq { 78 return sdkerrors.Wrapf( 79 sdkerrors.ErrInvalidSequence, 80 "invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq, 81 ) 82 } 83 } else { 84 // checkTx mode 85 checkTxModeNonce := seq 86 if !baseapp.IsMempoolEnableRecheck() { 87 // if is enable recheck, the sequence of checkState will increase after commit(), so we do not need 88 // to add pending txs len in the mempool. 89 // but, if disable recheck, we will not increase sequence of checkState (even in force recheck case, we 90 // will also reset checkState), so we will need to add pending txs len to get the right nonce 91 gPool := baseapp.GetGlobalMempool() 92 if gPool != nil { 93 addr := msgEthTx.GetSender(ctx) 94 if pendingNonce, ok := gPool.GetPendingNonce(addr); ok { 95 checkTxModeNonce = pendingNonce + 1 96 } 97 } 98 } 99 100 if baseapp.IsMempoolEnableSort() { 101 if msgEthTx.Data.AccountNonce < seq || msgEthTx.Data.AccountNonce > checkTxModeNonce { 102 accNonceStr := strconv.FormatUint(msgEthTx.Data.AccountNonce, 10) 103 seqStr := strconv.FormatUint(seq, 10) 104 checkTxModeNonceStr := strconv.FormatUint(checkTxModeNonce, 10) 105 106 errStr := strings.Join([]string{ 107 "invalid nonce; got ", accNonceStr, 108 ", expected in the range of [", seqStr, ", ", checkTxModeNonceStr, "]"}, 109 "") 110 111 return sdkerrors.WrapNoStack(sdkerrors.ErrInvalidSequence, errStr) 112 } 113 } else { 114 if msgEthTx.Data.AccountNonce != checkTxModeNonce { 115 accNonceStr := strconv.FormatUint(msgEthTx.Data.AccountNonce, 10) 116 checkTxModeNonceStr := strconv.FormatUint(checkTxModeNonce, 10) 117 118 errStr := strings.Join([]string{ 119 "invalid nonce; got ", accNonceStr, ", expected ", checkTxModeNonceStr}, 120 "") 121 122 return sdkerrors.WrapNoStack(sdkerrors.ErrInvalidSequence, errStr) 123 } 124 } 125 } 126 } 127 return nil 128 } 129 130 func nonceVerification(ctx sdk.Context, acc exported.Account, msgEthTx *evmtypes.MsgEthereumTx) (sdk.Context, error) { 131 seq := acc.GetSequence() 132 // if multiple transactions are submitted in succession with increasing nonces, 133 // all will be rejected except the first, since the first needs to be included in a block 134 // before the sequence increments 135 if ctx.IsCheckTx() { 136 ctx.SetAccountNonce(seq) 137 // will be checkTx and RecheckTx mode 138 err := nonceVerificationInCheckTx(ctx, seq, msgEthTx, ctx.IsReCheckTx()) 139 if err != nil { 140 return ctx, err 141 } 142 } else { 143 // only deliverTx mode 144 if msgEthTx.Data.AccountNonce != seq { 145 return ctx, sdkerrors.Wrapf( 146 sdkerrors.ErrInvalidSequence, 147 "invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq, 148 ) 149 } 150 } 151 return ctx, nil 152 } 153 154 func ethGasConsume(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx *sdk.Context, acc exported.Account, accGetGas sdk.Gas, msgEthTx *evmtypes.MsgEthereumTx, simulate bool) error { 155 gasLimit := msgEthTx.GetGas() 156 gas, err := ethcore.IntrinsicGas(msgEthTx.Data.Payload, []ethtypes.AccessTuple{}, msgEthTx.To() == nil, true, false) 157 if err != nil { 158 return sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") 159 } 160 161 // intrinsic gas verification during CheckTx 162 if ctx.IsCheckTx() && gasLimit < gas { 163 return sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas) 164 } 165 166 // Charge sender for gas up to limit 167 if gasLimit != 0 { 168 feeInts := feeIntsPool.Get().(*[2]big.Int) 169 defer feeIntsPool.Put(feeInts) 170 // Cost calculates the fees paid to validators based on gas limit and price 171 cost := (&feeInts[0]).SetUint64(gasLimit) 172 cost = cost.Mul(msgEthTx.Data.Price, cost) 173 174 const evmDenom = sdk.DefaultBondDenom 175 176 feeAmt := sdk.NewDecCoinsFromDec(evmDenom, sdk.NewDecWithBigIntAndPrec(cost, sdk.Precision)) 177 178 ctx.UpdateFromAccountCache(acc, accGetGas) 179 180 err = deductFees(ik, ak, sk, *ctx, acc, feeAmt) 181 if err != nil { 182 return err 183 } 184 } 185 186 // Set gas meter after ante handler to ignore gaskv costs 187 auth.SetGasMeter(simulate, ctx, gasLimit) 188 return nil 189 } 190 191 func deductFees(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error { 192 blockTime := ctx.BlockTime() 193 coins := acc.GetCoins() 194 195 if !fees.IsValid() { 196 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) 197 } 198 199 // verify the account has enough funds to pay for fees 200 balance, hasNeg := coins.SafeSub(fees) 201 if hasNeg { 202 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, 203 "insufficient funds to pay for fees; %s < %s", coins, fees) 204 } 205 206 // Validate the account has enough "spendable" coins as this will cover cases 207 // such as vesting accounts. 208 spendableCoins := acc.SpendableCoins(blockTime) 209 if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { 210 return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, 211 "insufficient funds to pay for fees; %s < %s", spendableCoins, fees) 212 } 213 214 // set coins and record innertx 215 err := acc.SetCoins(balance) 216 if !ctx.IsCheckTx() { 217 toAcc := sk.GetModuleAddress(types.FeeCollectorName) 218 ik.UpdateInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, acc.GetAddress(), toAcc, innertx.CosmosCallType, innertx.SendCallName, fees, err) 219 } 220 if err != nil { 221 return err 222 } 223 ak.SetAccount(ctx, acc) 224 225 return nil 226 } 227 228 func incrementSeq(ctx sdk.Context, msgEthTx *evmtypes.MsgEthereumTx, accAddress sdk.AccAddress, ak auth.AccountKeeper, acc exported.Account) { 229 if ctx.IsCheckTx() && !ctx.IsReCheckTx() && !baseapp.IsMempoolEnableRecheck() && !ctx.IsTraceTx() { 230 return 231 } 232 233 // get and set account must be called with an infinite gas meter in order to prevent 234 // additional gas from being deducted. 235 infGasMeter := sdk.GetReusableInfiniteGasMeter() 236 defer sdk.ReturnInfiniteGasMeter(infGasMeter) 237 ctx.SetGasMeter(infGasMeter) 238 239 // increment sequence of all signers 240 // eth tx only has one signer 241 if accAddress.Empty() { 242 accAddress = msgEthTx.AccountAddress() 243 } 244 var sacc exported.Account 245 if acc != nil && bytes.Equal(accAddress, acc.GetAddress()) { 246 // because we use infinite gas meter, we can don't care about the gas 247 sacc = acc 248 } else { 249 sacc = ak.GetAccount(ctx, accAddress) 250 } 251 seq := sacc.GetSequence() 252 if !baseapp.IsMempoolEnablePendingPool() { 253 seq++ 254 } else if msgEthTx.Data.AccountNonce == seq { 255 seq++ 256 } 257 if err := sacc.SetSequence(seq); err != nil { 258 panic(err) 259 } 260 ak.SetAccount(ctx, sacc) 261 262 return 263 } 264 265 func (avd AccountAnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 266 msgEthTx, ok := tx.(*evmtypes.MsgEthereumTx) 267 if !ok { 268 return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) 269 } 270 271 var acc exported.Account 272 var getAccGasUsed sdk.Gas 273 274 address := msgEthTx.AccountAddress() 275 if address.Empty() && ctx.From() != "" { 276 msgEthTx.SetFrom(ctx.From()) 277 address = msgEthTx.AccountAddress() 278 } 279 280 if !simulate { 281 if address.Empty() { 282 panic("sender address cannot be empty") 283 } 284 if ctx.IsCheckTx() { 285 acc = avd.ak.GetAccount(ctx, address) 286 if acc == nil { 287 acc = avd.ak.NewAccountWithAddress(ctx, address) 288 avd.ak.SetAccount(ctx, acc) 289 } 290 // on InitChain make sure account number == 0 291 err = accountVerification(&ctx, acc, msgEthTx) 292 if err != nil { 293 return ctx, err 294 } 295 } 296 297 acc, getAccGasUsed = getAccount(&avd.ak, &ctx, address, acc) 298 if acc == nil { 299 return ctx, sdkerrors.Wrapf( 300 sdkerrors.ErrUnknownAddress, 301 "account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address, 302 ) 303 } 304 305 // account would not be updated 306 ctx, err = nonceVerification(ctx, acc, msgEthTx) 307 if err != nil { 308 return ctx, err 309 } 310 311 // consume gas for compatible 312 ctx.GasMeter().ConsumeGas(getAccGasUsed, "get account") 313 314 ctx.EnableAccountCache() 315 // account would be updated 316 err = ethGasConsume(avd.evmKeeper, avd.ak, avd.sk, &ctx, acc, getAccGasUsed, msgEthTx, simulate) 317 acc = nil 318 acc, _ = ctx.GetFromAccountCacheData().(exported.Account) 319 ctx.DisableAccountCache() 320 if err != nil { 321 return ctx, err 322 } 323 } 324 325 incrementSeq(ctx, msgEthTx, address, avd.ak, acc) 326 327 return next(ctx, tx, simulate) 328 }