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

chore: Refactor deposit handlers #10

Closed
wants to merge 7 commits into from
Closed
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ genmocks:
mockgen -source=chains/evm/transactor/transact.go -destination=./mock/transact.go -package mock
mockgen -source=chains/evm/transactor/signAndSend/signAndSend.go -destination=./mock/signAndSend.go -package mock
mockgen -source=./store/store.go -destination=./mock/store.go -package mock
mockgen -destination=chains/evm/eventhandlers/mock/eventhandlers.go -source=./chains/evm/eventhandlers/event-handler.go -package mock
mockgen -source=./relayer/message/handler.go -destination=./mock/message.go -package mock
mockgen -source=./chains/evm/listener/listener.go -destination=./mock/evmListener.go -package mock
mockgen -destination=./mock/substrateListener.go -package mock github.com/sygmaprotocol/sygma-core/chains/substrate/listener ChainConnection
64 changes: 64 additions & 0 deletions chains/evm/deposithandlers/deposit-handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package deposithandlers

import (
"errors"

"github.com/ChainSafe/sygma-core/chains/evm/eventhandlers"

Check failure on line 6 in chains/evm/deposithandlers/deposit-handler.go

View workflow job for this annotation

GitHub Actions / vet-check

no required module provides package github.com/ChainSafe/sygma-core/chains/evm/eventhandlers; to add it:

Check failure on line 6 in chains/evm/deposithandlers/deposit-handler.go

View workflow job for this annotation

GitHub Actions / test (1.19.x, ubuntu-latest)

no required module provides package github.com/ChainSafe/sygma-core/chains/evm/eventhandlers; to add it:

"github.com/ChainSafe/sygma-core/types"

Check failure on line 8 in chains/evm/deposithandlers/deposit-handler.go

View workflow job for this annotation

GitHub Actions / vet-check

no required module provides package github.com/ChainSafe/sygma-core/types; to add it:

Check failure on line 8 in chains/evm/deposithandlers/deposit-handler.go

View workflow job for this annotation

GitHub Actions / test (1.19.x, ubuntu-latest)

no required module provides package github.com/ChainSafe/sygma-core/types; to add it:
"github.com/rs/zerolog/log"

"github.com/ethereum/go-ethereum/common"
)

type DepositHandlers map[common.Address]eventhandlers.DepositHandler
type HandlerMatcher interface {
GetHandlerAddressForResourceID(resourceID types.ResourceID) (common.Address, error)
}

type ETHDepositHandler struct {
handlerMatcher HandlerMatcher
depositHandlers DepositHandlers
}

// NewETHDepositHandler creates an instance of ETHDepositHandler that contains
// handler functions for processing deposit events
func NewETHDepositHandler(handlerMatcher HandlerMatcher) *ETHDepositHandler {
return &ETHDepositHandler{
handlerMatcher: handlerMatcher,
depositHandlers: make(map[common.Address]eventhandlers.DepositHandler),
}
}

func (e *ETHDepositHandler) HandleDeposit(sourceID, destID uint8, depositNonce uint64, resourceID types.ResourceID, calldata, handlerResponse []byte) (*types.Message, error) {
handlerAddr, err := e.handlerMatcher.GetHandlerAddressForResourceID(resourceID)
if err != nil {
return nil, err
}

depositHandler, err := e.matchAddressWithHandlerFunc(handlerAddr)
if err != nil {
return nil, err
}

return depositHandler.HandleDeposit(sourceID, destID, depositNonce, resourceID, calldata, handlerResponse)
}

// matchAddressWithHandlerFunc matches a handler address with an associated handler function
func (e *ETHDepositHandler) matchAddressWithHandlerFunc(handlerAddress common.Address) (eventhandlers.DepositHandler, error) {
hf, ok := e.depositHandlers[handlerAddress]
if !ok {
return nil, errors.New("no corresponding deposit handler for this address exists")
}
return hf, nil
}

// RegisterDepositHandler registers an event handler by associating a handler function to a specified address
func (e *ETHDepositHandler) RegisterDepositHandler(handlerAddress string, handler eventhandlers.DepositHandler) {
if handlerAddress == "" {
return
}

log.Debug().Msgf("Registered deposit handler for address %s", handlerAddress)
e.depositHandlers[common.HexToAddress(handlerAddress)] = handler
}
54 changes: 54 additions & 0 deletions chains/evm/deposithandlers/erc20.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package deposithandlers

import (
"errors"
"math/big"

"github.com/ChainSafe/sygma-core/types"
)

type Erc20DepositHandler struct{}

