Skip to content

Commit

Permalink
cleaner decode code (#1099)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tofel authored Aug 28, 2024
1 parent bf3c491 commit 18dd1ae
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 183 deletions.
168 changes: 0 additions & 168 deletions seth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package seth
import (
"context"
"crypto/ecdsa"
verr "errors"
"fmt"
"math/big"
"net/http"
Expand Down Expand Up @@ -420,173 +419,6 @@ func (m *Client) checkRPCHealth() error {
return nil
}

// Decode waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and
// depending on 'tracing_level' it either returns immediately or if the level matches it traces all calls.
// Where tracing results go depends on the 'trace_outputs' field in the config.
// If transaction was reverted the error returned will be revert error, not decoding error (that one, if any, will be logged).
// At the same time we also return decoded transaction, so contrary to go convention you might get both error and result.
// Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction timeout and resubmit it with higher gas.
func (m *Client) Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error) {
if len(m.Errors) > 0 {
return nil, verr.Join(m.Errors...)
}

// do not try to decode ABI error if contract deployment failed, because the error is not related to ABI
if txErr != nil {
//try to decode revert reason
reason, decodingErr := m.DecodeCustomABIErr(txErr)

if decodingErr == nil {
return nil, errors.Wrap(txErr, reason)
}

L.Trace().
Msg("Skipping decoding, transaction submission failed. Nothing to decode")
return nil, txErr
}

if tx == nil {
L.Trace().
Msg("Skipping decoding, because transaction is nil. Nothing to decode")
return nil, nil
}

l := L.With().Str("Transaction", tx.Hash().Hex()).Logger()

// if transaction was not mined, we will retry it with gas bumping, but only if gas bumping is enabled
// and if the transaction was not mined in time, other errors will be returned as is
var receipt *types.Receipt
err := retry.Do(
func() error {
var err error
ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration())
receipt, err = m.WaitMined(ctx, l, m.Client, tx)
cancel()

return err
}, retry.OnRetry(func(i uint, retryErr error) {
replacementTx, replacementErr := prepareReplacementTransaction(m, tx)
if replacementErr != nil {
L.Debug().Str("Replacement error", replacementErr.Error()).Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Failed to prepare replacement transaction. Retrying without the original one")
return
}
L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Waiting for transaction to be confirmed after gas bump")
tx = replacementTx
}),
retry.DelayType(retry.FixedDelay),
// unless attempts is at least 1 retry.Do won't execute at all
retry.Attempts(func() uint {
if m.Cfg.GasBumpRetries() == 0 {
return 1
}
return m.Cfg.GasBumpRetries()
}()),
retry.RetryIf(func(err error) bool {
return m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded)
}),
)

if err != nil {
L.Trace().
Err(err).
Msg("Skipping decoding, because transaction was not minted. Nothing to decode")
return nil, err
}

var revertErr error
if receipt.Status == 0 {
revertErr = m.callAndGetRevertReason(tx, receipt)
}

decoded, decodeErr := m.decodeTransaction(l, tx, receipt)

if decodeErr != nil && errors.Is(decodeErr, errors.New(ErrNoABIMethod)) {
if m.Cfg.hasOutput(TraceOutput_JSON) {
L.Trace().
Err(decodeErr).
Msg("Failed to decode transaction. Saving transaction data hash as JSON")

err = CreateOrAppendToJsonArray(m.Cfg.revertedTransactionsFile, tx.Hash().Hex())
if err != nil {
l.Warn().
Err(err).
Str("TXHash", tx.Hash().Hex()).
Msg("Failed to save reverted transaction hash to file")
} else {
l.Trace().
Str("TXHash", tx.Hash().Hex()).
Msg("Saved reverted transaction to file")
}
}
m.printDecodedTXData(l, decoded)
return decoded, revertErr
}

if m.Cfg.TracingLevel == TracingLevel_None {
L.Trace().
Str("Transaction Hash", tx.Hash().Hex()).
Msg("Tracing level is NONE, skipping decoding")
m.printDecodedTXData(l, decoded)
return decoded, revertErr
}

if m.Cfg.TracingLevel == TracingLevel_All || (m.Cfg.TracingLevel == TracingLevel_Reverted && revertErr != nil) {
traceErr := m.Tracer.TraceGethTX(decoded.Hash, revertErr)
if traceErr != nil {
if m.Cfg.hasOutput(TraceOutput_JSON) {
L.Trace().
Err(traceErr).
Msg("Failed to trace call, but decoding was successful. Saving decoded data as JSON")

path, saveErr := saveAsJson(decoded, filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash)
if saveErr != nil {
L.Warn().
Err(saveErr).
Msg("Failed to save decoded call as JSON")
} else {
L.Trace().
Str("Path", path).
Str("Tx hash", decoded.Hash).
Msg("Saved decoded transaction data to JSON")
}
}

if strings.Contains(traceErr.Error(), "debug_traceTransaction does not exist") {
L.Warn().
Err(err).
Msg("Debug API is either disabled or not available on the node. Disabling tracing")

m.Cfg.TracingLevel = TracingLevel_None
}

m.printDecodedTXData(l, decoded)
return decoded, revertErr
}

if m.Cfg.hasOutput(TraceOutput_JSON) {
path, saveErr := saveAsJson(m.Tracer.GetDecodedCalls(decoded.Hash), filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash)
if saveErr != nil {
L.Warn().
Err(saveErr).
Msg("Failed to save decoded call as JSON")
} else {
L.Trace().
Str("Path", path).
Str("Tx hash", decoded.Hash).
Msg("Saved decoded call data to JSON")
}
}
} else {
L.Trace().
Str("Transaction Hash", tx.Hash().Hex()).
Str("Tracing level", m.Cfg.TracingLevel).
Bool("Was reverted?", revertErr != nil).
Msg("Transaction doesn't match tracing level, skipping decoding")
}

return decoded, revertErr
}

func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to string, value *big.Int, gasPrice *big.Int) error {
if fromKeyNum > len(m.PrivateKeys) || fromKeyNum > len(m.Addresses) {
return errors.Wrap(errors.New(ErrNoKeyLoaded), fmt.Sprintf("requested key: %d", fromKeyNum))
Expand Down
Loading

0 comments on commit 18dd1ae

Please sign in to comment.