Skip to content

Commit

Permalink
Consolidate deposit and withdrawal erc20 with multiple commitments pe…
Browse files Browse the repository at this point in the history
…r epoch tests
  • Loading branch information
Stefan-Ethernal committed Apr 6, 2023
1 parent c56c49c commit 86027f9
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 102 deletions.
1 change: 1 addition & 0 deletions consensus/polybft/contractsapi/bindings-gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func main() {
"RootERC20",
gensc.RootERC20,
[]string{
"balanceOf",
"approve",
"mint",
},
Expand Down
16 changes: 16 additions & 0 deletions consensus/polybft/contractsapi/contractsapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions consensus/polybft/contractsapi/gen_sc_data.go

Large diffs are not rendered by default.

276 changes: 178 additions & 98 deletions e2e-polybft/e2e/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ const (

func TestE2E_Bridge_DepositAndWithdrawERC20(t *testing.T) {
const (
num = 10
transfersCount = 10
amount = 100
numBlockConfirmations = 2
// make epoch size long enough, so that all exit events are processed within the same epoch
epochSize = 30
)

receivers := make([]string, num)
amounts := make([]string, num)
receivers := make([]string, transfersCount)
amounts := make([]string, transfersCount)

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

Expand All @@ -63,136 +63,216 @@ func TestE2E_Bridge_DepositAndWithdrawERC20(t *testing.T) {
manifest, err := polybft.LoadManifest(path.Join(cluster.Config.TmpDir, manifestFileName))
require.NoError(t, err)

// DEPOSIT ERC20 TOKENS
// send a few transactions to the bridge
require.NoError(
t,
cluster.Bridge.DepositERC20(
manifest.RootchainConfig.RootNativeERC20Address,
manifest.RootchainConfig.RootERC20PredicateAddress,
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
),
)
validatorSrv := cluster.Servers[0]
childEthEndpoint := validatorSrv.JSONRPC().Eth()

t.Run("bridge ERC 20 tokens", func(t *testing.T) {
t.Logf("bridge ERC 20 tokens started...\n")
// DEPOSIT ERC20 TOKENS
// send a few transactions to the bridge
require.NoError(
t,
cluster.Bridge.DepositERC20(
manifest.RootchainConfig.RootNativeERC20Address,
manifest.RootchainConfig.RootERC20PredicateAddress,
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
),
)

// wait for a few more sprints
require.NoError(t, cluster.WaitForBlock(35, 2*time.Minute))

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

id := stateSyncedResult.Sig()
filter := &ethgo.LogFilter{
Topics: [][]*ethgo.Hash{
{&id},
},
}

// wait for a few more sprints
require.NoError(t, cluster.WaitForBlock(35, 2*time.Minute))
filter.SetFromUint64(0)
filter.SetToUint64(100)

// the transactions are processed and there should be a success events
var stateSyncedResult contractsapi.StateSyncResultEvent
logs, err := childEthEndpoint.GetLogs(filter)
require.NoError(t, err)

id := stateSyncedResult.Sig()
filter := &ethgo.LogFilter{
Topics: [][]*ethgo.Hash{
{&id},
},
}
// assert that all deposits are executed successfully
checkStateSyncResultLogs(t, logs, transfersCount)

filter.SetFromUint64(0)
filter.SetToUint64(100)
// check receivers balances got increased by deposited amount
for _, receiver := range receivers {
balance, err := childEthEndpoint.GetBalance(ethgo.Address(types.StringToAddress(receiver)), ethgo.Latest)
require.NoError(t, err)
require.Equal(t, big.NewInt(amount), balance)
}

childEthEndpoint := cluster.Servers[0].JSONRPC().Eth()
t.Log("Deposits were successfully processed")

logs, err := childEthEndpoint.GetLogs(filter)
require.NoError(t, err)
// WITHDRAW ERC20 TOKENS
rootchainTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(cluster.Bridge.JSONRPCAddr()))
require.NoError(t, err)

senderAccount, err := sidechain.GetAccountFromDir(validatorSrv.DataDir())
require.NoError(t, err)

// assert that all deposits are executed successfully
checkStateSyncResultLogs(t, logs, num)
t.Logf("Withdraw sender: %s\n", senderAccount.Ecdsa.Address())

// check receivers balances got increased by deposited amount
for _, receiver := range receivers {
balance, err := childEthEndpoint.GetBalance(ethgo.Address(types.StringToAddress(receiver)), ethgo.Latest)
rawKey, err := senderAccount.Ecdsa.MarshallPrivateKey()
require.NoError(t, err)
require.Equal(t, big.NewInt(amount), balance)
}

t.Log("Deposits were successfully processed")
// send withdraw transaction
err = cluster.Bridge.WithdrawERC20(
hex.EncodeToString(rawKey),
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
validatorSrv.JSONRPCAddr())
require.NoError(t, err)

// WITHDRAW ERC20 TOKENS
rootchainTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(cluster.Bridge.JSONRPCAddr()))
require.NoError(t, err)
currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false)
require.NoError(t, err)

senderAccount, err := sidechain.GetAccountFromDir(cluster.Servers[0].DataDir())
require.NoError(t, err)
currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData)
require.NoError(t, err)

t.Logf("Withdraw sender: %s\n", senderAccount.Ecdsa.Address())
t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber)

rawKey, err := senderAccount.Ecdsa.MarshallPrivateKey()
require.NoError(t, err)
currentEpoch := currentExtra.Checkpoint.EpochNumber

// send withdraw transaction
err = cluster.Bridge.WithdrawERC20(
hex.EncodeToString(rawKey),
strings.Join(receivers[:], ","),
strings.Join(amounts[:], ","),
cluster.Servers[0].JSONRPCAddr())
require.NoError(t, err)
require.NoError(t, waitForRootchainEpoch(currentEpoch, 3*time.Minute,
rootchainTxRelayer, manifest.RootchainConfig.CheckpointManagerAddress))

currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false)
require.NoError(t, err)
exitHelper := manifest.RootchainConfig.ExitHelperAddress
rootJSONRPC := cluster.Bridge.JSONRPCAddr()
childJSONRPC := validatorSrv.JSONRPCAddr()

currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData)
require.NoError(t, err)
for i := uint64(0); i < transfersCount; i++ {
exitEventID := i + 1

t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber)
// send exit transaction to exit helper
err = cluster.Bridge.SendExitTransaction(exitHelper, exitEventID, rootJSONRPC, childJSONRPC)
require.NoError(t, err)

currentEpoch := currentExtra.Checkpoint.EpochNumber
fail := 0
// make sure exit event is processed successfully
isProcessed, err := isExitEventProcessed(exitEventID, ethgo.Address(exitHelper), rootchainTxRelayer)
require.NoError(t, err)
require.True(t, isProcessed, fmt.Sprintf("exit event with ID %d was not processed", exitEventID))
}

// make sure we have progressed to the next epoch on the root chain
// before sending exit transaction to the root chain
// (it means that checkpoint was submitted)
for range time.Tick(time.Second) {
currentEpochString, err := ABICall(rootchainTxRelayer, contractsapi.CheckpointManager,
ethgo.Address(manifest.RootchainConfig.CheckpointManagerAddress), ethgo.ZeroAddress, "currentEpoch")
require.NoError(t, err)
// assert that receiver's balances on RootERC20 smart contract are expected
for _, receiver := range receivers {
balanceOfFn := &contractsapi.BalanceOfRootERC20Fn{Account: types.StringToAddress(receiver)}
balanceInput, err := balanceOfFn.EncodeAbi()
require.NoError(t, err)

rootchainEpoch, err := types.ParseUint64orHex(&currentEpochString)
require.NoError(t, err)
balanceRaw, err := rootchainTxRelayer.Call(ethgo.ZeroAddress,
ethgo.Address(manifest.RootchainConfig.RootNativeERC20Address), balanceInput)
require.NoError(t, err)

if rootchainEpoch >= currentEpoch {
break
balance, err := types.ParseUint256orHex(&balanceRaw)
require.NoError(t, err)
require.Equal(t, big.NewInt(amount), balance)
}

if fail > 300 {
t.Fatal("root chain hasn't progressed to the next epoch")
}
fail++
}
t.Logf("bridge ERC 20 tokens finished.\n")
})

