Skip to content

Commit

Permalink
feat(vald): unify GetTransactionReceiptsIfFinalized and GetTransactio…
Browse files Browse the repository at this point in the history
…nReceiptIfFinalized, and unify the behaviours of single and batch voting (#2120)

* feat(vald): unify GetTransactionReceiptsIfFinalized and GetTransactionReceiptIfFinalized, and unify the behaviours of single and batch voting

* remove commented-out code
  • Loading branch information
fish-sammy authored Mar 14, 2024
1 parent bc8a8db commit 48e9f98
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 250 deletions.
19 changes: 11 additions & 8 deletions vald/evm/deposit_confirmation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ func (mgr Mgr) ProcessDepositConfirmation(event *types.ConfirmDepositStarted) er
return nil
}

var vote *voteTypes.VoteRequest

txReceipt, err := mgr.GetTxReceiptIfFinalized(event.Chain, common.Hash(event.TxID), event.ConfirmationHeight)
if err != nil {
return err
}
if txReceipt == nil {
mgr.logger().Infof("broadcasting empty vote for poll %s", event.PollID.String())
_, err := mgr.broadcaster.Broadcast(context.TODO(), voteTypes.NewVoteRequest(mgr.proxy, event.PollID, types.NewVoteEvents(event.Chain)))
if txReceipt.Err() != nil {
vote = voteTypes.NewVoteRequest(mgr.proxy, event.PollID, types.NewVoteEvents(event.Chain))

return err
}
mgr.logger().Infof("broadcasting empty vote for poll %s: %s", event.PollID.String(), txReceipt.Err().Error())
} else {
events := mgr.processDepositConfirmationLogs(event, txReceipt.Ok().Logs)
vote = voteTypes.NewVoteRequest(mgr.proxy, event.PollID, types.NewVoteEvents(event.Chain, events...))

events := mgr.processDepositConfirmationLogs(event, txReceipt.Logs)
mgr.logger().Infof("broadcasting vote %v for poll %s", events, event.PollID.String())
}

mgr.logger().Infof("broadcasting vote %v for poll %s", events, event.PollID.String())
_, err = mgr.broadcaster.Broadcast(context.TODO(), voteTypes.NewVoteRequest(mgr.proxy, event.PollID, types.NewVoteEvents(event.Chain, events...)))
_, err = mgr.broadcaster.Broadcast(context.TODO(), vote)

return err
}
Expand Down
23 changes: 15 additions & 8 deletions vald/evm/deposit_confirmation_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package evm_test

