github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/e2e/datacommittee_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"encoding/json"
     7  	"fmt"
     8  	"math/big"
     9  	"os"
    10  	"os/exec"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/0xPolygon/supernets2-data-availability/config"
    18  	cTypes "github.com/0xPolygon/supernets2-node/config/types"
    19  	"github.com/0xPolygon/supernets2-node/db"
    20  	"github.com/0xPolygon/supernets2-node/etherman/smartcontracts/supernets2datacommittee"
    21  	"github.com/0xPolygon/supernets2-node/jsonrpc"
    22  	"github.com/0xPolygon/supernets2-node/log"
    23  	"github.com/0xPolygon/supernets2-node/test/operations"
    24  	"github.com/ethereum/go-ethereum"
    25  	eTypes "github.com/ethereum/go-ethereum/core/types"
    26  
    27  	"github.com/ethereum/go-ethereum/accounts/keystore"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/ethclient"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestDataCommittee(t *testing.T) {
    36  	const (
    37  		nSignatures      = 4
    38  		mMembers         = 5
    39  		ksFile           = "/tmp/pkey"
    40  		cfgFile          = "/tmp/dacnodeconfigfile.json"
    41  		ksPass           = "pass"
    42  		dacNodeContainer = "hermeznetwork/supernets2-data-availability:v0.0.1"
    43  	)
    44  
    45  	// Setup
    46  	var err error
    47  	if testing.Short() {
    48  		t.Skip()
    49  	}
    50  	ctx := context.Background()
    51  	defer func() {
    52  		require.NoError(t, operations.Teardown())
    53  	}()
    54  	err = operations.Teardown()
    55  	require.NoError(t, err)
    56  	opsCfg := operations.GetDefaultOperationsConfig()
    57  	opsCfg.State.MaxCumulativeGasUsed = 80000000000
    58  	opsman, err := operations.NewManager(ctx, opsCfg)
    59  	require.NoError(t, err)
    60  	defer func() {
    61  		require.NoError(t, opsman.StopDACDB())
    62  	}()
    63  	err = opsman.Setup()
    64  	require.NoError(t, err)
    65  	require.NoError(t, opsman.StartDACDB())
    66  	time.Sleep(5 * time.Second)
    67  	authL2, err := operations.GetAuth(operations.DefaultSequencerPrivateKey, operations.DefaultL2ChainID)
    68  	require.NoError(t, err)
    69  	authL1, err := operations.GetAuth(operations.DefaultSequencerPrivateKey, operations.DefaultL1ChainID)
    70  	require.NoError(t, err)
    71  	clientL2, err := ethclient.Dial(operations.DefaultL2NetworkURL)
    72  	require.NoError(t, err)
    73  	clientL1, err := ethclient.Dial(operations.DefaultL1NetworkURL)
    74  	require.NoError(t, err)
    75  	dacSC, err := supernets2datacommittee.NewSupernets2datacommittee(
    76  		common.HexToAddress(operations.DefaultL1DataCommitteeContract),
    77  		clientL1,
    78  	)
    79  	require.NoError(t, err)
    80  
    81  	// Register committe with N / M signatures
    82  	membs := members{}
    83  	addrsBytes := []byte{}
    84  	urls := []string{}
    85  	for i := 0; i < mMembers; i++ {
    86  		pk, err := crypto.GenerateKey()
    87  		require.NoError(t, err)
    88  		membs = append(membs, member{
    89  			addr: crypto.PubkeyToAddress(pk.PublicKey),
    90  			pk:   pk,
    91  			url:  fmt.Sprintf("http://supernets2-data-availability-%d:420%d", i, i),
    92  			i:    i,
    93  		})
    94  	}
    95  	sort.Sort(membs)
    96  	for _, m := range membs {
    97  		addrsBytes = append(addrsBytes, m.addr.Bytes()...)
    98  		urls = append(urls, m.url)
    99  	}
   100  	tx, err := dacSC.SetupCommittee(authL1, big.NewInt(nSignatures), urls, addrsBytes)
   101  	if err != nil {
   102  		for _, m := range membs {
   103  			fmt.Println(m.addr)
   104  		}
   105  	}
   106  	require.NoError(t, err)
   107  	err = operations.WaitTxToBeMined(ctx, clientL1, tx, operations.DefaultTimeoutTxToBeMined)
   108  	require.NoError(t, err)
   109  
   110  	// Spin up M DAC nodes
   111  	dacNodeConfig := config.Config{
   112  		L1: config.L1Config{
   113  			WsURL:       "ws://supernets2-mock-l1-network:8546",
   114  			Contract:    "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
   115  			Timeout:     cTypes.NewDuration(time.Minute * 3),
   116  			RetryPeriod: cTypes.NewDuration(time.Second * 5),
   117  		},
   118  		PrivateKey: cTypes.KeystoreFileConfig{
   119  			Path:     ksFile,
   120  			Password: ksPass,
   121  		},
   122  		DB: db.Config{
   123  			Name:      "committee_db",
   124  			User:      "committee_user",
   125  			Password:  "committee_password",
   126  			Host:      "supernets2-data-node-db",
   127  			Port:      "5432",
   128  			EnableLog: false,
   129  			MaxConns:  10,
   130  		},
   131  		RPC: jsonrpc.Config{
   132  			Host:                             "0.0.0.0",
   133  			EnableL2SuggestedGasPricePolling: false,
   134  			MaxRequestsPerIPAndSecond:        100,
   135  		},
   136  	}
   137  	defer func() {
   138  		// Remove tmp files
   139  		assert.NoError(t,
   140  			exec.Command("rm", cfgFile).Run(),
   141  		)
   142  		assert.NoError(t,
   143  			exec.Command("rmdir", ksFile+"_").Run(),
   144  		)
   145  		assert.NoError(t,
   146  			exec.Command("rm", ksFile).Run(),
   147  		)
   148  		// Stop DAC nodes
   149  		for i := 0; i < mMembers; i++ {
   150  			assert.NoError(t, exec.Command(
   151  				"docker", "kill", "supernets2-data-availability-"+strconv.Itoa(i),
   152  			).Run())
   153  			assert.NoError(t, exec.Command(
   154  				"docker", "rm", "supernets2-data-availability-"+strconv.Itoa(i),
   155  			).Run())
   156  		}
   157  		// Stop permissionless node
   158  		require.NoError(t, opsman.StopPermissionlessNodeForcedToSYncThroughDAC())
   159  	}()
   160  	// Start permissionless node
   161  	require.NoError(t, opsman.StartPermissionlessNodeForcedToSYncThroughDAC())
   162  	// Star DAC nodes
   163  	for _, m := range membs {
   164  		// Set correct port
   165  		port := 4200 + m.i
   166  		dacNodeConfig.RPC.Port = port
   167  		// Write config file
   168  		file, err := json.MarshalIndent(dacNodeConfig, "", " ")
   169  		require.NoError(t, err)
   170  		err = os.WriteFile(cfgFile, file, 0644)
   171  		require.NoError(t, err)
   172  		// Write private key keystore file
   173  		require.NoError(t, createKeyStore(m.pk, ksFile, ksPass))
   174  		// Run DAC node
   175  		cmd := exec.Command(
   176  			"docker", "run", "-d",
   177  			"--name", "supernets2-data-availability-"+strconv.Itoa(m.i),
   178  			"-v", cfgFile+":/app/config.json",
   179  			"-v", ksFile+":"+ksFile,
   180  			"--network", "supernets2",
   181  			dacNodeContainer,
   182  			"/bin/sh", "-c",
   183  			"/app/supernets2-data-availability run --cfg /app/config.json",
   184  		)
   185  		out, err := cmd.CombinedOutput()
   186  		require.NoError(t, err, string(out))
   187  		time.Sleep(time.Second * 5)
   188  	}
   189  
   190  	// Send txs
   191  	nTxs := 10
   192  	amount := big.NewInt(10000)
   193  	toAddress := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
   194  	_, err = clientL2.BalanceAt(ctx, authL2.From, nil)
   195  	require.NoError(t, err)
   196  	_, err = clientL2.PendingNonceAt(ctx, authL2.From)
   197  	require.NoError(t, err)
   198  
   199  	gasLimit, err := clientL2.EstimateGas(ctx, ethereum.CallMsg{From: authL2.From, To: &toAddress, Value: amount})
   200  	require.NoError(t, err)
   201  
   202  	gasPrice, err := clientL2.SuggestGasPrice(ctx)
   203  	require.NoError(t, err)
   204  
   205  	nonce, err := clientL2.PendingNonceAt(ctx, authL2.From)
   206  	require.NoError(t, err)
   207  
   208  	txs := make([]*eTypes.Transaction, 0, nTxs)
   209  	for i := 0; i < nTxs; i++ {
   210  		tx := eTypes.NewTransaction(nonce+uint64(i), toAddress, amount, gasLimit, gasPrice, nil)
   211  		log.Infof("generating tx %d / %d: %s", i+1, nTxs, tx.Hash().Hex())
   212  		txs = append(txs, tx)
   213  	}
   214  
   215  	// Wait for verification
   216  	_, err = operations.ApplyL2Txs(ctx, txs, authL2, clientL2, operations.VerifiedConfirmationLevel)
   217  	require.NoError(t, err)
   218  
   219  	// Assert that he permissionless node is fully synced (through the DAC)
   220  	time.Sleep(30 * time.Second) // Give some time for the permissionless node to get synced
   221  	clientL2Permissionless, err := ethclient.Dial(operations.PermissionlessL2NetworkURL)
   222  	require.NoError(t, err)
   223  	expectedBlock, err := clientL2.BlockByNumber(ctx, nil)
   224  	require.NoError(t, err)
   225  	actualBlock, err := clientL2Permissionless.BlockByNumber(ctx, nil)
   226  	require.NoError(t, err)
   227  	// je, err := expectedBlock.Header().MarshalJSON()
   228  	// require.NoError(t, err)
   229  	// log.Info(string(je))
   230  	// ja, err := actualBlock.Header().MarshalJSON()
   231  	// require.NoError(t, err)
   232  	// log.Info(string(ja))
   233  	// require.Equal(t, string(je), string(ja))
   234  	require.Equal(t, expectedBlock.Root().Hex(), actualBlock.Root().Hex())
   235  }
   236  
   237  type member struct {
   238  	addr common.Address
   239  	pk   *ecdsa.PrivateKey
   240  	url  string
   241  	i    int
   242  }
   243  type members []member
   244  
   245  func (s members) Len() int { return len(s) }
   246  func (s members) Less(i, j int) bool {
   247  	return strings.ToUpper(s[i].addr.Hex()) < strings.ToUpper(s[j].addr.Hex())
   248  }
   249  func (s members) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   250  
   251  func createKeyStore(pk *ecdsa.PrivateKey, outputDir, password string) error {
   252  	ks := keystore.NewKeyStore(outputDir+"_", keystore.StandardScryptN, keystore.StandardScryptP)
   253  	_, err := ks.ImportECDSA(pk, password)
   254  	if err != nil {
   255  		return err
   256  	}
   257  	fileNameB, err := exec.Command("ls", outputDir+"_/").CombinedOutput()
   258  	fileName := strings.TrimSuffix(string(fileNameB), "\n")
   259  	if err != nil {
   260  		fmt.Println(fileName)
   261  		return err
   262  	}
   263  	out, err := exec.Command("mv", outputDir+"_/"+fileName, outputDir).CombinedOutput()
   264  	if err != nil {
   265  		fmt.Println(string(out))
   266  		return err
   267  	}
   268  	return nil
   269  }