Skip to content

Commit

Permalink
Merge pull request #613 from oasisprotocol/andrew7234/abi-backfill-bu…
Browse files Browse the repository at this point in the history
…gfix-2

use coalesce for backfill analyzer queries
  • Loading branch information
Andrew7234 authored Jan 24, 2024
2 parents 5bc2786 + a11009d commit faea7b1
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 109 deletions.
179 changes: 77 additions & 102 deletions analyzer/evmabibackfill/evm_abi_backfill.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
ethCommon "github.com/ethereum/go-ethereum/common"
sdkEVM "github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/evm"

"github.com/oasisprotocol/nexus/analyzer"
Expand Down Expand Up @@ -167,134 +168,108 @@ func cleanTxRevertReason(raw string) ([]byte, error) {
return base64.StdEncoding.DecodeString(s)
}

func (p *processor) ProcessItem(ctx context.Context, batch *storage.QueryBatch, item *abiEncodedItem) error {
// Unmarshal abi
contractAbi, err := abi.JSON(bytes.NewReader(item.Abi))
// Attempts to parse the raw event body into the event name, args, and signature
// as defined by the abi of the contract that emitted this event.
func (p *processor) parseEvent(ev *abiEncodedEvent, contractAbi abi.ABI) (*string, []*abiEncodedArg, *ethCommon.Hash) {
abiEvent, abiEventArgs, err := abiparse.ParseEvent(ev.EventBody.Topics, ev.EventBody.Data, &contractAbi)
if err != nil {
return fmt.Errorf("error unmarshalling abi: %w", err)
p.logger.Warn("error processing event using abi", "err", err)
return nil, nil, nil
}
// Parse data
if item.Event != nil { //nolint:nestif // has complex nested blocks (complexity: 12) (nestif)
abiEvent, abiEventArgs, err := abiparse.ParseEvent(item.Event.EventBody.Topics, item.Event.EventBody.Data, &contractAbi)
eventArgs, err := marshalArgs(abiEvent.Inputs, abiEventArgs)
if err != nil {
p.logger.Warn("error processing event args using abi", "err", err)
return nil, nil, nil
}

return &abiEvent.Name, eventArgs, &abiEvent.ID
}

// Attempts to parse the raw evm.Call transaction data into the transaction
// method name and arguments as defined by the abi of the contract that was called.
func (p *processor) parseTxCall(tx *abiEncodedTx, contractAbi abi.ABI) (*string, []*abiEncodedArg) {
method, abiTxArgs, err := abiparse.ParseData(tx.TxData, &contractAbi)
if err != nil {
p.logger.Warn("error processing tx using abi", "err", err)
return nil, nil
}
txArgs, err := marshalArgs(method.Inputs, abiTxArgs)
if err != nil {
p.logger.Warn("error processing tx args using abi", "err", err)
return nil, nil
}

return &method.RawName, txArgs
}

// Attempts to parse the transaction revert reason into the error name and args
// as defined by the abi of the contract that was called.
func (p *processor) parseTxErr(tx *abiEncodedTx, contractAbi abi.ABI) (*string, []*abiEncodedArg) {
var abiErrMsg string
var abiErr *abi.Error
var abiErrArgs []interface{}
var errArgs []*abiEncodedArg
if tx.TxRevertReason != nil {
txrr, err := cleanTxRevertReason(*tx.TxRevertReason)
if err != nil {
queueIncompatibleEventUpdate(batch, p.runtime, item.Event.Round, item.Event.TxIndex)
p.logger.Warn("error processing event using abi", "contract address", item.ContractAddr, "err", err)
return nil
// This is most likely an older tx with a plaintext revert reason, such
// as "reverted: Ownable: caller is not the owner". In this case, we do
// not parse the error with the abi.
p.logger.Info("encountered likely old-style reverted transaction", "revert reason", tx.TxRevertReason, "tx hash", tx.TxHash, "err", err)
return nil, nil
}
eventArgs, err := marshalArgs(abiEvent.Inputs, abiEventArgs)
abiErr, abiErrArgs, err = abiparse.ParseError(txrr, &contractAbi)
if err != nil || abiErr == nil {
p.logger.Warn("error processing tx error using abi", "contract address", "err", err)
return nil, nil
}
abiErrMsg = runtime.TxRevertErrPrefix + abiErr.Name + prettyPrintArgs(abiErrArgs)
errArgs, err = marshalArgs(abiErr.Inputs, abiErrArgs)
if err != nil {
queueIncompatibleEventUpdate(batch, p.runtime, item.Event.Round, item.Event.TxIndex)
p.logger.Warn("error processing event args using abi", "contract address", item.ContractAddr, "err", err)
return nil
p.logger.Warn("error processing tx error args", "err", err)
return nil, nil
}
}

return &abiErrMsg, errArgs
}

func (p *processor) ProcessItem(ctx context.Context, batch *storage.QueryBatch, item *abiEncodedItem) error {
// Unmarshal abi
contractAbi, err := abi.JSON(bytes.NewReader(item.Abi))
if err != nil {
return fmt.Errorf("error unmarshalling abi: %w", err)
}
// Parse data
p.logger.Debug("processing item using abi", "contract_address", item.ContractAddr)
if item.Event != nil {
eventName, eventArgs, eventSig := p.parseEvent(item.Event, contractAbi)
batch.Queue(
queries.RuntimeEventEvmParsedFieldsUpdate,
p.runtime,
item.Event.Round,
item.Event.TxIndex,
abiEvent.RawName,
eventName,
eventArgs,
abiEvent.ID,
eventSig,
)
} else if item.Tx != nil {
method, abiTxArgs, err := abiparse.ParseData(item.Tx.TxData, &contractAbi)
if err != nil {
queueIncompatibleTxUpdate(batch, p.runtime, item.Tx.TxHash)
p.logger.Warn("error processing tx using abi", "contract address", item.ContractAddr, "err", err)
return nil
}
txArgs, err := marshalArgs(method.Inputs, abiTxArgs)
if err != nil {
queueIncompatibleTxUpdate(batch, p.runtime, item.Tx.TxHash)
p.logger.Warn("error processing tx args using abi", "contract address", item.ContractAddr, "err", err)
return nil
}
var abiErrName string
var abiErr *abi.Error
var abiErrArgs []interface{}
var errArgs []*abiEncodedArg
if item.Tx.TxRevertReason != nil {
txrr, err := cleanTxRevertReason(*item.Tx.TxRevertReason)
if err != nil {
// This is most likely an older tx with a plaintext revert reason, such
// as "reverted: Ownable: caller is not the owner". In this case, we do
// not parse the error with the abi, but we still update the tx table with
// the method and args.
batch.Queue(
queries.RuntimeTransactionEvmParsedFieldsUpdate,
p.runtime,
item.Tx.TxHash,
method.RawName,
txArgs,
nil, // error name
nil, // error args
)
p.logger.Info("encountered likely old-style reverted transaction", "revert reason", item.Tx.TxRevertReason, "tx hash", item.Tx.TxHash, "contract address", item.ContractAddr, "err", err)
return nil
}
abiErr, abiErrArgs, err = abiparse.ParseError(txrr, &contractAbi)
if err != nil || abiErr == nil {
queueIncompatibleTxUpdate(batch, p.runtime, item.Tx.TxHash)
p.logger.Warn("error processing tx error using abi", "contract address", item.ContractAddr, "err", err)
return nil
}
abiErrName = runtime.TxRevertErrPrefix + abiErr.Name + prettyPrintArgs(abiErrArgs)
errArgs, err = marshalArgs(abiErr.Inputs, abiErrArgs)
if err != nil {
queueIncompatibleTxUpdate(batch, p.runtime, item.Tx.TxHash)
p.logger.Warn("error processing tx error args", "contract address", item.ContractAddr, "err", err)
return nil
}
}
methodName, methodArgs := p.parseTxCall(item.Tx, contractAbi)
errMsg, errArgs := p.parseTxErr(item.Tx, contractAbi)
batch.Queue(
queries.RuntimeTransactionEvmParsedFieldsUpdate,
p.runtime,
item.Tx.TxHash,
method.RawName,
txArgs,
abiErrName,
methodName,
methodArgs,
errMsg,
errArgs,
)
}

return nil
}

// If abi processing of an event fails, it is likely due to incompatible
// data+abi, which is the event's fault. We thus mark the incompatible
// event as processed and continue.
//
// Note that if the abi is ever updated, we may need to revisit these events.
func queueIncompatibleEventUpdate(batch *storage.QueryBatch, runtime common.Runtime, round uint64, txIndex *int) {
batch.Queue(
queries.RuntimeEventEvmParsedFieldsUpdate,
runtime,
round,
txIndex,
nil, // event name
nil, // event args
nil, // event signature
)
}

// If abi processing of an transaction fails, it is likely due to incompatible
// data+abi, which is the transaction's fault. We thus mark the incompatible
// transaction as processed and continue.
//
// Note that if the abi is ever updated, we may need to revisit these txs.
func queueIncompatibleTxUpdate(batch *storage.QueryBatch, runtime common.Runtime, txHash string) {
batch.Queue(
queries.RuntimeTransactionEvmParsedFieldsUpdate,
runtime,
txHash,
nil, // method name
nil, // method args
nil, // error name
nil, // error args
)
}

func marshalArgs(abiArgs abi.Arguments, argVals []interface{}) ([]*abiEncodedArg, error) {
if len(abiArgs) != len(argVals) {
return nil, fmt.Errorf("number of args does not match abi specification")
Expand Down
16 changes: 9 additions & 7 deletions analyzer/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,14 @@ var (
INSERT INTO chain.runtime_transactions (runtime, round, tx_index, tx_hash, tx_eth_hash, fee, gas_limit, gas_used, size, timestamp, method, body, "to", amount, evm_encrypted_format, evm_encrypted_public_key, evm_encrypted_data_nonce, evm_encrypted_data_data, evm_encrypted_result_nonce, evm_encrypted_result_data, success, error_module, error_code, error_message_raw, error_message)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)`

// We use COALESCE here to avoid overwriting existing data with null values.
RuntimeTransactionEvmParsedFieldsUpdate = `
UPDATE chain.runtime_transactions
SET
evm_fn_name = $3,
evm_fn_params = $4,
error_message = $5,
error_params = $6,
evm_fn_name = COALESCE($3, evm_fn_name),
evm_fn_params = COALESCE($4, evm_fn_params),
error_message = COALESCE($5, error_message),
error_params = COALESCE($6, error_params),
abi_parsed_at = CURRENT_TIMESTAMP
WHERE
runtime = $1 AND
Expand All @@ -472,12 +473,13 @@ var (
INSERT INTO chain.runtime_events (runtime, round, tx_index, tx_hash, tx_eth_hash, timestamp, type, body, related_accounts, evm_log_name, evm_log_params, evm_log_signature)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`

// We use COALESCE here to avoid overwriting existing data with null values.
RuntimeEventEvmParsedFieldsUpdate = `
UPDATE chain.runtime_events
SET
evm_log_name = $4,
evm_log_params = $5,
evm_log_signature = $6,
evm_log_name = COALESCE($4, evm_log_name),
evm_log_params = COALESCE($5, evm_log_params),
evm_log_signature = COALESCE($6, evm_log_signature),
abi_parsed_at = CURRENT_TIMESTAMP
WHERE
runtime = $1 AND
Expand Down

0 comments on commit faea7b1

Please sign in to comment.