import (
"bytes"
"context"
"math/big"
"strings"
Expand All @@ -22,6 +23,8 @@ import (
"github.com/axelarnetwork/axelar-core/x/evm/types/testutils"
nexus "github.com/axelarnetwork/axelar-core/x/nexus/exported"
votetypes "github.com/axelarnetwork/axelar-core/x/vote/types"
"github.com/axelarnetwork/utils/monads/results"
"github.com/axelarnetwork/utils/slices"
. "github.com/axelarnetwork/utils/test"
)

Expand Down Expand Up @@ -144,11 +147,14 @@ func TestMgr_ProccessDepositConfirmation(t *testing.T) {
HeaderByNumberFunc: func(context.Context, *big.Int) (*evmRpc.Header, error) {
return &evmRpc.Header{Transactions: []common.Hash{receipt.TxHash}}, nil
},
TransactionReceiptFunc: func(_ context.Context, txID common.Hash) (*geth.Receipt, error) {
if txID != receipt.TxHash {
return nil, ethereum.NotFound
}
return receipt, nil
TransactionReceiptsFunc: func(ctx context.Context, txHashes []common.Hash) ([]evmRpc.TxReceiptResult, error) {
return slices.Map(txHashes, func(txHash common.Hash) evmRpc.TxReceiptResult {
if bytes.Equal(txHash.Bytes(), receipt.TxHash.Bytes()) {
return evmRpc.TxReceiptResult(results.FromOk(*receipt))
}

return evmRpc.TxReceiptResult(results.FromErr[geth.Receipt](ethereum.NotFound))
}), nil
},
LatestFinalizedBlockNumberFunc: func(ctx context.Context, confirmations uint64) (*big.Int, error) {
return receipt.BlockNumber, nil
Expand All @@ -157,8 +163,10 @@ func TestMgr_ProccessDepositConfirmation(t *testing.T) {
}).
Branch(
Given("no deposit has been made", func() {
rpc.TransactionReceiptFunc = func(context.Context, common.Hash) (*geth.Receipt, error) {
return nil, ethereum.NotFound
rpc.TransactionReceiptsFunc = func(ctx context.Context, txHashes []common.Hash) ([]evmRpc.TxReceiptResult, error) {
return slices.Map(txHashes, func(hash common.Hash) evmRpc.TxReceiptResult {
return evmRpc.TxReceiptResult(results.FromErr[geth.Receipt](ethereum.NotFound))
}), nil
}
}).
When("confirming a random deposit on the correct chain", func() {
Expand All @@ -180,7 +188,6 @@ func TestMgr_ProccessDepositConfirmation(t *testing.T) {
event.Participants = append(event.Participants, valAddr)

err = mgr.ProcessDepositConfirmation(&event)

}).
Then("return error", func(t *testing.T) {
assert.Error(t, err)
Expand Down
111 changes: 57 additions & 54 deletions vald/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
geth "github.com/ethereum/go-ethereum/core/types"

Expand All @@ -18,7 +17,7 @@ import (
"github.com/axelarnetwork/axelar-core/x/evm/types"
nexus "github.com/axelarnetwork/axelar-core/x/nexus/exported"
"github.com/axelarnetwork/utils/log"
rs "github.com/axelarnetwork/utils/monads/results"
"github.com/axelarnetwork/utils/monads/results"
"github.com/axelarnetwork/utils/slices"
)

Expand Down Expand Up @@ -59,7 +58,7 @@ func (mgr Mgr) ProcessNewChain(event *types.ChainAdded) (err error) {
return nil
}

func (mgr Mgr) isTxReceiptFinalized(chain nexus.ChainName, txReceipt *geth.Receipt, confHeight uint64) (bool, error) {
func (mgr Mgr) isFinalized(chain nexus.ChainName, txReceipt geth.Receipt, confHeight uint64) (bool, error) {
client, ok := mgr.rpcs[strings.ToLower(chain.String())]
if !ok {
return false, fmt.Errorf("rpc client not found for chain %s", chain.String())
Expand All @@ -83,74 +82,78 @@ func (mgr Mgr) isTxReceiptFinalized(chain nexus.ChainName, txReceipt *geth.Recei
return true, nil
}

func (mgr Mgr) GetTxReceiptIfFinalized(chain nexus.ChainName, txID common.Hash, confHeight uint64) (*geth.Receipt, error) {
client, ok := mgr.rpcs[strings.ToLower(chain.String())]
if !ok {
return nil, fmt.Errorf("rpc client not found for chain %s", chain.String())
}

txReceipt, err := client.TransactionReceipt(context.Background(), txID)
keyvals := []interface{}{"chain", chain.String(), "tx_id", txID.Hex()}
logger := mgr.logger(keyvals...)
if err == ethereum.NotFound {
logger.Debug(fmt.Sprintf("transaction receipt %s not found", txID.Hex()))
return nil, nil
}
if err != nil {
return nil, sdkerrors.Wrap(errors.With(err, keyvals...), "failed getting transaction receipt")
}

if txReceipt.Status != geth.ReceiptStatusSuccessful {
return nil, nil
}

isFinalized, err := mgr.isTxReceiptFinalized(chain, txReceipt, confHeight)
// GetTxReceiptIfFinalized retrieves receipt for provided transaction ID.
//
// # Result is
//
// - Ok(receipt) if the transaction is finalized and successful
//
// - Err(ethereum.NotFound) if the transaction is not found
//
// - Err(ErrTxFailed) if the transaction is finalized but failed
//
// - Err(ErrNotFinalized) if the transaction is not finalized
//
// - Err(err) otherwise
func (mgr Mgr) GetTxReceiptIfFinalized(chain nexus.ChainName, txID common.Hash, confHeight uint64) (results.Result[geth.Receipt], error) {
txReceipts, err := mgr.GetTxReceiptsIfFinalized(chain, []common.Hash{txID}, confHeight)
if err != nil {
return nil, sdkerrors.Wrapf(errors.With(err, keyvals...), "cannot determine if the transaction %s is finalized", txID.Hex())
}
if !isFinalized {
logger.Debug(fmt.Sprintf("transaction %s in block %s not finalized", txID.Hex(), txReceipt.BlockNumber.String()))

return nil, nil
return results.Result[geth.Receipt]{}, err
}

return txReceipt, nil
return txReceipts[0], err
}

// GetTxReceiptsIfFinalized retrieves receipts for provided transaction IDs, only if they're finalized.
func (mgr Mgr) GetTxReceiptsIfFinalized(chain nexus.ChainName, txIDs []common.Hash, confHeight uint64) ([]rs.Result[*geth.Receipt], error) {
// GetTxReceiptsIfFinalized retrieves receipts for provided transaction IDs.
//
// # Individual result is
//
// - Ok(receipt) if the transaction is finalized and successful
//
// - Err(ethereum.NotFound) if the transaction is not found
//
// - Err(ErrTxFailed) if the transaction is finalized but failed
//
// - Err(ErrNotFinalized) if the transaction is not finalized
//
// - Err(err) otherwise
func (mgr Mgr) GetTxReceiptsIfFinalized(chain nexus.ChainName, txIDs []common.Hash, confHeight uint64) ([]results.Result[geth.Receipt], error) {
client, ok := mgr.rpcs[strings.ToLower(chain.String())]
if !ok {
return nil, fmt.Errorf("rpc client not found for chain %s", chain.String())
}

results, err := client.TransactionReceipts(context.Background(), txIDs)
receipts, err := client.TransactionReceipts(context.Background(), txIDs)
if err != nil {
return nil, sdkerrors.Wrapf(errors.With(err, "chain", chain.String(), "tx_ids", txIDs),
"cannot get transaction receipts")
return slices.Map(txIDs, func(_ common.Hash) results.Result[geth.Receipt] {
return results.FromErr[geth.Receipt](
sdkerrors.Wrapf(
errors.With(err, "chain", chain.String(), "tx_ids", txIDs),
"cannot get transaction receipts"),
)
}), nil
}

isFinalized := func(receipt *geth.Receipt) rs.Result[*geth.Receipt] {
if receipt.Status != geth.ReceiptStatusSuccessful {
return rs.FromErr[*geth.Receipt](ErrTxFailed)
}
return slices.Map(receipts, func(receipt rpc.TxReceiptResult) results.Result[geth.Receipt] {
return results.Pipe(results.Result[geth.Receipt](receipt), func(receipt geth.Receipt) results.Result[geth.Receipt] {

isFinalized, err := mgr.isTxReceiptFinalized(chain, receipt, confHeight)
if err != nil {
return rs.FromErr[*geth.Receipt](sdkerrors.Wrapf(errors.With(err, "chain", chain.String()),
"cannot determine if the transaction %s is finalized", receipt.TxHash.Hex()),
)
}
isFinalized, err := mgr.isFinalized(chain, receipt, confHeight)
if err != nil {
return results.FromErr[geth.Receipt](sdkerrors.Wrapf(errors.With(err, "chain", chain.String()),
"cannot determine if the transaction %s is finalized", receipt.TxHash.Hex()),
)
}

if !isFinalized {
return rs.FromErr[*geth.Receipt](ErrNotFinalized)
}
if !isFinalized {
return results.FromErr[geth.Receipt](ErrNotFinalized)
}

return rs.FromOk(receipt)
}
if receipt.Status != geth.ReceiptStatusSuccessful {
return results.FromErr[geth.Receipt](ErrTxFailed)
}

return slices.Map(results, func(r rpc.Result) rs.Result[*geth.Receipt] {
return rs.Pipe(rs.Result[*geth.Receipt](r), isFinalized)
return results.FromOk[geth.Receipt](receipt)
})
}), nil
}

Expand Down
Loading

0 comments on commit 48e9f98

Please sign in to comment.