github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/types/msg_evm.go (about) 1 package types 2 3 import ( 4 "crypto/ecdsa" 5 "errors" 6 "fmt" 7 "io" 8 "math/big" 9 "sync" 10 11 ethcmn "github.com/ethereum/go-ethereum/common" 12 ethcrypto "github.com/ethereum/go-ethereum/crypto" 13 "github.com/ethereum/go-ethereum/rlp" 14 "github.com/fibonacci-chain/fbc/app/types" 15 ethermint "github.com/fibonacci-chain/fbc/app/types" 16 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 17 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/ante" 19 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 20 "github.com/tendermint/go-amino" 21 ) 22 23 var ( 24 _ sdk.Msg = (*MsgEthereumTx)(nil) 25 _ sdk.Tx = (*MsgEthereumTx)(nil) 26 _ ante.FeeTx = (*MsgEthereumTx)(nil) 27 ) 28 29 var big2 = big.NewInt(2) 30 var big8 = big.NewInt(8) 31 var DefaultDeployContractFnSignature = ethcmn.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001") 32 var DefaultSendCoinFnSignature = ethcmn.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000010") 33 var emptyEthAddr = ethcmn.Address{} 34 35 // message type and route constants 36 const ( 37 // TypeMsgEthereumTx defines the type string of an Ethereum tranasction 38 TypeMsgEthereumTx = "ethereum" 39 ) 40 41 // MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. 42 type MsgEthereumTx struct { 43 Data TxData 44 45 sdk.BaseTx `json:"-" rlp:"-"` 46 47 addr ethcmn.Address 48 } 49 50 func (tx *MsgEthereumTx) GetType() sdk.TransactionType { 51 return sdk.EvmTxType 52 } 53 54 func (tx *MsgEthereumTx) SetFrom(addr string) { 55 tx.From = addr 56 tx.addr = ethcmn.HexToAddress(addr) 57 } 58 59 // GetFrom returns sender address of MsgEthereumTx if signature is valid, or returns "". 60 func (tx *MsgEthereumTx) GetFrom() string { 61 from := tx.BaseTx.GetFrom() 62 if from != "" { 63 return from 64 } 65 from, _ = tmtypes.SignatureCache().Get(tx.TxHash()) 66 if from != "" { 67 return from 68 } 69 // Verify the signature with chain-id in the tx, so it can be a tx from other chain with unexpected chain. 70 // Only use from addr for some safe usage and do not update the signature cache or the `From` field of the tx. 71 sender, err := tx.firstVerifySig(tx.ChainID()) 72 if err != nil { 73 return "" 74 } 75 from = EthAddressToString(&sender) 76 return from 77 } 78 79 func (msg MsgEthereumTx) GetSender(ctx sdk.Context) string { 80 chainID, err := ethermint.ParseChainID(ctx.ChainID()) 81 if err != nil { 82 return "" 83 } 84 err = msg.VerifySig(chainID, ctx.BlockHeight()) 85 if err != nil { 86 return "" 87 } 88 89 return msg.BaseTx.GetFrom() 90 } 91 92 func (msg *MsgEthereumTx) GetNonce() uint64 { 93 return msg.Data.AccountNonce 94 } 95 96 func (msg *MsgEthereumTx) GetFee() sdk.Coins { 97 fee := make(sdk.Coins, 1) 98 feeInt := new(big.Int) 99 feeInt = msg.CalcFee(feeInt) 100 fee[0] = sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithBigIntAndPrec(feeInt, sdk.Precision)) 101 return fee 102 } 103 104 func (msg MsgEthereumTx) FeePayer(ctx sdk.Context) sdk.AccAddress { 105 chainID, err := ethermint.ParseChainID(ctx.ChainID()) 106 if err != nil { 107 return nil 108 } 109 err = msg.VerifySig(chainID, ctx.BlockHeight()) 110 if err != nil { 111 return nil 112 } 113 114 return msg.AccountAddress() 115 } 116 117 // NewMsgEthereumTx returns a reference to a new Ethereum transaction message. 118 func NewMsgEthereumTx( 119 nonce uint64, to *ethcmn.Address, amount *big.Int, 120 gasLimit uint64, gasPrice *big.Int, payload []byte, 121 ) *MsgEthereumTx { 122 return newMsgEthereumTx(nonce, to, amount, gasLimit, gasPrice, payload) 123 } 124 125 // NewMsgEthereumTxContract returns a reference to a new Ethereum transaction 126 // message designated for contract creation. 127 func NewMsgEthereumTxContract( 128 nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte, 129 ) *MsgEthereumTx { 130 return newMsgEthereumTx(nonce, nil, amount, gasLimit, gasPrice, payload) 131 } 132 133 func newMsgEthereumTx( 134 nonce uint64, to *ethcmn.Address, amount *big.Int, 135 gasLimit uint64, gasPrice *big.Int, payload []byte, 136 ) *MsgEthereumTx { 137 if len(payload) > 0 { 138 payload = ethcmn.CopyBytes(payload) 139 } 140 141 txData := TxData{ 142 AccountNonce: nonce, 143 Recipient: to, 144 Payload: payload, 145 GasLimit: gasLimit, 146 Amount: new(big.Int), 147 Price: new(big.Int), 148 V: new(big.Int), 149 R: new(big.Int), 150 S: new(big.Int), 151 } 152 153 if amount != nil { 154 txData.Amount.Set(amount) 155 } 156 if gasPrice != nil { 157 txData.Price.Set(gasPrice) 158 } 159 160 return &MsgEthereumTx{Data: txData} 161 } 162 163 func (msg *MsgEthereumTx) String() string { 164 return msg.Data.String() 165 } 166 167 // Route returns the route value of an MsgEthereumTx. 168 func (msg *MsgEthereumTx) Route() string { return RouterKey } 169 170 // Type returns the type value of an MsgEthereumTx. 171 func (msg *MsgEthereumTx) Type() string { return TypeMsgEthereumTx } 172 173 // ValidateBasic implements the sdk.Msg interface. It performs basic validation 174 // checks of a Transaction. If returns an error if validation fails. 175 func (msg *MsgEthereumTx) ValidateBasic() error { 176 if msg.Data.Price.Sign() <= 0 { 177 return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be non positive %s", msg.Data.Price) 178 } 179 180 // Amount can be 0 181 if msg.Data.Amount.Sign() == -1 { 182 return sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Data.Amount) 183 } 184 185 return nil 186 } 187 188 // To returns the recipient address of the transaction. It returns nil if the 189 // transaction is a contract creation. 190 func (msg *MsgEthereumTx) To() *ethcmn.Address { 191 return msg.Data.Recipient 192 } 193 194 // GetSigners returns the expected signers for an Ethereum transaction message. 195 // For such a message, there should exist only a single 'signer'. 196 // 197 // NOTE: This method panics if 'VerifySig' hasn't been called first. 198 func (msg *MsgEthereumTx) GetSigners() []sdk.AccAddress { 199 addr := msg.AccountAddress() 200 if msg.BaseTx.From == "" || addr.Empty() { 201 panic("must use 'VerifySig' with a chain ID to get the from addr") 202 } 203 return []sdk.AccAddress{addr} 204 } 205 206 // GetSignBytes returns the Amino bytes of an Ethereum transaction message used 207 // for signing. 208 // 209 // NOTE: This method cannot be used as a chain ID is needed to create valid bytes 210 // to sign over. Use 'RLPSignBytes' instead. 211 func (msg *MsgEthereumTx) GetSignBytes() []byte { 212 panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") 213 } 214 215 type rlpHashData struct { 216 Params [9]interface{} 217 Hash ethcmn.Hash 218 219 GasLimit uint64 220 Payload []byte 221 222 ParamsSlice interface{} 223 } 224 225 var rlpHashDataPool = &sync.Pool{ 226 New: func() interface{} { 227 data := &rlpHashData{} 228 data.ParamsSlice = data.Params[:] 229 return data 230 }, 231 } 232 233 // RLPSignBytes returns the RLP hash of an Ethereum transaction message with a 234 // given chainID used for signing. 235 func (msg *MsgEthereumTx) RLPSignBytes(chainID *big.Int) (h ethcmn.Hash) { 236 rlpData := rlpHashDataPool.Get().(*rlpHashData) 237 rlpData.GasLimit = msg.Data.GasLimit 238 rlpData.Payload = msg.Data.Payload 239 240 rlpParams := &rlpData.Params 241 rlpParams[0] = msg.Data.AccountNonce 242 rlpParams[1] = msg.Data.Price 243 rlpParams[2] = &rlpData.GasLimit 244 rlpParams[3] = msg.Data.Recipient 245 rlpParams[4] = msg.Data.Amount 246 rlpParams[5] = &rlpData.Payload 247 rlpParams[6] = chainID 248 rlpParams[7] = uint(0) 249 rlpParams[8] = uint(0) 250 rlpHashTo(rlpData.ParamsSlice, &rlpData.Hash) 251 h = rlpData.Hash 252 rlpHashDataPool.Put(rlpData) 253 return 254 } 255 256 // Hash returns the hash to be signed by the sender. 257 // It does not uniquely identify the transaction. 258 func (msg *MsgEthereumTx) HomesteadSignHash() ethcmn.Hash { 259 return rlpHash([]interface{}{ 260 msg.Data.AccountNonce, 261 msg.Data.Price, 262 msg.Data.GasLimit, 263 msg.Data.Recipient, 264 msg.Data.Amount, 265 msg.Data.Payload, 266 }) 267 } 268 269 // EncodeRLP implements the rlp.Encoder interface. 270 func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error { 271 return rlp.Encode(w, &msg.Data) 272 } 273 274 // DecodeRLP implements the rlp.Decoder interface. 275 func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error { 276 _, _, err := s.Kind() 277 if err != nil { 278 // return error if stream is too large 279 return err 280 } 281 282 if err := s.Decode(&msg.Data); err != nil { 283 return err 284 } 285 286 return nil 287 } 288 289 // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It 290 // takes a private key and chainID to sign an Ethereum transaction according to 291 // EIP155 standard. It mutates the transaction as it populates the V, R, S 292 // fields of the Transaction's Signature. 293 func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) error { 294 txHash := msg.RLPSignBytes(chainID) 295 296 sig, err := ethcrypto.Sign(txHash[:], priv) 297 if err != nil { 298 return err 299 } 300 301 if len(sig) != 65 { 302 return fmt.Errorf("wrong size for signature: got %d, want 65", len(sig)) 303 } 304 305 r := new(big.Int).SetBytes(sig[:32]) 306 s := new(big.Int).SetBytes(sig[32:64]) 307 308 var v *big.Int 309 310 if chainID.Sign() == 0 { 311 v = new(big.Int).SetBytes([]byte{sig[64] + 27}) 312 } else { 313 v = big.NewInt(int64(sig[64] + 35)) 314 chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2)) 315 316 v.Add(v, chainIDMul) 317 } 318 319 msg.Data.V = v 320 msg.Data.R = r 321 msg.Data.S = s 322 return nil 323 } 324 325 var sigBigNumPool = &sync.Pool{ 326 New: func() interface{} { 327 return new(big.Int) 328 }, 329 } 330 331 func (msg *MsgEthereumTx) firstVerifySig(chainID *big.Int) (ethcmn.Address, error) { 332 var V *big.Int 333 var sigHash ethcmn.Hash 334 if isProtectedV(msg.Data.V) { 335 // do not allow recovery for transactions with an unprotected chainID 336 if chainID.Sign() == 0 { 337 return emptyEthAddr, errors.New("chainID cannot be zero") 338 } 339 340 bigNum := sigBigNumPool.Get().(*big.Int) 341 defer sigBigNumPool.Put(bigNum) 342 chainIDMul := bigNum.Mul(chainID, big2) 343 V = chainIDMul.Sub(msg.Data.V, chainIDMul) 344 345 // chainIDMul := new(big.Int).Mul(chainID, big2) 346 // V = new(big.Int).Sub(msg.Data.V, chainIDMul) 347 V.Sub(V, big8) 348 349 sigHash = msg.RLPSignBytes(chainID) 350 } else { 351 V = msg.Data.V 352 353 sigHash = msg.HomesteadSignHash() 354 } 355 356 sender, err := recoverEthSig(msg.Data.R, msg.Data.S, V, &sigHash) 357 if err != nil { 358 return emptyEthAddr, err 359 } 360 return sender, nil 361 } 362 363 // VerifySig attempts to verify a Transaction's signature for a given chainID. 364 // A derived address is returned upon success or an error if recovery fails. 365 func (msg *MsgEthereumTx) VerifySig(chainID *big.Int, height int64) error { 366 if !isProtectedV(msg.Data.V) && 367 tmtypes.HigherThanMercury(height) && 368 !tmtypes.HigherThanVenus5(height) { 369 return errors.New("deprecated support for homestead Signer") 370 } 371 372 if msg.BaseTx.GetFrom() != "" { 373 return nil 374 } 375 from, ok := tmtypes.SignatureCache().Get(msg.TxHash()) 376 if ok { 377 msg.SetFrom(from) 378 return nil 379 } 380 381 if tmtypes.HigherThanJupiter(height) { 382 if types.IsJupiterBeforeMainNetChainId(chainID) { 383 chainID = big.NewInt(types.JupiterMainNetChainId) 384 } else if types.IsJupiterBeforeTestNetChainId(chainID) { 385 chainID = big.NewInt(types.JupiterTestNetChainId) 386 } 387 } 388 389 sender, err := msg.firstVerifySig(chainID) 390 if err != nil { 391 return err 392 } 393 from = EthAddressToString(&sender) 394 tmtypes.SignatureCache().Add(msg.TxHash(), from) 395 msg.BaseTx.From = from 396 msg.addr = sender 397 return nil 398 } 399 400 // Protected says whether the transaction is replay-protected. 401 func (msg *MsgEthereumTx) Protected() bool { 402 return isProtectedV(msg.Data.V) 403 } 404 405 // codes from go-ethereum/core/types/transaction.go:122 406 func isProtectedV(V *big.Int) bool { 407 if V.BitLen() <= 8 { 408 v := V.Uint64() 409 return v != 27 && v != 28 410 } 411 // anything not 27 or 28 is considered protected 412 return true 413 } 414 415 // GetGas implements the GasTx interface. It returns the GasLimit of the transaction. 416 func (msg *MsgEthereumTx) GetGas() uint64 { 417 return msg.Data.GasLimit 418 } 419 420 // Fee returns gasprice * gaslimit. 421 func (msg *MsgEthereumTx) Fee() *big.Int { 422 fee := new(big.Int) 423 fee.SetUint64(msg.Data.GasLimit) 424 fee.Mul(fee, msg.Data.Price) 425 return fee 426 } 427 428 // CalcFee set fee to gasprice * gaslimit and return fee 429 func (msg *MsgEthereumTx) CalcFee(fee *big.Int) *big.Int { 430 fee.SetUint64(msg.Data.GasLimit) 431 fee.Mul(fee, msg.Data.Price) 432 return fee 433 } 434 435 // ChainID returns which chain id this transaction was signed for (if at all) 436 func (msg *MsgEthereumTx) ChainID() *big.Int { 437 return deriveChainID(msg.Data.V) 438 } 439 440 // Cost returns amount + gasprice * gaslimit. 441 func (msg *MsgEthereumTx) Cost() *big.Int { 442 total := msg.Fee() 443 total.Add(total, msg.Data.Amount) 444 return total 445 } 446 447 // CalcCostTo set total to amount + gasprice * gaslimit and return it 448 func (msg *MsgEthereumTx) CalcCostTo(total *big.Int) *big.Int { 449 total = msg.CalcFee(total) 450 total.Add(total, msg.Data.Amount) 451 return total 452 } 453 454 // RawSignatureValues returns the V, R, S signature values of the transaction. 455 // The return values should not be modified by the caller. 456 func (msg *MsgEthereumTx) RawSignatureValues() (v, r, s *big.Int) { 457 return msg.Data.V, msg.Data.R, msg.Data.S 458 } 459 460 // From loads the ethereum sender address from the sigcache and returns an 461 // sdk.AccAddress from its bytes 462 func (msg *MsgEthereumTx) AccountAddress() sdk.AccAddress { 463 if msg.addr == emptyEthAddr { 464 return ethcmn.FromHex(msg.GetFrom()) 465 } else { 466 return msg.addr[:] 467 } 468 } 469 470 func (msg *MsgEthereumTx) EthereumAddress() ethcmn.Address { 471 if msg.addr == emptyEthAddr { 472 return ethcmn.HexToAddress(msg.GetFrom()) 473 } else { 474 return msg.addr 475 } 476 } 477 478 // deriveChainID derives the chain id from the given v parameter 479 func deriveChainID(v *big.Int) *big.Int { 480 if v.BitLen() <= 64 { 481 v := v.Uint64() 482 if v == 27 || v == 28 { 483 return new(big.Int) 484 } 485 return new(big.Int).SetUint64((v - 35) / 2) 486 } 487 v = new(big.Int).Sub(v, big.NewInt(35)) 488 return v.Div(v, big.NewInt(2)) 489 } 490 491 func (msg *MsgEthereumTx) UnmarshalFromAmino(cdc *amino.Codec, data []byte) error { 492 var dataLen uint64 = 0 493 var subData []byte 494 495 for { 496 data = data[dataLen:] 497 498 if len(data) == 0 { 499 break 500 } 501 502 pos, pbType, err := amino.ParseProtoPosAndTypeMustOneByte(data[0]) 503 if err != nil { 504 return err 505 } 506 data = data[1:] 507 508 if pbType == amino.Typ3_ByteLength { 509 var n int 510 dataLen, n, err = amino.DecodeUvarint(data) 511 if err != nil { 512 return err 513 } 514 data = data[n:] 515 if len(data) < int(dataLen) { 516 return fmt.Errorf("invalid tx data") 517 } 518 subData = data[:dataLen] 519 } 520 521 switch pos { 522 case 1: 523 if err := msg.Data.UnmarshalFromAmino(cdc, subData); err != nil { 524 return err 525 } 526 default: 527 return fmt.Errorf("unexpect feild num %d", pos) 528 } 529 } 530 return nil 531 }