Merge pull request #11 from deep-stack/pm-integration-testing

Add integration tests
This commit is contained in:
Ashwin Phatak 2022-06-02 15:24:39 +05:30 committed by GitHub
commit 7f079a0d38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 249 additions and 49 deletions

View File

@ -7,7 +7,6 @@ import (
"os/signal" "os/signal"
"sync" "sync"
"github.com/ethereum/go-ethereum/statediff"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -34,20 +33,7 @@ func stateValidator() {
logWithCommand.Fatal(err) logWithCommand.Fatal(err)
} }
height := viper.GetUint64("validate.block-height") service := validator.NewService(cfg, nil)
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)
wg := new(sync.WaitGroup) wg := new(sync.WaitGroup)
wg.Add(1) wg.Add(1)

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/statediff"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres" "github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -59,11 +60,35 @@ var TestChainConfig = &params.ChainConfig{
type Config struct { type Config struct {
dbConfig postgres.Config dbConfig postgres.Config
DB *sqlx.DB DB *sqlx.DB
ChainCfg *params.ChainConfig
BlockNum, Trail uint64
SleepInterval uint
} }
func NewConfig() (*Config, error) { func NewConfig() (*Config, error) {
cfg := new(Config) 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 { func (c *Config) setupDB() error {

View File

@ -37,17 +37,19 @@ type service struct {
logger *log.Logger logger *log.Logger
chainCfg *params.ChainConfig chainCfg *params.ChainConfig
quitChan chan bool quitChan chan bool
progressChan chan uint64
} }
func NewService(db *sqlx.DB, blockNum, trailNum uint64, sleepInterval uint, chainCfg *params.ChainConfig) *service { func NewService(cfg *Config, progressChan chan uint64) *service {
return &service{ return &service{
db: db, db: cfg.DB,
blockNum: blockNum, blockNum: cfg.BlockNum,
trail: trailNum, trail: cfg.Trail,
sleepInterval: sleepInterval, sleepInterval: cfg.SleepInterval,
logger: log.New(), logger: log.New(),
chainCfg: chainCfg, chainCfg: cfg.ChainCfg,
quitChan: make(chan bool), quitChan: make(chan bool),
progressChan: progressChan,
} }
} }
@ -96,7 +98,9 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) {
select { select {
case <-s.quitChan: case <-s.quitChan:
s.logger.Infof("last validated block %v", idxBlockNum-1) s.logger.Infof("last validated block %v", idxBlockNum-1)
s.logger.Info("stopping ipld-eth-db-validator process") if s.progressChan != nil {
close(s.progressChan)
}
return return
default: default:
idxBlockNum, err = s.Validate(ctx, api, idxBlockNum) idxBlockNum, err = s.Validate(ctx, api, idxBlockNum)
@ -111,6 +115,7 @@ func (s *service) Start(ctx context.Context, wg *sync.WaitGroup) {
// Stop is used to gracefully stop the service // Stop is used to gracefully stop the service
func (s *service) Stop() { func (s *service) Stop() {
s.logger.Info("stopping ipld-eth-db-validator process")
close(s.quitChan) 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) s.logger.Infof("state root verified for block %d", idxBlockNum)
if s.progressChan != nil {
s.progressChan <- idxBlockNum
}
idxBlockNum++ idxBlockNum++
} else { } else {
// Sleep / wait for head to move ahead // Sleep / wait for head to move ahead

View File

@ -6,18 +6,18 @@
- Clone [stack-orchestrator](https://github.com/vulcanize/stack-orchestrator) and [go-ethereum](https://github.com/vulcanize/go-ethereum) repositories. - 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 ```bash
# In go-ethereum repo. # 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. - Checkout working commit in stack-orchestrator repo.
```bash ```bash
# In stack-orchestrator repo. # In stack-orchestrator repo.
git checkout 3bb1796a59827fb755410c5ce69fac567a0f832b git checkout main
``` ```
## Run ## Run

View File

@ -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);
}
}

View File

@ -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() { async function main() {
try { try {
await fastify.listen(3000, '0.0.0.0'); await fastify.listen(3000, '0.0.0.0');
@ -72,4 +115,4 @@ async function main() {
} }
} }
main(); main();

54
test/helper.go Normal file
View File

@ -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)
}

View File

@ -2,53 +2,107 @@ package integration_test
import ( import (
"context" "context"
"sync"
"time" "time"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/vulcanize/ipld-eth-db-validator/pkg/validator" "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" "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 ( const (
blockNum = 1
trail = 0 trail = 0
validatorSleepInterval = uint(5) validatorSleepInterval = uint(5)
) )
var (
testAddresses = []string{
"0x1111111111111111111111111111111111111112",
"0x1ca7c995f8eF0A2989BbcE08D5B7Efe50A584aa1",
"0x9a4b666af23a2cdb4e5538e1d222a445aeb82134",
"0xF7C7AEaECD2349b129d5d15790241c32eeE4607B",
"0x992b6E9BFCA1F7b0797Cee10b0170E536EAd3532",
"0x29ed93a7454Bc17a8D4A24D0627009eE0849B990",
"0x66E3dCA826b04B5d4988F7a37c91c9b1041e579D",
"0x96288939Ac7048c27E0E087b02bDaad3cd61b37b",
"0xD354280BCd771541c935b15bc04342c26086FE9B",
"0x7f887e25688c274E77b8DeB3286A55129B55AF14",
}
)
var _ = Describe("Integration test", func() { var _ = Describe("Integration test", func() {
ctx := context.Background() ctx := context.Background()
var contract *integration.ContractDeployed var contract *ethServerIntegration.ContractDeployed
var contractErr error var err error
sleepInterval := 5 * time.Second sleepInterval := 2 * time.Second
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(&cfg, validationProgressChan)
wg := new(sync.WaitGroup)
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() {
service.Stop()
wg.Wait()
Expect(validationProgressChan).To(BeClosed())
})
Describe("Validate state", func() { Describe("Validate state", func() {
BeforeEach(func() { It("performs validation on contract deployment", func() {
// Deploy a dummy contract as the first contract might get deployed at block number 0 contract, err = integration.DeployTestContract()
_, _ = integration.DeployContract() Expect(err).ToNot(HaveOccurred())
time.Sleep(sleepInterval) time.Sleep(sleepInterval)
contract, contractErr = integration.DeployContract() Expect(validationProgressChan).ToNot(BeClosed())
time.Sleep(sleepInterval) Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(contract.BlockNumber))))
}) })
It("Validate state root", func() { It("performs validation on contract transactions", func() {
Expect(contractErr).ToNot(HaveOccurred()) for i := 0; i < 10; i++ {
res, txErr := integration.PutTestValue(contract.Address, i, i)
Expect(txErr).ToNot(HaveOccurred())
time.Sleep(sleepInterval)
db := shared.SetupDB() Expect(validationProgressChan).ToNot(BeClosed())
srvc := validator.NewService(db, uint64(contract.BlockNumber), trail, validatorSleepInterval, validator.IntegrationTestChainConfig) Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(res.BlockNumber))))
stopCh := make(chan int, 1) }
go func() { })
srvc.Start(ctx, nil)
stopCh <- 1 It("performs validation on eth transfer transactions", func() {
}() for _, address := range testAddresses {
go func() { tx, txErr := ethServerIntegration.SendEth(address, "0.01")
<-stopCh Expect(txErr).ToNot(HaveOccurred())
srvc.Stop() time.Sleep(sleepInterval)
}()
Expect(validationProgressChan).ToNot(BeClosed())
Eventually(validationProgressChan, timeout).Should(Receive(Equal(uint64(tx.BlockNumber))))
}
}) })
}) })
}) })