Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 110 additions & 56 deletions injective-chain/modules/evm/rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func (b *Backend) GetTxHashByEthHash(ethHash common.Hash) (common.Hash, error) {
return common.Hash{}, fmt.Errorf("block not found, err: %w", err)
}

if int(res.TxIndex) >= len(block.Block.Txs) {
return common.Hash{}, fmt.Errorf("tx index out of bounds")
}

bftHash := block.Block.Txs[res.TxIndex].Hash()

return common.Hash(bftHash), nil
Expand All @@ -52,13 +56,22 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac
return nil, nil
}

if int(res.TxIndex) >= len(block.Block.Txs) {
return nil, errors.New("tx index out of range")
}
Comment on lines +59 to +61

tx, err := b.clientCtx.TxConfig.TxDecoder()(block.Block.Txs[res.TxIndex])
if err != nil {
return nil, err
}

msgs := tx.GetMsgs()
if int(res.MsgIndex) >= len(msgs) {
return nil, errors.New("msg index out of range")
}

// the `res.MsgIndex` is inferred from tx index, should be within the bound.
msg, ok := tx.GetMsgs()[res.MsgIndex].(*evmtypes.MsgEthereumTx)
msg, ok := msgs[res.MsgIndex].(*evmtypes.MsgEthereumTx)
if !ok {
return nil, errors.New("invalid ethereum tx")
}
Expand All @@ -71,9 +84,9 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac

if res.EthTxIndex == -1 {
// Fallback to find tx index by iterating all valid eth transactions
msgs := b.EthMsgsFromTendermintBlock(block)
for i := range msgs {
if msgs[i].Hash() == txHash {
ethMsgs := b.EthMsgsFromTendermintBlock(block)
for i := range ethMsgs {
if ethMsgs[i].Hash() == txHash {
res.EthTxIndex = int32(i)
break
}
Expand Down Expand Up @@ -156,31 +169,52 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
b.logger.Debug("block not found", "height", res.Height, "error", err.Error())
return nil, err
}
if resBlock == nil {
if resBlock == nil || resBlock.Block == nil {
b.logger.Debug("block not found", "height", res.Height)
return nil, nil
}

if int(res.TxIndex) >= len(resBlock.Block.Txs) {
b.logger.Warn("txIndex out of range", "txIndex", res.TxIndex, "len", len(resBlock.Block.Txs))
return nil, nil
}

tx, err := b.clientCtx.TxConfig.TxDecoder()(resBlock.Block.Txs[res.TxIndex])
if err != nil {
b.logger.Warn("decoding failed", "error", err.Error())
return nil, fmt.Errorf("failed to decode tx: %w", err)
}
ethMsg := tx.GetMsgs()[res.MsgIndex].(*evmtypes.MsgEthereumTx)

msgs := tx.GetMsgs()
if int(res.MsgIndex) >= len(msgs) {
b.logger.Warn("msgIndex out of range", "msgIndex", res.MsgIndex, "len", len(msgs))
return nil, nil
}

ethMsg, ok := msgs[res.MsgIndex].(*evmtypes.MsgEthereumTx)
if !ok {
b.logger.Error("invalid ethereum tx type")
return nil, errors.New("invalid ethereum tx")
}

txData := ethMsg.AsTransaction()
if txData == nil {
b.logger.Error("failed to unpack tx data")
return nil, err
return nil, errors.New("failed to unpack tx data")
}

cumulativeGasUsed := uint64(0)
blockRes, err := b.TendermintBlockResultByNumber(&res.Height)
if err != nil {
b.logger.Warn("failed to retrieve block results", "height", res.Height, "error", err.Error())
return nil, nil
}
for _, txResult := range blockRes.TxResults[0:res.TxIndex] {

cumulativeGasUsed := uint64(0)
maxTxIndex := int(res.TxIndex)
if maxTxIndex > len(blockRes.TxResults) {
maxTxIndex = len(blockRes.TxResults)
}
for _, txResult := range blockRes.TxResults[0:maxTxIndex] {
cumulativeGasUsed += uint64(txResult.GasUsed)
}
cumulativeGasUsed += res.CumulativeGasUsed
Expand All @@ -197,21 +231,13 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, err
}

// parse tx logs from events
logs, err := evmtypes.DecodeMsgLogs(
blockRes.TxResults[res.TxIndex].Data,
int(res.MsgIndex),
uint64(blockRes.Height),
)
if err != nil {
b.logger.Warn("failed to parse logs", "hash", hash, "error", err.Error())
}
blockHash := common.BytesToHash(resBlock.Block.Header.Hash())

if res.EthTxIndex == -1 {
// Fallback to find tx index by iterating all valid eth transactions
msgs := b.EthMsgsFromTendermintBlock(resBlock)
for i := range msgs {
if msgs[i].Hash() == hash {
ethMsgs := b.EthMsgsFromTendermintBlock(resBlock)
for i := range ethMsgs {
if ethMsgs[i].Hash() == hash {
res.EthTxIndex = int32(i)
break
}
Expand All @@ -222,6 +248,28 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, errors.New("can't find index of ethereum tx")
}

// parse tx logs from events
var logs []*ethtypes.Log
if int(res.TxIndex) < len(blockRes.TxResults) {
logs, err = evmtypes.DecodeMsgLogs(
blockRes.TxResults[res.TxIndex].Data,
int(res.MsgIndex),
uint64(blockRes.Height),
)
if err != nil {
b.logger.Warn("failed to parse logs", "hash", hash, "error", err.Error())
logs = nil
}
}

// Enrich log fields (blockHash, transactionHash, transactionIndex, etc.)
for i, log := range logs {
log.BlockHash = blockHash
log.TxHash = hash
log.TxIndex = uint(res.EthTxIndex)
log.Index = uint(i)
}
Comment on lines +265 to +271

var baseFee *big.Int
if txData.Type() == ethtypes.DynamicFeeTxType {
baseFee, err = b.BaseFee(blockRes)
Expand All @@ -230,41 +278,40 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
}
}

var contractAddress interface{} = nil
if txData.To() == nil {
contractAddress = crypto.CreateAddress(from, txData.Nonce())
}

formattedLogs := logs
if formattedLogs == nil {
formattedLogs = []*ethtypes.Log{}
}

receipt := map[string]interface{}{
// Consensus fields: These fields are defined by the Yellow Paper
"status": status,
"cumulativeGasUsed": hexutil.Uint64(cumulativeGasUsed),
"logsBloom": ethtypes.BytesToBloom(evmtypes.LogsBloom(logs)),
"logs": logs,
"logs": formattedLogs,

// Implementation fields: These fields are added by geth when processing a transaction.
// They are stored in the chain database.
"transactionHash": hash,
"contractAddress": nil,
"contractAddress": contractAddress,
"gasUsed": hexutil.Uint64(b.GetGasUsed(res, txData.Gas())),

// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
"blockHash": common.BytesToHash(resBlock.Block.Header.Hash()).Hex(),
// Inclusion information: These fields provide information about the inclusion
"blockHash": blockHash.Hex(),
"blockNumber": hexutil.Uint64(res.Height),
"transactionIndex": hexutil.Uint64(res.EthTxIndex),

// https://github.com/foundry-rs/foundry/issues/7640
"effectiveGasPrice": (*hexutil.Big)(effectiveGasPrice(txData, baseFee)),

// sender and receiver (contract or EOA) addreses
// sender and receiver (contract or EOA) addresses
"from": from,
"to": txData.To(),
"type": hexutil.Uint(ethMsg.AsTransaction().Type()),
}

if logs == nil {
receipt["logs"] = [][]*ethtypes.Log{}
}

// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if txData.To() == nil {
receipt["contractAddress"] = crypto.CreateAddress(from, txData.Nonce())
"type": hexutil.Uint(txData.Type()),
}

return receipt, nil
Expand Down Expand Up @@ -312,11 +359,12 @@ func (b *Backend) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockNum
}

// GetTxByEthHash uses `/tx_query` to find transaction by ethereum tx hash
// TODO: Don't need to convert once hashing is fixed on Tendermint
// https://github.com/tendermint/tendermint/issues/6539
func (b *Backend) GetTxByEthHash(hash common.Hash) (*chaintypes.TxResult, error) {
if b.indexer != nil {
return b.indexer.GetByTxHash(hash)
res, err := b.indexer.GetByTxHash(hash)
if err == nil && res != nil {
return res, nil
}
}

// fallback to tendermint tx indexer
Expand All @@ -334,7 +382,10 @@ func (b *Backend) GetTxByEthHash(hash common.Hash) (*chaintypes.TxResult, error)
// GetTxByTxIndex uses `/tx_query` to find transaction by tx index of valid ethereum txs
func (b *Backend) GetTxByTxIndex(height int64, index uint) (*chaintypes.TxResult, error) {
if b.indexer != nil {
return b.indexer.GetByBlockAndIndex(height, int32(index))
res, err := b.indexer.GetByBlockAndIndex(height, int32(index))
if err == nil && res != nil {
return res, nil
}
}

// fallback to tendermint tx indexer
Expand Down Expand Up @@ -390,21 +441,24 @@ func (b *Backend) GetTransactionByBlockAndIndex(block *cmrpctypes.ResultBlock, i
var msg *evmtypes.MsgEthereumTx
// find in tx indexer
res, err := b.GetTxByTxIndex(block.Block.Height, uint(idx))
if err == nil {
tx, err := b.clientCtx.TxConfig.TxDecoder()(block.Block.Txs[res.TxIndex])
if err != nil {
b.logger.Warn("invalid ethereum tx", "height", block.Block.Header, "index", idx)
return nil, nil
if err == nil && res != nil {
if int(res.TxIndex) < len(block.Block.Txs) {
tx, err := b.clientCtx.TxConfig.TxDecoder()(block.Block.Txs[res.TxIndex])
if err == nil {
msgs := tx.GetMsgs()
if int(res.MsgIndex) < len(msgs) {
var ok bool
msg, ok = msgs[res.MsgIndex].(*evmtypes.MsgEthereumTx)
if !ok {
msg = nil
}
}
}
}
}

var ok bool
// msgIndex is inferred from tx events, should be within bound.
msg, ok = tx.GetMsgs()[res.MsgIndex].(*evmtypes.MsgEthereumTx)
if !ok {
b.logger.Warn("invalid ethereum tx", "height", block.Block.Header, "index", idx)
return nil, nil
}
} else {
// Fallback when indexer fail or returned invalid msg
if msg == nil {
i := int(idx)
if i < 0 {
i = 0
Expand Down Expand Up @@ -432,4 +486,4 @@ func (b *Backend) GetTransactionByBlockAndIndex(block *cmrpctypes.ResultBlock, i
baseFee,
b.ChainID().ToInt(),
)
}
}