// Erc20DepositHandler converts data pulled from event logs into message
// handlerResponse can be an empty slice
func (dh *Erc20DepositHandler) HandleDeposit(sourceID, destId uint8, nonce uint64, resourceID types.ResourceID, calldata, handlerResponse []byte) (*types.Message, error) {
if len(calldata) < 84 {
err := errors.New("invalid calldata length: less than 84 bytes")
return nil, err
}

// @dev
// amount: first 32 bytes of calldata
amount := calldata[:32]

// lenRecipientAddress: second 32 bytes of calldata [32:64]
// does not need to be derived because it is being calculated
// within ERC20MessageHandler
// https://github.com/ChainSafe/chainbridge-core/blob/main/chains/evm/voter/message-handler.go#L108

// 32-64 is recipient address length
recipientAddressLength := big.NewInt(0).SetBytes(calldata[32:64])

// 64 - (64 + recipient address length) is recipient address
recipientAddress := calldata[64:(64 + recipientAddressLength.Int64())]

// if there is priority data, parse it and use it
payload := []interface{}{
amount,
recipientAddress,
}

// arbitrary metadata that will be most likely be used by the relayer
var metadata types.Metadata
if 64+recipientAddressLength.Int64() < int64(len(calldata)) {
priorityLength := big.NewInt(0).SetBytes(calldata[(64 + recipientAddressLength.Int64()):((64 + recipientAddressLength.Int64()) + 1)])

// (64 + recipient address length + 1) - ((64 + recipient address length + 1) + priority length) is priority data
priority := calldata[(64 + recipientAddressLength.Int64() + 1):((64 + recipientAddressLength.Int64()) + 1 + priorityLength.Int64())]

// Assign the priority data to the Metadata struct
metadata.Data = make(map[string]interface{})
metadata.Data["Priority"] = priority[0]
}
return types.NewMessage(sourceID, destId, nonce, resourceID, types.FungibleTransfer, payload, metadata), nil
}
138 changes: 138 additions & 0 deletions chains/evm/deposithandlers/erc20_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package deposithandlers_test

import (
"errors"
"math/big"
"testing"

"github.com/ChainSafe/chainbridge-core/chains/evm/calls/contracts/deposit"
"github.com/ChainSafe/sygma-core/chains/evm/deposithandlers"
"github.com/ChainSafe/sygma-core/chains/evm/eventhandlers"
"github.com/ChainSafe/sygma-core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/stretchr/testify/suite"
)

var errIncorrectDataLen = errors.New("invalid calldata length: less than 84 bytes")

type Erc20HandlerTestSuite struct {
suite.Suite
}

func TestRunErc20HandlerTestSuite(t *testing.T) {
suite.Run(t, new(Erc20HandlerTestSuite))
}

func (s *Erc20HandlerTestSuite) SetupSuite() {}
func (s *Erc20HandlerTestSuite) TearDownSuite() {}
func (s *Erc20HandlerTestSuite) SetupTest() {}
func (s *Erc20HandlerTestSuite) TearDownTest() {}

func (s *Erc20HandlerTestSuite) TestErc20HandleEvent() {
// 0xf1e58fb17704c2da8479a533f9fad4ad0993ca6b
recipientByteSlice := []byte{241, 229, 143, 177, 119, 4, 194, 218, 132, 121, 165, 51, 249, 250, 212, 173, 9, 147, 202, 107}

calldata := deposit.ConstructErc20DepositData(recipientByteSlice, big.NewInt(2))
depositLog := &eventhandlers.Deposit{
DestinationDomainID: 0,
ResourceID: [32]byte{0},
DepositNonce: 1,
SenderAddress: common.HexToAddress("0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485"),
Data: calldata,
HandlerResponse: []byte{},
}

sourceID := uint8(1)
amountParsed := calldata[:32]
recipientAddressParsed := calldata[64:]

expected := &types.Message{
Source: sourceID,
Destination: depositLog.DestinationDomainID,
DepositNonce: depositLog.DepositNonce,
ResourceId: depositLog.ResourceID,
Type: types.FungibleTransfer,
Payload: []interface{}{
amountParsed,
recipientAddressParsed,
},
}
erc20DepositHandler := deposithandlers.Erc20DepositHandler{}
message, err := erc20DepositHandler.HandleDeposit(sourceID, depositLog.DestinationDomainID, depositLog.DepositNonce, depositLog.ResourceID, depositLog.Data, depositLog.HandlerResponse)

s.Nil(err)
s.NotNil(message)
s.Equal(message, expected)
}

