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

Implement non-native tokens bridge e2e test #1746

Merged
247 changes: 184 additions & 63 deletions e2e-polybft/e2e/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,20 @@ func TestE2E_Bridge_Transfers(t *testing.T) {
sprintSize = uint64(5)
)

receiversAddrs := make([]types.Address, transfersCount)
receivers := make([]string, transfersCount)
amounts := make([]string, transfersCount)
receiverKeys := make([]string, transfersCount)

for i := 0; i < transfersCount; i++ {
key, err := ethgow.GenerateKey()
require.NoError(t, err)

rawKey, err := key.MarshallPrivateKey()
require.NoError(t, err)

receiverKeys[i] = hex.EncodeToString(rawKey)
receiversAddrs[i] = types.Address(key.Address())
receivers[i] = types.Address(key.Address()).String()
amounts[i] = fmt.Sprintf("%d", amount)

Expand All @@ -73,18 +80,17 @@ func TestE2E_Bridge_Transfers(t *testing.T) {
// bridge some tokens for first validator to child chain
tokensToDeposit := ethgo.Ether(10)

require.NoError(
t, cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
senderAccount.Address().String(),
tokensToDeposit.String(),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
require.NoError(t, cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
senderAccount.Address().String(),
tokensToDeposit.String(),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
)

// wait for a couple of sprints
Expand All @@ -108,28 +114,24 @@ func TestE2E_Bridge_Transfers(t *testing.T) {
t.Run("bridge ERC 20 tokens", func(t *testing.T) {
// DEPOSIT ERC20 TOKENS
// send a few transactions to the bridge
require.NoError(
t,
cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
)
require.NoError(t, cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false,
))

finalBlockNum := 10 * sprintSize
// wait for a couple of sprints
require.NoError(t, cluster.WaitForBlock(finalBlockNum, 2*time.Minute))

// the transactions are processed and there should be a success events
var stateSyncedResult contractsapi.StateSyncResultEvent

logs, err := getFilteredLogs(stateSyncedResult.Sig(), 0, finalBlockNum, childEthEndpoint)
require.NoError(t, err)

Expand Down Expand Up @@ -228,19 +230,17 @@ func TestE2E_Bridge_Transfers(t *testing.T) {
require.NoError(t, cluster.WaitForBlock(initialBlockNum, 1*time.Minute))

// send two transactions to the bridge so that we have a minimal commitment
require.NoError(
t,
cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[:depositsSubset], ","),
strings.Join(amounts[:depositsSubset], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
require.NoError(t, cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[:depositsSubset], ","),
strings.Join(amounts[:depositsSubset], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
)

// wait for a few more sprints
Expand All @@ -257,19 +257,17 @@ func TestE2E_Bridge_Transfers(t *testing.T) {
require.Equal(t, initialCommittedID+depositsSubset, lastCommittedID)

// send some more transactions to the bridge to build another commitment in epoch
require.NoError(
t,
cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[depositsSubset:], ","),
strings.Join(amounts[depositsSubset:], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
require.NoError(t, cluster.Bridge.Deposit(
common.ERC20,
polybftCfg.Bridge.RootNativeERC20Addr,
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[depositsSubset:], ","),
strings.Join(amounts[depositsSubset:], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false),
)

finalBlockNum := midBlockNumber + 5*sprintSize
Expand All @@ -287,14 +285,142 @@ func TestE2E_Bridge_Transfers(t *testing.T) {

// the transactions are mined and state syncs should be executed by the relayer
// and there should be a success events
var stateSyncedResult contractsapi.StateSyncResultEvent

logs, err := getFilteredLogs(stateSyncedResult.Sig(), initialBlockNum, finalBlockNum, childEthEndpoint)
require.NoError(t, err)

// assert that all state syncs are executed successfully
checkStateSyncResultLogs(t, logs, transfersCount)
})

t.Run("non native ERC20 deposit and withdraw", func(t *testing.T) {
rootchainTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(cluster.Bridge.JSONRPCAddr()))
require.NoError(t, err)

childchainTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithClient(validatorSrv.JSONRPC()))
require.NoError(t, err)

// Deploy ERC20 contract to the rootchain
rootchainDeployer, err := rootHelper.DecodePrivateKey("")
require.NoError(t, err)

txn := &ethgo.Transaction{To: nil, Input: contractsapi.RootERC20.Bytecode}
receipt, err := rootchainTxRelayer.SendTransaction(txn, rootchainDeployer)
require.NoError(t, err)
begmaroman marked this conversation as resolved.
Show resolved Hide resolved
require.NotNil(t, receipt)
require.Equal(t, uint64(types.ReceiptSuccess), receipt.Status)

rootTokenAddr := receipt.ContractAddress

t.Log("Rootchain token address:", rootTokenAddr)

// wait for next sprint block as the starting point,
// in order to be able to make assertions against blocks offsetted by sprints
initialBlockNum, err := childEthEndpoint.BlockNumber()
require.NoError(t, err)

initialBlockNum = initialBlockNum + sprintSize - (initialBlockNum % sprintSize)
require.NoError(t, cluster.WaitForBlock(initialBlockNum, 1*time.Minute))

t.Log("Initial block number:", initialBlockNum)

// send a few transactions to the bridge
require.NoError(t, cluster.Bridge.Deposit(
common.ERC20,
types.Address(rootTokenAddr),
polybftCfg.Bridge.RootERC20PredicateAddr,
rootHelper.TestAccountPrivKey,
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
"",
cluster.Bridge.JSONRPCAddr(),
rootHelper.TestAccountPrivKey,
false,
))

// wait for a few more sprints
finalBlockNumber := initialBlockNum + 10*sprintSize
require.NoError(t, cluster.WaitForBlock(finalBlockNumber, 2*time.Minute))

t.Log("Final block number:", finalBlockNumber)

// the transactions are processed and there should be a success events
logs, err := getFilteredLogs(stateSyncedResult.Sig(), initialBlockNum, finalBlockNumber, childEthEndpoint)
require.NoError(t, err)

// assert that all deposits are executed successfully
// map token action is executed along with the first deposit transaction
checkStateSyncResultLogs(t, logs, transfersCount+1)

// retrieve child token address
childTokenAddr := getChildToken(
t,
contractsapi.ChildERC20Predicate.Abi,
contracts.ChildERC20PredicateContract,
types.Address(rootTokenAddr),
childchainTxRelayer,
)

t.Log("Childchain token address:", childTokenAddr)

// check receivers balances got increased by deposited amount
for _, receiver := range receiversAddrs {
balance := erc20BalanceOf(t, receiver, childTokenAddr, childchainTxRelayer)
require.Equal(t, big.NewInt(int64(amount)), balance)
}

t.Log("Deposits were successfully processed")

// get initial exit id
initialExitEventID := getLastExitEventID(t, childchainTxRelayer)

// WITHDRAW ERC20 TOKENS
for i, receiverKey := range receiverKeys {
t.Logf("Withdraw to: %s\n", receivers[i])

require.NoError(t, cluster.Bridge.Withdraw(
common.ERC20,
receiverKey,
receivers[i],
amounts[i],
"",
validatorSrv.JSONRPCAddr(),
contracts.ChildERC20PredicateContract,
childTokenAddr,
false,
))
}

currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false)
require.NoError(t, err)

currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData)
require.NoError(t, err)

t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber)

require.NoError(t, waitForRootchainEpoch(
currentExtra.Checkpoint.EpochNumber+1,
3*time.Minute,
rootchainTxRelayer,
polybftCfg.Bridge.CheckpointManagerAddr,
))

exitHelper := polybftCfg.Bridge.ExitHelperAddr
childJSONRPC := validatorSrv.JSONRPCAddr()

initialExitEventID++
for i := initialExitEventID; i < initialExitEventID+transfersCount; i++ {
// send exit transaction to exit helper
err = cluster.Bridge.SendExitTransaction(exitHelper, i, childJSONRPC)
require.NoError(t, err)
}

// assert that receiver's balances on RootERC20 smart contract are expected
for _, receiver := range receivers {
balance := erc20BalanceOf(t, types.StringToAddress(receiver), types.Address(rootTokenAddr), rootchainTxRelayer)
require.Equal(t, big.NewInt(amount), balance)
}
})
}

func TestE2E_Bridge_ERC721Transfer(t *testing.T) {
Expand Down Expand Up @@ -817,13 +943,8 @@ func TestE2E_Bridge_ChildChainMintableTokensTransfer(t *testing.T) {
rootchainInitialBlock, err := rootchainTxRelayer.Client().Eth().BlockNumber()
require.NoError(t, err)

exitEventsCounterFn := contractsapi.L2StateSender.Abi.Methods["counter"]
input, err := exitEventsCounterFn.Encode([]interface{}{})
require.NoError(t, err)
initialExitEventIDRaw, err := childchainTxRelayer.Call(ethgo.ZeroAddress, ethgo.Address(contracts.L2StateSenderContract), input)
require.NoError(t, err)
initialExitEventID, err := types.ParseUint64orHex(&initialExitEventIDRaw)
require.NoError(t, err)
// get initial exit id
initialExitEventID := getLastExitEventID(t, childchainTxRelayer)

erc721DeployTxn := cluster.Deploy(t, admin, contractsapi.RootERC721.Bytecode)
require.NoError(t, erc721DeployTxn.Wait())
Expand Down
37 changes: 18 additions & 19 deletions e2e-polybft/e2e/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/umbracle/ethgo"
"github.com/umbracle/ethgo/abi"
"github.com/umbracle/ethgo/contract"
"github.com/umbracle/ethgo/jsonrpc"

"github.com/0xPolygon/polygon-edge/consensus/polybft"
Expand All @@ -31,23 +30,6 @@ import (

const nativeTokenMintableTestCfg = "Mintable Edge Coin:MEC:18:true:%s"

type e2eStateProvider struct {
txRelayer txrelayer.TxRelayer
}

func (s *e2eStateProvider) Call(contractAddr ethgo.Address, input []byte, opts *contract.CallOpts) ([]byte, error) {
response, err := s.txRelayer.Call(ethgo.Address(types.ZeroAddress), contractAddr, input)
if err != nil {
return nil, err
}

return hex.DecodeHex(response)
}

func (s *e2eStateProvider) Txn(ethgo.Address, ethgo.Key, []byte) (contract.Txn, error) {
return nil, errors.New("send txn is not supported")
}

// getCheckpointManagerValidators queries rootchain validator set on CheckpointManager contract
func getCheckpointManagerValidators(relayer txrelayer.TxRelayer, checkpointManagerAddr ethgo.Address) ([]*polybft.ValidatorInfo, error) {
validatorsCountRaw, err := ABICall(relayer, contractsapi.CheckpointManager,
Expand Down Expand Up @@ -217,7 +199,7 @@ func waitForRootchainEpoch(targetEpoch uint64, timeout time.Duration,
return err
}

rootchainEpoch, err := types.ParseUint64orHex(&rootchainEpochRaw)
rootchainEpoch, err := common.ParseUint64orHex(&rootchainEpochRaw)
if err != nil {
return err
}
Expand Down Expand Up @@ -348,3 +330,20 @@ func getChildToken(t *testing.T, predicateABI *abi.ABI, predicateAddr types.Addr

return types.StringToAddress(childTokenRaw)
}

func getLastExitEventID(t *testing.T, relayer txrelayer.TxRelayer) uint64 {
t.Helper()

exitEventsCounterFn := contractsapi.L2StateSender.Abi.Methods["counter"]

input, err := exitEventsCounterFn.Encode([]interface{}{})
require.NoError(t, err)

exitEventIDRaw, err := relayer.Call(ethgo.ZeroAddress, ethgo.Address(contracts.L2StateSenderContract), input)
require.NoError(t, err)

exitEventID, err := common.ParseUint64orHex(&exitEventIDRaw)
require.NoError(t, err)

return exitEventID
}
Loading