github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/types/utils.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "math/big" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/common/hexutil" 12 ethtypes "github.com/ethereum/go-ethereum/core/types" 13 "github.com/fibonacci-chain/fbc/app/crypto/ethsecp256k1" 14 clientcontext "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 16 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 17 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 18 tmbytes "github.com/fibonacci-chain/fbc/libs/tendermint/libs/bytes" 19 ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types" 20 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 21 evmtypes "github.com/fibonacci-chain/fbc/x/evm/types" 22 "github.com/fibonacci-chain/fbc/x/evm/watcher" 23 ) 24 25 var ( 26 // static gas limit for all blocks 27 defaultGasLimit = hexutil.Uint64(int64(^uint32(0))) 28 defaultGasUsed = hexutil.Uint64(0) 29 defaultDifficulty = (*hexutil.Big)(big.NewInt(0)) 30 ) 31 32 // RawTxToEthTx returns a evm MsgEthereum transaction from raw tx bytes. 33 func RawTxToEthTx(clientCtx clientcontext.CLIContext, bz []byte, height int64) (*evmtypes.MsgEthereumTx, error) { 34 tx, err := evmtypes.TxDecoder(clientCtx.Codec)(bz, height) 35 if err != nil { 36 return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 37 } 38 39 ethTx, ok := tx.(*evmtypes.MsgEthereumTx) 40 if !ok { 41 return nil, fmt.Errorf("invalid transaction type %T, expected %T", tx, evmtypes.MsgEthereumTx{}) 42 } 43 return ethTx, nil 44 } 45 46 func ToTransaction(tx *evmtypes.MsgEthereumTx, from *common.Address) *watcher.Transaction { 47 rpcTx := &watcher.Transaction{ 48 From: *from, 49 Gas: hexutil.Uint64(tx.Data.GasLimit), 50 GasPrice: (*hexutil.Big)(tx.Data.Price), 51 Input: hexutil.Bytes(tx.Data.Payload), 52 Nonce: hexutil.Uint64(tx.Data.AccountNonce), 53 To: tx.To(), 54 Value: (*hexutil.Big)(tx.Data.Amount), 55 V: (*hexutil.Big)(tx.Data.V), 56 R: (*hexutil.Big)(tx.Data.R), 57 S: (*hexutil.Big)(tx.Data.S), 58 } 59 return rpcTx 60 } 61 62 // RpcBlockFromTendermint returns a JSON-RPC compatible Ethereum blockfrom a given Tendermint block. 63 func RpcBlockFromTendermint(clientCtx clientcontext.CLIContext, block *tmtypes.Block, fullTx bool) (*watcher.Block, error) { 64 gasLimit, err := BlockMaxGasFromConsensusParams(context.Background(), clientCtx) 65 if err != nil { 66 return nil, err 67 } 68 69 gasUsed, ethTxs, err := EthTransactionsFromTendermint(clientCtx, block.Txs, common.BytesToHash(block.Hash()), uint64(block.Height)) 70 if err != nil { 71 return nil, err 72 } 73 74 var bloom ethtypes.Bloom 75 clientCtx = clientCtx.WithHeight(block.Height) 76 res, _, err := clientCtx.Query(fmt.Sprintf("custom/%s/%s/%d", evmtypes.ModuleName, evmtypes.QueryBloom, block.Height)) 77 if err == nil { 78 var bloomRes evmtypes.QueryBloomFilter 79 clientCtx.Codec.MustUnmarshalJSON(res, &bloomRes) 80 bloom = bloomRes.Bloom 81 } 82 83 return FormatBlock(block.Header, block.Size(), block.Hash(), gasLimit, gasUsed, ethTxs, bloom, fullTx), nil 84 } 85 86 // EthHeaderFromTendermint is an util function that returns an Ethereum Header 87 // from a tendermint Header. 88 func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header { 89 return ðtypes.Header{ 90 ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()), 91 UncleHash: ethtypes.EmptyUncleHash, 92 Coinbase: common.BytesToAddress(header.ProposerAddress), 93 Root: common.BytesToHash(header.AppHash), 94 TxHash: common.BytesToHash(header.DataHash), 95 ReceiptHash: ethtypes.EmptyRootHash, 96 Difficulty: nil, 97 Number: big.NewInt(header.Height), 98 Time: uint64(header.Time.Unix()), 99 Extra: nil, 100 MixDigest: common.Hash{}, 101 Nonce: ethtypes.BlockNonce{}, 102 } 103 } 104 105 // EthTransactionsFromTendermint returns a slice of ethereum transaction hashes and the total gas usage from a set of 106 // tendermint block transactions. 107 func EthTransactionsFromTendermint(clientCtx clientcontext.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, blockNumber uint64) (*big.Int, []*watcher.Transaction, error) { 108 var transactions []*watcher.Transaction 109 gasUsed := big.NewInt(0) 110 index := uint64(0) 111 112 for _, tx := range txs { 113 ethTx, err := RawTxToEthTx(clientCtx, tx, int64(blockNumber)) 114 if err != nil { 115 // continue to next transaction in case it's not a MsgEthereumTx 116 continue 117 } 118 // TODO: Remove gas usage calculation if saving gasUsed per block 119 gasUsed.Add(gasUsed, big.NewInt(int64(ethTx.GetGas()))) 120 tx, err := watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), blockHash, blockNumber, index) 121 if err == nil { 122 transactions = append(transactions, tx) 123 index++ 124 } 125 } 126 127 return gasUsed, transactions, nil 128 } 129 130 // BlockMaxGasFromConsensusParams returns the gas limit for the latest block from the chain consensus params. 131 func BlockMaxGasFromConsensusParams(_ context.Context, clientCtx clientcontext.CLIContext) (int64, error) { 132 //resConsParams, err := clientCtx.Client.ConsensusParams(nil) 133 //if err != nil { 134 // return 0, err 135 //} 136 // 137 //gasLimit := resConsParams.ConsensusParams.Block.MaxGas 138 //if gasLimit == -1 { 139 // // Sets gas limit to max uint32 to not error with javascript dev tooling 140 // // This -1 value indicating no block gas limit is set to max uint64 with geth hexutils 141 // // which errors certain javascript dev tooling which only supports up to 53 bits 142 // gasLimit = int64(^uint32(0)) 143 //} 144 // 145 //return gasLimit, nil 146 147 return int64(^uint32(0)), nil 148 } 149 150 // FormatBlock creates an ethereum block from a tendermint header and ethereum-formatted 151 // transactions. 152 func FormatBlock( 153 header tmtypes.Header, size int, curBlockHash tmbytes.HexBytes, gasLimit int64, 154 gasUsed *big.Int, transactions []*watcher.Transaction, bloom ethtypes.Bloom, fullTx bool, 155 ) *watcher.Block { 156 transactionsRoot := ethtypes.EmptyRootHash 157 if len(header.DataHash) > 0 { 158 transactionsRoot = common.BytesToHash(header.DataHash) 159 } 160 161 parentHash := header.LastBlockID.Hash 162 if parentHash == nil { 163 parentHash = ethtypes.EmptyRootHash.Bytes() 164 } 165 ret := &watcher.Block{ 166 Number: hexutil.Uint64(header.Height), 167 Hash: common.BytesToHash(curBlockHash), 168 ParentHash: common.BytesToHash(parentHash), 169 Nonce: watcher.BlockNonce{}, // PoW specific 170 UncleHash: ethtypes.EmptyUncleHash, // No uncles in Tendermint 171 LogsBloom: bloom, 172 TransactionsRoot: transactionsRoot, 173 StateRoot: common.BytesToHash(header.AppHash), 174 Miner: common.BytesToAddress(header.ProposerAddress), 175 MixHash: common.Hash{}, 176 Difficulty: hexutil.Uint64(0), 177 TotalDifficulty: hexutil.Uint64(0), 178 ExtraData: hexutil.Bytes{}, 179 Size: hexutil.Uint64(size), 180 GasLimit: hexutil.Uint64(gasLimit), // Static gas limit 181 GasUsed: (*hexutil.Big)(gasUsed), 182 Timestamp: hexutil.Uint64(header.Time.Unix()), 183 Uncles: []common.Hash{}, 184 ReceiptsRoot: ethtypes.EmptyRootHash, 185 } 186 187 if fullTx { 188 // return empty slice instead of nil for compatibility with Ethereum 189 if len(transactions) == 0 { 190 ret.Transactions = []*watcher.Transaction{} 191 } else { 192 ret.Transactions = transactions 193 } 194 } else { 195 txHashes := make([]common.Hash, len(transactions)) 196 for i, tx := range transactions { 197 txHashes[i] = tx.Hash 198 } 199 ret.Transactions = txHashes 200 } 201 return ret 202 } 203 204 // GetKeyByAddress returns the private key matching the given address. If not found it returns false. 205 func GetKeyByAddress(keys []ethsecp256k1.PrivKey, address common.Address) (key *ethsecp256k1.PrivKey, exist bool) { 206 for _, key := range keys { 207 if bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) { 208 return &key, true 209 } 210 } 211 return nil, false 212 } 213 214 // GetBlockCumulativeGas returns the cumulative gas used on a block up to a given 215 // transaction index. The returned gas used includes the gas from both the SDK and 216 // EVM module transactions. 217 func GetBlockCumulativeGas(cdc *codec.Codec, block *tmtypes.Block, idx int) uint64 { 218 var gasUsed uint64 219 txDecoder := evmtypes.TxDecoder(cdc) 220 221 for i := 0; i < idx && i < len(block.Txs); i++ { 222 txi, err := txDecoder(block.Txs[i], block.Height) 223 if err != nil { 224 continue 225 } 226 227 gasUsed += txi.GetGas() 228 } 229 return gasUsed 230 } 231 232 // EthHeaderWithBlockHashFromTendermint gets the eth Header with block hash from Tendermint block inside 233 func EthHeaderWithBlockHashFromTendermint(tmHeader *tmtypes.Header) (header *EthHeaderWithBlockHash, err error) { 234 if tmHeader == nil { 235 return header, errors.New("failed. nil tendermint block header") 236 } 237 238 header = &EthHeaderWithBlockHash{ 239 ParentHash: common.BytesToHash(tmHeader.LastBlockID.Hash.Bytes()), 240 UncleHash: ethtypes.EmptyUncleHash, 241 Coinbase: common.BytesToAddress(tmHeader.ProposerAddress), 242 Root: common.BytesToHash(tmHeader.AppHash), 243 TxHash: common.BytesToHash(tmHeader.DataHash), 244 ReceiptHash: ethtypes.EmptyRootHash, 245 Number: (*hexutil.Big)(big.NewInt(tmHeader.Height)), 246 // difficulty is not available for DPOS 247 Difficulty: defaultDifficulty, 248 GasLimit: defaultGasLimit, 249 GasUsed: defaultGasUsed, 250 Time: hexutil.Uint64(tmHeader.Time.Unix()), 251 Hash: common.BytesToHash(tmHeader.Hash()), 252 } 253 254 return 255 } 256 257 func RawTxToRealTx(clientCtx clientcontext.CLIContext, bz tmtypes.Tx, 258 blockHash common.Hash, blockNumber, index uint64) (sdk.Tx, error) { 259 realTx, err := evmtypes.TxDecoder(clientCtx.CodecProy)(bz, int64(blockNumber)) 260 if err != nil { 261 return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 262 } 263 264 return realTx, nil 265 } 266 267 func RawTxResultToEthReceipt(chainID *big.Int, tr *ctypes.ResultTx, realTx sdk.Tx, 268 blockHash common.Hash) (*watcher.TransactionResult, error) { 269 // Convert tx bytes to eth transaction 270 ethTx, ok := realTx.(*evmtypes.MsgEthereumTx) 271 if !ok { 272 return nil, fmt.Errorf("invalid transaction type %T, expected %T", realTx, evmtypes.MsgEthereumTx{}) 273 } 274 275 // try to get from event 276 if from, err := GetEthSender(tr); err == nil { 277 ethTx.BaseTx.From = from 278 } else { 279 // try to get from sig 280 err := ethTx.VerifySig(chainID, tr.Height) 281 if err != nil { 282 return nil, err 283 } 284 } 285 286 // Set status codes based on tx result 287 var status = hexutil.Uint64(0) 288 if tr.TxResult.IsOK() { 289 status = hexutil.Uint64(1) 290 } 291 292 txData := tr.TxResult.GetData() 293 data, err := evmtypes.DecodeResultData(txData) 294 if err != nil { 295 status = 0 // transaction failed 296 } 297 298 if len(data.Logs) == 0 { 299 data.Logs = []*ethtypes.Log{} 300 } 301 contractAddr := &data.ContractAddress 302 if data.ContractAddress == common.HexToAddress("0x00000000000000000000") { 303 contractAddr = nil 304 } 305 306 // fix gasUsed when deliverTx ante handler check sequence invalid 307 gasUsed := tr.TxResult.GasUsed 308 if tr.TxResult.Code == sdkerrors.ErrInvalidSequence.ABCICode() { 309 gasUsed = 0 310 } 311 312 receipt := watcher.TransactionReceipt{ 313 Status: status, 314 //CumulativeGasUsed: hexutil.Uint64(cumulativeGasUsed), 315 LogsBloom: data.Bloom, 316 Logs: data.Logs, 317 TransactionHash: common.BytesToHash(tr.Hash.Bytes()).String(), 318 ContractAddress: contractAddr, 319 GasUsed: hexutil.Uint64(gasUsed), 320 BlockHash: blockHash.String(), 321 BlockNumber: hexutil.Uint64(tr.Height), 322 TransactionIndex: hexutil.Uint64(tr.Index), 323 From: ethTx.GetFrom(), 324 To: ethTx.To(), 325 } 326 327 rpcTx, err := watcher.NewTransaction(ethTx, common.BytesToHash(tr.Hash), 328 blockHash, uint64(tr.Height), uint64(tr.Index)) 329 if err != nil { 330 return nil, err 331 } 332 333 return &watcher.TransactionResult{TxType: hexutil.Uint64(watcher.EthReceipt), 334 Receipt: &receipt, EthTx: rpcTx, EthTxLog: tr.TxResult.Log}, nil 335 } 336 337 func GetEthSender(tr *ctypes.ResultTx) (string, error) { 338 for _, ev := range tr.TxResult.Events { 339 if ev.Type == sdk.EventTypeMessage { 340 fromAddr := "" 341 realEvmTx := false 342 for _, attr := range ev.Attributes { 343 if string(attr.Key) == sdk.AttributeKeySender { 344 fromAddr = string(attr.Value) 345 } 346 if string(attr.Key) == sdk.AttributeKeyModule && 347 string(attr.Value) == evmtypes.AttributeValueCategory { // to avoid the evm to cm tx enter 348 realEvmTx = true 349 } 350 // find the sender 351 if fromAddr != "" && realEvmTx { 352 return fromAddr, nil 353 } 354 } 355 } 356 } 357 return "", errors.New("No sender in Event") 358 }