func (s *Erc20HandlerTestSuite) TestErc20HandleEventWithPriority() {
// 0xf1e58fb17704c2da8479a533f9fad4ad0993ca6b
recipientByteSlice := []byte{241, 229, 143, 177, 119, 4, 194, 218, 132, 121, 165, 51, 249, 250, 212, 173, 9, 147, 202, 107}

calldata := deposit.ConstructErc20DepositDataWithPriority(recipientByteSlice, big.NewInt(2), uint8(1))
depositLog := &eventhandlers.Deposit{
DestinationDomainID: 0,
ResourceID: [32]byte{0},
DepositNonce: 1,
SenderAddress: common.HexToAddress("0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485"),
Data: calldata,
HandlerResponse: []byte{},
}

sourceID := uint8(1)
amountParsed := calldata[:32]
// 32-64 is recipient address length
recipientAddressLength := big.NewInt(0).SetBytes(calldata[32:64])

// 64 - (64 + recipient address length) is recipient address
recipientAddressParsed := calldata[64:(64 + recipientAddressLength.Int64())]
expected := &types.Message{
Source: sourceID,
Destination: depositLog.DestinationDomainID,
DepositNonce: depositLog.DepositNonce,
ResourceId: depositLog.ResourceID,
Type: types.FungibleTransfer,
Payload: []interface{}{
amountParsed,
recipientAddressParsed,
},
Metadata: types.Metadata{
Data: map[string]interface{}{
"Priority": uint8(1),
},
},
}

erc20DepositHandler := deposithandlers.Erc20DepositHandler{}
message, err := erc20DepositHandler.HandleDeposit(sourceID, depositLog.DestinationDomainID, depositLog.DepositNonce, depositLog.ResourceID, depositLog.Data, depositLog.HandlerResponse)

s.Nil(err)
s.NotNil(message)
s.Equal(message, expected)
}

func (s *Erc20HandlerTestSuite) TestErc20HandleEventIncorrectDataLen() {
metadata := []byte("0xdeadbeef")

var calldata []byte
calldata = append(calldata, math.PaddedBigBytes(big.NewInt(int64(len(metadata))), 32)...)
calldata = append(calldata, metadata...)

depositLog := &eventhandlers.Deposit{
DestinationDomainID: 0,
ResourceID: [32]byte{0},
DepositNonce: 1,
SenderAddress: common.HexToAddress("0x4CEEf6139f00F9F4535Ad19640Ff7A0137708485"),
Data: calldata,
HandlerResponse: []byte{},
}

sourceID := uint8(1)

erc20DepositHandler := deposithandlers.Erc20DepositHandler{}
message, err := erc20DepositHandler.HandleDeposit(sourceID, depositLog.DestinationDomainID, depositLog.DepositNonce, depositLog.ResourceID, depositLog.Data, depositLog.HandlerResponse)

s.Nil(message)
s.EqualError(err, errIncorrectDataLen.Error())
}
59 changes: 59 additions & 0 deletions chains/evm/deposithandlers/erc721.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package deposithandlers

import (
"errors"
"math/big"

"github.com/ChainSafe/sygma-core/types"
)

type Erc721DepositHandler struct{}

// Erc721DepositHandler converts data pulled from ERC721 deposit event logs into message
func (dh *Erc721DepositHandler) HandleDeposit(sourceID, destId uint8, nonce uint64, resourceID types.ResourceID, calldata, handlerResponse []byte) (*types.Message, error) {
if len(calldata) < 64 {
err := errors.New("invalid calldata length: less than 84 bytes")
return nil, err
}

// first 32 bytes are tokenId
tokenId := calldata[:32]

// 32 - 64 is recipient address length
recipientAddressLength := big.NewInt(0).SetBytes(calldata[32:64])

// 64 - (64 + recipient address length) is recipient address
recipientAddress := calldata[64:(64 + recipientAddressLength.Int64())]

// (64 + recipient address length) - ((64 + recipient address length) + 32) is metadata length
metadataLength := big.NewInt(0).SetBytes(
calldata[(64 + recipientAddressLength.Int64()):((64 + recipientAddressLength.Int64()) + 32)],
)
// ((64 + recipient address length) + 32) - ((64 + recipient address length) + 32 + metadata length) is metadata
var metadata []byte
var metadataStart int64
if metadataLength.Cmp(big.NewInt(0)) == 1 {
metadataStart = (64 + recipientAddressLength.Int64()) + 32
metadata = calldata[metadataStart : metadataStart+metadataLength.Int64()]
}
// arbitrary metadata that will be most likely be used by the relayer
var meta types.Metadata

payload := []interface{}{
tokenId,
recipientAddress,
metadata,
}

if 64+recipientAddressLength.Int64()+32+metadataLength.Int64() < int64(len(calldata)) {
// (metadataStart + metadataLength) - (metadataStart + metadataLength + 1) is priority length
priorityLength := big.NewInt(0).SetBytes(calldata[(64 + recipientAddressLength.Int64() + 32 + metadataLength.Int64()):(64 + recipientAddressLength.Int64() + 32 + metadataLength.Int64() + 1)])
// (metadataStart + metadataLength + 1) - (metadataStart + metadataLength + 1) + priority length) is priority data
priority := calldata[(64 + recipientAddressLength.Int64() + 32 + metadataLength.Int64() + 1):(64 + recipientAddressLength.Int64() + 32 + metadataLength.Int64() + 1 + priorityLength.Int64())]

// Assign the priority data to the Metadata struct
meta.Data = make(map[string]interface{})
meta.Data["Priority"] = priority[0]
}
return types.NewMessage(sourceID, destId, nonce, resourceID, types.NonFungibleTransfer, payload, meta), nil
}
Loading
Loading