Skip to content

Commit

Permalink
Merge pull request #342 from multiversx/merge-implementation-to-v3.5
Browse files Browse the repository at this point in the history
Merge implementation to v3.5
  • Loading branch information
cosmatudor authored Oct 4, 2024
2 parents 8cd2a8a + 436846e commit 0d2ff83
Show file tree
Hide file tree
Showing 22 changed files with 562 additions and 85 deletions.
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

0 comments on commit 0d2ff83

Please sign in to comment.