From 301bc8859d2e1f7ca5a1260ddabe8ce4a6e0566c Mon Sep 17 00:00:00 2001 From: prathamesh0 Date: Wed, 1 Jun 2022 16:36:46 +0530 Subject: [PATCH 1/3] Track validation progress on a channel --- cmd/state_validator.go | 2 +- pkg/validator/validator.go | 13 +++++++++++-- test/integration_test.go | 34 ++++++++++++++++++++++------------ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/cmd/state_validator.go b/cmd/state_validator.go index 5c734c7..ded52df 100644 --- a/cmd/state_validator.go +++ b/cmd/state_validator.go @@ -47,7 +47,7 @@ func stateValidator() { logWithCommand.Fatal(err) } - service := validator.NewService(cfg.DB, height, trail, sleepInterval, chainCfg) + service := validator.NewService(cfg.DB, height, trail, sleepInterval, chainCfg, nil) wg := new(sync.WaitGroup) wg.Add(1) diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 6c9d159..b31d88f 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -37,9 +37,10 @@ type service struct { logger *log.Logger chainCfg *params.ChainConfig quitChan chan bool + progressChan chan uint64 } -func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chainCfg *params.ChainConfig) *service { +func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chainCfg *params.ChainConfig, progressChan chan uint64) *service { return &service{ db: db, blockNum: blockNum, @@ -48,6 +49,7 @@ func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chai logger: log.New(), chainCfg: chainCfg, quitChan: make(chan bool), + progressChan: progressChan, } } @@ -96,7 +98,6 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) { select { case <-s.quitChan: s.logger.Infof("last validated block %v", idxBlockNum-1) - s.logger.Info("stopping ipld-eth-db-validator process") return default: idxBlockNum, err = s.Validate(ctx, api, idxBlockNum) @@ -111,6 +112,10 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) { // Stop is used to gracefully stop the service func (s *service) Stop() { + s.logger.Info("stopping ipld-eth-db-validator process") + if s.progressChan != nil { + close(s.progressChan) + } close(s.quitChan) } @@ -129,6 +134,10 @@ func (s *service) Validate(ctx context.Context, api *ipldEth.PublicEthAPI, idxBl } s.logger.Infof("state root verified for block %d", idxBlockNum) + if s.progressChan != nil { + s.progressChan <- idxBlockNum + } + idxBlockNum++ } else { // Sleep / wait for head to move ahead diff --git a/test/integration_test.go b/test/integration_test.go index ed76a77..ec0c1ba 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -2,6 +2,7 @@ package integration_test import ( "context" + "sync" "time" . "github.com/onsi/ginkgo" @@ -25,6 +26,24 @@ var _ = Describe("Integration test", func() { var contractErr error sleepInterval := 5 * time.Second + db := shared.SetupDB() + validationProgressChan := make(chan uint64) + service := validator.NewService(db, 1, trail, validatorSleepInterval, validator.IntegrationTestChainConfig, validationProgressChan) + + wg := new(sync.WaitGroup) + + It("test init", func() { + wg.Add(1) + go service.Start(ctx, wg) + }) + + defer It("test teardown", func() { + service.Stop() + wg.Wait() + + Expect(validationProgressChan).To(BeClosed()) + }) + Describe("Validate state", func() { BeforeEach(func() { // Deploy a dummy contract as the first contract might get deployed at block number 0 @@ -35,20 +54,11 @@ var _ = Describe("Integration test", func() { time.Sleep(sleepInterval) }) - It("Validate state root", func() { + It("performs state root validation", func() { Expect(contractErr).ToNot(HaveOccurred()) - db := shared.SetupDB() - srvc := validator.NewService(db, uint64(contract.BlockNumber), trail, validatorSleepInterval, validator.IntegrationTestChainConfig) - stopCh := make(chan int, 1) - go func() { - srvc.Start(ctx, nil) - stopCh <- 1 - }() - go func() { - <-stopCh - srvc.Stop() - }() + Expect(validationProgressChan).ToNot(BeClosed()) + Eventually(validationProgressChan).Should(Receive(Equal(uint64(contract.BlockNumber)))) }) }) }) From 3182a607945be60d452ef3d68f5a86e27dc8ccc0 Mon Sep 17 00:00:00 2001 From: prathamesh0 Date: Wed, 1 Jun 2022 18:19:20 +0530 Subject: [PATCH 2/3] Add integration tests --- pkg/validator/validator.go | 6 +-- test/contract/contracts/Test.sol | 29 ++++++++++++++ test/contract/src/index.js | 45 +++++++++++++++++++++- test/helper.go | 54 ++++++++++++++++++++++++++ test/integration_test.go | 66 ++++++++++++++++++++++++-------- 5 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 test/contract/contracts/Test.sol create mode 100644 test/helper.go diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index b31d88f..e844cc5 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -98,6 +98,9 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) { select { case <-s.quitChan: s.logger.Infof("last validated block %v", idxBlockNum-1) + if s.progressChan != nil { + close(s.progressChan) + } return default: idxBlockNum, err = s.Validate(ctx, api, idxBlockNum) @@ -113,9 +116,6 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) { // Stop is used to gracefully stop the service func (s *service) Stop() { s.logger.Info("stopping ipld-eth-db-validator process") - if s.progressChan != nil { - close(s.progressChan) - } close(s.quitChan) } diff --git a/test/contract/contracts/Test.sol b/test/contract/contracts/Test.sol new file mode 100644 index 0000000..8fe1b4b --- /dev/null +++ b/test/contract/contracts/Test.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +contract Test { + address payable owner; + + modifier onlyOwner { + require( + msg.sender == owner, + "Only owner can call this function." + ); + _; + } + + uint256[100] data; + + constructor() { + owner = payable(msg.sender); + data = [1]; + } + + function Put(uint256 addr, uint256 value) public { + data[addr] = value; + } + + function close() public onlyOwner { + selfdestruct(owner); + } +} diff --git a/test/contract/src/index.js b/test/contract/src/index.js index b20d54a..f42b6af 100644 --- a/test/contract/src/index.js +++ b/test/contract/src/index.js @@ -63,6 +63,49 @@ fastify.get('/v1/sendEth', async (req, reply) => { } }); +fastify.get('/v1/deployTestContract', async (req, reply) => { + const testContract = await hre.ethers.getContractFactory("Test"); + const test = await testContract.deploy(); + await test.deployed(); + + return { + address: test.address, + txHash: test.deployTransaction.hash, + blockNumber: test.deployTransaction.blockNumber, + blockHash: test.deployTransaction.blockHash, + } +}); + +fastify.get('/v1/putTestValue', async (req, reply) => { + const addr = req.query.addr; + const index = req.query.index; + const value = req.query.value; + + const testContract = await hre.ethers.getContractFactory("Test"); + const test = await testContract.attach(addr); + + const tx = await test.Put(index, value); + const receipt = await tx.wait(); + + return { + blockNumber: receipt.blockNumber, + } +}); + +fastify.get('/v1/destroyTestContract', async (req, reply) => { + const addr = req.query.addr; + + const testContract = await hre.ethers.getContractFactory("Test"); + const test = await testContract.attach(addr); + + await test.destroy(); + const blockNum = await hre.ethers.provider.getBlockNumber() + + return { + blockNumber: blockNum, + } +}) + async function main() { try { await fastify.listen(3000, '0.0.0.0'); @@ -72,4 +115,4 @@ async function main() { } } -main(); \ No newline at end of file +main(); diff --git a/test/helper.go b/test/helper.go new file mode 100644 index 0000000..bca384f --- /dev/null +++ b/test/helper.go @@ -0,0 +1,54 @@ +package integration + +import ( + "encoding/json" + "fmt" + "net/http" + + ethServerIntegration "github.com/vulcanize/ipld-eth-server/v3/test" +) + +type PutResult struct { + BlockNumber int64 `json:"blockNumber"` +} + +const srvUrl = "http://localhost:3000" + +func DeployTestContract() (*ethServerIntegration.ContractDeployed, error) { + ethServerIntegration.DeployContract() + res, err := http.Get(fmt.Sprintf("%s/v1/deployTestContract", srvUrl)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var contract ethServerIntegration.ContractDeployed + decoder := json.NewDecoder(res.Body) + + return &contract, decoder.Decode(&contract) +} + +func PutTestValue(addr string, index, value int) (*PutResult, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/putTestValue?addr=%s&index=%d&value=%d", srvUrl, addr, index, value)) + if err != nil { + return nil, err + } + + var blockNumber PutResult + decoder := json.NewDecoder(res.Body) + + return &blockNumber, decoder.Decode(&blockNumber) +} + +func DestroyTestContract(addr string) (*ethServerIntegration.ContractDestroyed, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/destroyTestContract?addr=%s", srvUrl, addr)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var data ethServerIntegration.ContractDestroyed + decoder := json.NewDecoder(res.Body) + + return &data, decoder.Decode(&data) +} diff --git a/test/integration_test.go b/test/integration_test.go index ec0c1ba..9101a6a 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -9,9 +9,10 @@ import ( . "github.com/onsi/gomega" "github.com/vulcanize/ipld-eth-db-validator/pkg/validator" + integration "github.com/vulcanize/ipld-eth-db-validator/test" "github.com/vulcanize/ipld-eth-server/v3/pkg/shared" - integration "github.com/vulcanize/ipld-eth-server/v3/test" + ethServerIntegration "github.com/vulcanize/ipld-eth-server/v3/test" ) const ( @@ -19,12 +20,28 @@ const ( validatorSleepInterval = uint(5) ) +var ( + testAddresses = []string{ + "0x1111111111111111111111111111111111111112", + "0x1ca7c995f8eF0A2989BbcE08D5B7Efe50A584aa1", + "0x9a4b666af23a2cdb4e5538e1d222a445aeb82134", + "0xF7C7AEaECD2349b129d5d15790241c32eeE4607B", + "0x992b6E9BFCA1F7b0797Cee10b0170E536EAd3532", + "0x29ed93a7454Bc17a8D4A24D0627009eE0849B990", + "0x66E3dCA826b04B5d4988F7a37c91c9b1041e579D", + "0x96288939Ac7048c27E0E087b02bDaad3cd61b37b", + "0xD354280BCd771541c935b15bc04342c26086FE9B", + "0x7f887e25688c274E77b8DeB3286A55129B55AF14", + } +) + var _ = Describe("Integration test", func() { ctx := context.Background() - var contract *integration.ContractDeployed - var contractErr error - sleepInterval := 5 * time.Second + var contract *ethServerIntegration.ContractDeployed + var err error + sleepInterval := 2 * time.Second + timeout := 4 * time.Second db := shared.SetupDB() validationProgressChan := make(chan uint64) @@ -35,6 +52,10 @@ var _ = Describe("Integration test", func() { It("test init", func() { wg.Add(1) go service.Start(ctx, wg) + + // Deploy a dummy contract as the first contract might get deployed at block number 0 + _, _ = ethServerIntegration.DeployContract() + time.Sleep(sleepInterval) }) defer It("test teardown", func() { @@ -45,20 +66,35 @@ var _ = Describe("Integration test", func() { }) Describe("Validate state", func() { - BeforeEach(func() { - // Deploy a dummy contract as the first contract might get deployed at block number 0 - _, _ = integration.DeployContract() + It("performs validation on contract deployment", func() { + contract, err = integration.DeployTestContract() + Expect(err).ToNot(HaveOccurred()) time.Sleep(sleepInterval) - contract, contractErr = integration.DeployContract() - time.Sleep(sleepInterval) - }) - - It("performs state root validation", func() { - Expect(contractErr).ToNot(HaveOccurred()) - Expect(validationProgressChan).ToNot(BeClosed()) - Eventually(validationProgressChan).Should(Receive(Equal(uint64(contract.BlockNumber)))) + Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(contract.BlockNumber)))) + }) + + It("performs validation on contract transactions", func() { + for i := 0; i < 10; i++ { + res, txErr := integration.PutTestValue(contract.Address, i, i) + Expect(txErr).ToNot(HaveOccurred()) + time.Sleep(sleepInterval) + + Expect(validationProgressChan).ToNot(BeClosed()) + Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(res.BlockNumber)))) + } + }) + + It("performs validation on eth transfer transactions", func() { + for _, address := range testAddresses { + tx, txErr := ethServerIntegration.SendEth(address, "0.01") + Expect(txErr).ToNot(HaveOccurred()) + time.Sleep(sleepInterval) + + Expect(validationProgressChan).ToNot(BeClosed()) + Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(tx.BlockNumber)))) + } }) }) }) From 44fb305858759184d2e5e1fa5a003c88cc603a0d Mon Sep 17 00:00:00 2001 From: prathamesh0 Date: Thu, 2 Jun 2022 14:35:59 +0530 Subject: [PATCH 3/3] Setup validator config and update instructions to run tests locally --- cmd/state_validator.go | 16 +--------------- pkg/validator/config.go | 27 ++++++++++++++++++++++++++- pkg/validator/validator.go | 12 ++++++------ test/README.md | 6 +++--- test/integration_test.go | 10 +++++++++- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/cmd/state_validator.go b/cmd/state_validator.go index ded52df..20b0fe3 100644 --- a/cmd/state_validator.go +++ b/cmd/state_validator.go @@ -7,7 +7,6 @@ import ( "os/signal" "sync" - "github.com/ethereum/go-ethereum/statediff" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -34,20 +33,7 @@ func stateValidator() { logWithCommand.Fatal(err) } - height := viper.GetUint64("validate.block-height") - if height < 1 { - logWithCommand.Fatalf("block height cannot be less the 1") - } - trail := viper.GetUint64("validate.trail") - sleepInterval := viper.GetUint("validate.sleepInterval") - - chainConfigPath := viper.GetString("ethereum.chainConfig") - chainCfg, err := statediff.LoadConfig(chainConfigPath) - if err != nil { - logWithCommand.Fatal(err) - } - - service := validator.NewService(cfg.DB, height, trail, sleepInterval, chainCfg, nil) + service := validator.NewService(cfg, nil) wg := new(sync.WaitGroup) wg.Add(1) diff --git a/pkg/validator/config.go b/pkg/validator/config.go index 068f10d..6ca8966 100644 --- a/pkg/validator/config.go +++ b/pkg/validator/config.go @@ -6,6 +6,7 @@ import ( "time" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/statediff" "github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres" "github.com/jmoiron/sqlx" "github.com/spf13/viper" @@ -59,11 +60,35 @@ var TestChainConfig = ¶ms.ChainConfig{ type Config struct { dbConfig postgres.Config DB *sqlx.DB + + ChainCfg *params.ChainConfig + + BlockNum, Trail uint64 + SleepInterval uint } func NewConfig() (*Config, error) { cfg := new(Config) - return cfg, cfg.setupDB() + err := cfg.setupDB() + if err != nil { + return nil, err + } + + cfg.BlockNum = viper.GetUint64("validate.block-height") + if cfg.BlockNum < 1 { + return nil, fmt.Errorf("block height cannot be less the 1") + } + + cfg.Trail = viper.GetUint64("validate.trail") + cfg.SleepInterval = viper.GetUint("validate.sleepInterval") + + chainConfigPath := viper.GetString("ethereum.chainConfig") + cfg.ChainCfg, err = statediff.LoadConfig(chainConfigPath) + if err != nil { + return nil, err + } + + return cfg, nil } func (c *Config) setupDB() error { diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index e844cc5..5c6cac5 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -40,14 +40,14 @@ type service struct { progressChan chan uint64 } -func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chainCfg *params.ChainConfig, progressChan chan uint64) *service { +func NewService(cfg *Config, progressChan chan uint64) *service { return &service{ - db: db, - blockNum: blockNum, - trail: trailNum, - sleepInterval: sleepInterval, + db: cfg.DB, + blockNum: cfg.BlockNum, + trail: cfg.Trail, + sleepInterval: cfg.SleepInterval, logger: log.New(), - chainCfg: chainCfg, + chainCfg: cfg.ChainCfg, quitChan: make(chan bool), progressChan: progressChan, } diff --git a/test/README.md b/test/README.md index 0f8b5ad..30715fa 100644 --- a/test/README.md +++ b/test/README.md @@ -6,18 +6,18 @@ - Clone [stack-orchestrator](https://github.com/vulcanize/stack-orchestrator) and [go-ethereum](https://github.com/vulcanize/go-ethereum) repositories. - - Checkout [v3 release](https://github.com/vulcanize/go-ethereum/releases/tag/v1.10.17-statediff-3.2.1) in go-ethereum repo. + - Checkout [v3 release](https://github.com/vulcanize/go-ethereum/releases/tag/v1.10.18-statediff-3.2.2) in go-ethereum repo. ```bash # In go-ethereum repo. - git checkout v1.10.17-statediff-3.2.1 + git checkout v1.10.18-statediff-3.2.2 ``` - Checkout working commit in stack-orchestrator repo. ```bash # In stack-orchestrator repo. - git checkout 3bb1796a59827fb755410c5ce69fac567a0f832b + git checkout main ``` ## Run diff --git a/test/integration_test.go b/test/integration_test.go index 9101a6a..8da58a9 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -16,6 +16,7 @@ import ( ) const ( + blockNum = 1 trail = 0 validatorSleepInterval = uint(5) ) @@ -44,8 +45,15 @@ var _ = Describe("Integration test", func() { timeout := 4 * time.Second db := shared.SetupDB() + cfg := validator.Config{ + DB: db, + BlockNum: blockNum, + Trail: trail, + SleepInterval: validatorSleepInterval, + ChainCfg: validator.IntegrationTestChainConfig, + } validationProgressChan := make(chan uint64) - service := validator.NewService(db, 1, trail, validatorSleepInterval, validator.IntegrationTestChainConfig, validationProgressChan) + service := validator.NewService(&cfg, validationProgressChan) wg := new(sync.WaitGroup)