t.Run("multiple deposit batches per epoch", func(t *testing.T) {
const (
depositsSubset = 2
sprintSize = 5
)

initialBlockNum, err := childEthEndpoint.BlockNumber()
require.NoError(t, err)

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

t.Logf("multiple deposit batches started %d...\n", initialBlockNum)

exitHelper := manifest.RootchainConfig.ExitHelperAddress
rootJSONRPC := cluster.Bridge.JSONRPCAddr()
childJSONRPC := cluster.Servers[0].JSONRPCAddr()
// send two transactions to the bridge so that we have a minimal commitment
require.NoError(
t,
cluster.Bridge.DepositERC20(
manifest.RootchainConfig.RootNativeERC20Address,
manifest.RootchainConfig.RootERC20PredicateAddress,
strings.Join(receivers[:depositsSubset], ","),
strings.Join(amounts[:depositsSubset], ","),
),
)

for i := uint64(0); i < num; i++ {
exitEventID := i + 1
// wait for a few more sprints
midBlockNumber := initialBlockNum + 2*sprintSize
require.NoError(t, cluster.WaitForBlock(midBlockNumber, 2*time.Minute))

// send exit transaction to exit helper
err = cluster.Bridge.SendExitTransaction(exitHelper, exitEventID, rootJSONRPC, childJSONRPC)
txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithClient(validatorSrv.JSONRPC()))
require.NoError(t, err)

// make sure exit event is processed successfully
isProcessed, err := isExitEventProcessed(exitEventID, ethgo.Address(exitHelper), rootchainTxRelayer)
lastCommittedIDMethod := contractsapi.StateReceiver.Abi.GetMethod("lastCommittedId")
encode, err := lastCommittedIDMethod.Encode([]interface{}{})
require.NoError(t, err)
require.True(t, isProcessed, fmt.Sprintf("exit event with ID %d was not processed", exitEventID))
}

// assert that receiver's balances on RootERC20 smart contract are expected
for _, receiver := range receivers {
balanceInput, err := contractsapi.RootERC20.Abi.Methods["balanceOf"].Encode([]interface{}{receiver})
// check that we submitted the minimal commitment to smart contract
commitmentIDRaw, err := txRelayer.Call(ethgo.ZeroAddress, ethgo.Address(contracts.StateReceiverContract), encode)
require.NoError(t, err)

balanceRaw, err := rootchainTxRelayer.Call(ethgo.ZeroAddress,
ethgo.Address(manifest.RootchainConfig.RootNativeERC20Address), balanceInput)
lastCommittedID, err := types.ParseUint64orHex(&commitmentIDRaw)
require.NoError(t, err)
require.Equal(t, uint64(transfersCount+depositsSubset), lastCommittedID)

// send some more transactions to the bridge to build another commitment in epoch
require.NoError(
t,
cluster.Bridge.DepositERC20(
manifest.RootchainConfig.RootNativeERC20Address,
manifest.RootchainConfig.RootERC20PredicateAddress,
strings.Join(receivers[depositsSubset:], ","),
strings.Join(amounts[depositsSubset:], ","),
),
)

// wait for a few more sprints
require.NoError(t, cluster.WaitForBlock(midBlockNumber+epochSize, 3*time.Minute))

// check that we submitted the minimal commitment to smart contract
commitmentIDRaw, err = txRelayer.Call(ethgo.ZeroAddress, ethgo.Address(contracts.StateReceiverContract), encode)
require.NoError(t, err)

balance, err := types.ParseUint256orHex(&balanceRaw)
// check that the second (larger commitment) was also submitted in epoch
lastCommittedID, err = types.ParseUint64orHex(&commitmentIDRaw)
require.NoError(t, err)
require.Equal(t, big.NewInt(amount), balance)
}
require.Equal(t, uint64(2*transfersCount), lastCommittedID)

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

id := stateSyncedResult.Sig()
filter := &ethgo.LogFilter{
Topics: [][]*ethgo.Hash{
{&id},
},
}

filter.SetFromUint64(initialBlockNum)
filter.SetToUint64(initialBlockNum + 2*epochSize)

logs, err := childEthEndpoint.GetLogs(filter)
require.NoError(t, err)

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

t.Logf("multiple deposit batches finished.\n")
})
}

func TestE2E_Bridge_MultipleCommitmentsPerEpoch(t *testing.T) {
t.Skip("REMOVE")

const depositsCount = 10

receivers := make([]string, depositsCount)
Expand Down Expand Up @@ -487,10 +567,10 @@ func TestE2E_Bridge_L2toL1ExitMultiple(t *testing.T) {
}

for i := uint64(1); i <= roundNumber; i++ {
for j := 0; j < userNumber; j++ {
go addTransaction(uint64(j), uint64(i))
for j := uint64(0); j < userNumber; j++ {
go addTransaction(j, i)
}
require.NoError(t, cluster.WaitForBlock(uint64((i)*epochSize), 2*time.Minute))
require.NoError(t, cluster.WaitForBlock(i*epochSize, 2*time.Minute))
require.NoError(t, waitForRootchainEpoch(i*checkpointEpoch, 5*time.Minute, l1TxRelayer, types.Address(checkpointManagerAddr)))
}

Expand Down

0 comments on commit 86027f9

Please sign in to comment.