Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge implementation to v3.5 #342

Merged
merged 8 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
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
128 changes: 106 additions & 22 deletions bridges/ethMultiversX/bridgeExecutor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ import (
"github.com/multiversx/mx-bridge-eth-go/core"
bridgeCore "github.com/multiversx/mx-bridge-eth-go/core"
"github.com/multiversx/mx-bridge-eth-go/core/batchProcessor"
"github.com/multiversx/mx-bridge-eth-go/core/converters"
"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/multiversx/mx-chain-core-go/data/transaction"
logger "github.com/multiversx/mx-chain-logger-go"
)

// splits - represent the number of times we split the maximum interval
// we wait for the transfer confirmation on Ethereum
const splits = 10
const minRetries = 1

const (
// splits - represent the number of times we split the maximum interval
// we wait for the transfer confirmation on Ethereum
splits = 10

// Minimum number of retries
minRetries = 1

// Number of topics required for createTransactionScCallEvent
scCallEventTopicsCount = 9

// Indices for specific topics in createTransactionScCallEvent
depositNonceIndex = 1
calldataIndex = 8
)

// ArgsBridgeExecutor is the arguments DTO struct used in both bridges
type ArgsBridgeExecutor struct {
Expand Down Expand Up @@ -153,10 +169,28 @@ func (executor *bridgeExecutor) MyTurnAsLeader() bool {
// GetBatchFromMultiversX fetches the pending batch from MultiversX
func (executor *bridgeExecutor) GetBatchFromMultiversX(ctx context.Context) (*bridgeCore.TransferBatch, error) {
batch, err := executor.multiversXClient.GetPendingBatch(ctx)
if err == nil {
executor.statusHandler.SetIntMetric(core.MetricNumBatches, int(batch.ID)-1)
if err != nil {
return nil, err
}

if batch == nil {
return nil, ErrNilBatch
}

executor.statusHandler.SetIntMetric(core.MetricNumBatches, int(batch.ID)-1)

isBatchInvalid := len(batch.Deposits) == 0
if isBatchInvalid {
return nil, fmt.Errorf("%w, fetched nonce: %d",
ErrBatchWithoutDeposits, batch.ID)
}

batch, err = executor.addBatchSCMetadataMvx(ctx, batch)
if err != nil {
return nil, err
}
return batch, err

return batch, nil
}

// StoreBatchFromMultiversX saves the pending batch from MultiversX
Expand All @@ -169,6 +203,53 @@ func (executor *bridgeExecutor) StoreBatchFromMultiversX(batch *bridgeCore.Trans
return nil
}

// addBatchSCMetadataMvx fetches the logs containing sc calls metadata for the current batch
func (executor *bridgeExecutor) addBatchSCMetadataMvx(ctx context.Context, batch *bridgeCore.TransferBatch) (*bridgeCore.TransferBatch, error) {
events, err := executor.multiversXClient.GetBatchSCMetadata(ctx, batch)
if err != nil {
return nil, err
}

eventsByDepositNonce := make(map[uint64]*transaction.Events)

for _, event := range events {
if len(event.Topics) < scCallEventTopicsCount {
return nil, ErrInvalidTopicsNumber
}

depositNonceBytes := event.Topics[depositNonceIndex]
depositNonce, err := converters.ParseUInt64FromByteSlice(depositNonceBytes)
if err != nil {
return nil, fmt.Errorf("%w while parsing deposit nonce", err)
}

eventsByDepositNonce[depositNonce] = event
}

for _, t := range batch.Deposits {
executor.addMetadataToTransferMvx(t, eventsByDepositNonce)
}

return batch, nil
}

// addMetadataToTransferMvx fetches the logs containing sc calls metadata for the current batch
func (executor *bridgeExecutor) addMetadataToTransferMvx(transfer *bridgeCore.DepositTransfer, eventsByDepositNonce map[uint64]*transaction.Events) {
event, exists := eventsByDepositNonce[transfer.Nonce]
if !exists {
transfer.DisplayableData = ""
return
}

calldataBytes := event.Topics[calldataIndex]
processDataForMvx(transfer, calldataBytes)
}

func processDataForMvx(transfer *bridgeCore.DepositTransfer, buff []byte) {
transfer.Data = buff
transfer.DisplayableData = hex.EncodeToString(transfer.Data)
}

// GetStoredBatch returns the stored batch
func (executor *bridgeExecutor) GetStoredBatch() *bridgeCore.TransferBatch {
return executor.batch
Expand Down Expand Up @@ -462,38 +543,41 @@ func (executor *bridgeExecutor) GetAndStoreBatchFromEthereum(ctx context.Context
}

// addBatchSCMetadata fetches the logs containing sc calls metadata for the current batch
func (executor *bridgeExecutor) addBatchSCMetadata(ctx context.Context, transfers *bridgeCore.TransferBatch) (*bridgeCore.TransferBatch, error) {
if transfers == nil {
func (executor *bridgeExecutor) addBatchSCMetadata(ctx context.Context, batch *bridgeCore.TransferBatch) (*bridgeCore.TransferBatch, error) {
if batch == nil {
return nil, ErrNilBatch
}

events, err := executor.ethereumClient.GetBatchSCMetadata(ctx, transfers.ID, int64(transfers.BlockNumber))
events, err := executor.ethereumClient.GetBatchSCMetadata(ctx, batch.ID, int64(batch.BlockNumber))
if err != nil {
return nil, err
}

for i, t := range transfers.Deposits {
transfers.Deposits[i] = executor.addMetadataToTransfer(t, events)
eventsByDepositNonce := make(map[uint64]*contract.ERC20SafeERC20SCDeposit)

for _, event := range events {
eventsByDepositNonce[event.DepositNonce.Uint64()] = event
}

for _, t := range batch.Deposits {
executor.addMetadataToTransfer(t, eventsByDepositNonce)
}

return transfers, nil
return batch, nil
}

func (executor *bridgeExecutor) addMetadataToTransfer(transfer *bridgeCore.DepositTransfer, events []*contract.ERC20SafeERC20SCDeposit) *bridgeCore.DepositTransfer {
for _, event := range events {
if event.DepositNonce.Uint64() == transfer.Nonce {
processData(transfer, event.CallData)
return transfer
}
func (executor *bridgeExecutor) addMetadataToTransfer(transfer *bridgeCore.DepositTransfer, eventsByDepositNonce map[uint64]*contract.ERC20SafeERC20SCDeposit) {
event, exist := eventsByDepositNonce[transfer.Nonce]
if !exist {
transfer.Data = []byte{bridgeCore.MissingDataProtocolMarker}
transfer.DisplayableData = ""
return
}

transfer.Data = []byte{bridgeCore.MissingDataProtocolMarker}
transfer.DisplayableData = ""

return transfer
processDataForEth(transfer, event.CallData)
}

func processData(transfer *bridgeCore.DepositTransfer, buff []byte) {
func processDataForEth(transfer *bridgeCore.DepositTransfer, buff []byte) {
transfer.Data = buff
dataLen := len(transfer.Data)
if dataLen == 0 {
Expand Down
86 changes: 81 additions & 5 deletions bridges/ethMultiversX/bridgeExecutor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package ethmultiversx

import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"math/big"
Expand All @@ -17,6 +19,7 @@ import (
"github.com/multiversx/mx-bridge-eth-go/testsCommon"
bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge"
"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/multiversx/mx-chain-core-go/data/transaction"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -991,27 +994,100 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreBatchFromMultiversX(t *testing
err := executor.StoreBatchFromMultiversX(nil)
assert.Equal(t, ErrNilBatch, err)
})
t.Run("should work", func(t *testing.T) {
t.Run("no deposits should error", func(t *testing.T) {
t.Parallel()

wasCalled := false
args := createMockExecutorArgs()
wasCalled := false
args.MultiversXClient = &bridgeTests.MultiversXClientStub{
GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) {
wasCalled = true
return providedBatch, nil
return &bridgeCore.TransferBatch{}, nil
},
}

executor, _ := NewBridgeExecutor(args)
batch, err := executor.GetBatchFromMultiversX(context.Background())
assert.True(t, wasCalled)
assert.Equal(t, providedBatch, batch)
assert.Nil(t, batch)
assert.Equal(t, fmt.Errorf("%w, fetched nonce: %d", ErrBatchWithoutDeposits, 0), err)
})
t.Run("should work", func(t *testing.T) {
t.Parallel()

wasPendingBatchCalled := false
wasGetBatchSCMetadataCalled := false
providedBatchWithDeposits := &bridgeCore.TransferBatch{
Deposits: []*bridgeCore.DepositTransfer{{}, {}},
}
args := createMockExecutorArgs()
args.MultiversXClient = &bridgeTests.MultiversXClientStub{
GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) {
wasPendingBatchCalled = true
return providedBatchWithDeposits, nil
},
GetBatchSCMetadataCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) ([]*transaction.Events, error) {
wasGetBatchSCMetadataCalled = true
return []*transaction.Events{}, nil
},
}

executor, _ := NewBridgeExecutor(args)
batch, err := executor.GetBatchFromMultiversX(context.Background())
assert.True(t, wasPendingBatchCalled)
assert.True(t, wasGetBatchSCMetadataCalled)
assert.Equal(t, providedBatchWithDeposits, batch)
assert.Nil(t, err)

err = executor.StoreBatchFromMultiversX(batch)
assert.Equal(t, providedBatch, executor.batch)
assert.Equal(t, providedBatchWithDeposits, executor.batch)
assert.Nil(t, err)
})
t.Run("should add deposits metadata for sc calls", func(t *testing.T) {
t.Parallel()

providedNonce := uint64(8346)
depositNonce := uint64(100)

providedBatchWithDeposit := &bridgeCore.TransferBatch{
ID: providedNonce,
Deposits: []*bridgeCore.DepositTransfer{
{
Nonce: depositNonce,
},
},
}

depositData := []byte("testData")
expectedBatch := &bridgeCore.TransferBatch{
ID: providedNonce,
Deposits: []*bridgeCore.DepositTransfer{
{
Nonce: depositNonce,
Data: depositData,
DisplayableData: hex.EncodeToString(depositData),
},
},
}

args := createMockExecutorArgs()
args.MultiversXClient = &bridgeTests.MultiversXClientStub{
GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) {
return providedBatchWithDeposit, nil
},
GetBatchSCMetadataCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) ([]*transaction.Events, error) {
depositNonceBytes := make([]byte, 8)
binary.BigEndian.PutUint64(depositNonceBytes, depositNonce)
return []*transaction.Events{
{Topics: [][]byte{{}, depositNonceBytes, {}, {}, {}, {}, {}, {}, depositData}},
}, nil
},
}

executor, _ := NewBridgeExecutor(args)
batch, err := executor.GetBatchFromMultiversX(context.Background())
assert.Nil(t, err)
assert.Equal(t, expectedBatch, batch)
})
}

Expand Down
6 changes: 6 additions & 0 deletions bridges/ethMultiversX/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ var ErrNilStatusHandler = errors.New("nil status handler")
// ErrFinalBatchNotFound signals that a final batch was not found
var ErrFinalBatchNotFound = errors.New("final batch not found")

// ErrBatchWithoutDeposits signals that a batch was found, but has no deposits
var ErrBatchWithoutDeposits = errors.New("batch was found, but has no deposits")

// ErrInvalidTopicsNumber signals that an invalid number of topics was provided
var ErrInvalidTopicsNumber = errors.New("invalid number of topics")

// ErrNilSignaturesHolder signals that a nil signatures holder was provided
var ErrNilSignaturesHolder = errors.New("nil signatures holder")

Expand Down
2 changes: 2 additions & 0 deletions bridges/ethMultiversX/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract"
bridgeCore "github.com/multiversx/mx-bridge-eth-go/core"
"github.com/multiversx/mx-bridge-eth-go/core/batchProcessor"
"github.com/multiversx/mx-chain-core-go/data/transaction"
)

// MultiversXClient defines the behavior of the MultiversX client able to communicate with the MultiversX chain
Expand All @@ -25,6 +26,7 @@ type MultiversXClient interface {
GetLastExecutedEthBatchID(ctx context.Context) (uint64, error)
GetLastExecutedEthTxID(ctx context.Context) (uint64, error)
GetCurrentNonce(ctx context.Context) (uint64, error)
GetBatchSCMetadata(ctx context.Context, batch *bridgeCore.TransferBatch) ([]*transaction.Events, error)

ProposeSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error)
ProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error)
Expand Down
Loading
Loading