Skip to content

Commit

Permalink
Config structs (#11)
Browse files Browse the repository at this point in the history
* bring config into core to be used by implementation

* added substrate configurations

* moved reading of config to the top core level and parsing to the individual eth and substrate packages

* unit tests for loading of config and parsing of into correct types for EVMConfig and SubstrateConfig

* additional config fields and little clean up changes

* additional fields

* change to pointer for chain id and unit testing of parsers

* reverse changes

* remove configs from within core to be moved into modules

* cleanup of old config structs

* added shared evm config for loading and validation

* moved config into separate package and usage of more embedded structs

* Config flags (#13)

* changes to incorporate flags and organizing the config package to better help understand how to incorporate a new configuration

* cleanup

* moved blockstore set up into blockstore package

* insecure testkey flag and moved keystore into core

* added config flag name

* added config tests, all passing, and fixed go mo

* moved viper flag binding into core package
  • Loading branch information
vezenovm authored Jul 2, 2021
1 parent d490a6e commit c073bc8
Show file tree
Hide file tree
Showing 20 changed files with 1,635 additions and 58 deletions.
19 changes: 19 additions & 0 deletions blockstore/blockstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"math/big"

"github.com/ChainSafe/chainbridge-core/config"
"github.com/syndtr/goleveldb/leveldb"
)

Expand Down Expand Up @@ -54,3 +55,21 @@ func GetLastStoredBlock(db KeyValueReader, chainID uint8) (*big.Int, error) {
block := big.NewInt(0).SetBytes(v)
return block, nil
}

// SetupBlockstore queries the blockstore for the latest known block. If the latest block is
// greater than config.StartBlock, then config.StartBlock is replaced with the latest known block.
func SetupBlockstore(generalConfig *config.GeneralChainConfig, kvdb KeyValueReaderWriter, startBlock *big.Int) (*big.Int, error) {
latestBlock, err := GetLastStoredBlock(kvdb, *generalConfig.Id)
if err != nil {
return nil, err
}

if !generalConfig.FreshStart {
if latestBlock.Cmp(startBlock) == 1 {
return latestBlock, nil
} else {
return startBlock, nil
}
}
return big.NewInt(0), nil
}
11 changes: 7 additions & 4 deletions chains/evm/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/big"

"github.com/ChainSafe/chainbridge-core/blockstore"
"github.com/ChainSafe/chainbridge-core/config"
"github.com/ChainSafe/chainbridge-core/relayer"
"github.com/rs/zerolog/log"
)
Expand All @@ -27,22 +28,24 @@ type EVMChain struct {
chainID uint8
kvdb blockstore.KeyValueReaderWriter
bridgeContractAddress string
config *config.SharedEVMConfig
}

func NewEVMChain(dr EventListener, writer ProposalVoter, kvdb blockstore.KeyValueReaderWriter, chainID uint8) *EVMChain {
return &EVMChain{listener: dr, writer: writer, kvdb: kvdb, chainID: chainID}
func NewEVMChain(dr EventListener, writer ProposalVoter, kvdb blockstore.KeyValueReaderWriter, chainID uint8, config *config.SharedEVMConfig) *EVMChain {
return &EVMChain{listener: dr, writer: writer, kvdb: kvdb, chainID: chainID, config: config}
}

// PollEvents is the goroutine that polling blocks and searching Deposit Events in them. Event then sent to eventsChan
func (c *EVMChain) PollEvents(stop <-chan struct{}, sysErr chan<- error, eventsChan chan *relayer.Message) {
log.Info().Msg("Polling Blocks...")
// Handler chain specific configs and flags
b, err := blockstore.GetLastStoredBlock(c.kvdb, c.chainID)
//b, err := blockstore.GetLastStoredBlock(c.kvdb, c.chainID)
block, err := blockstore.SetupBlockstore(&c.config.GeneralChainConfig, c.kvdb, c.config.StartBlock)
if err != nil {
sysErr <- fmt.Errorf("error %w on getting last stored block", err)
return
}
ech := c.listener.ListenToEvents(b, c.chainID, c.kvdb, stop, sysErr)
ech := c.listener.ListenToEvents(block, c.chainID, c.kvdb, stop, sysErr)
for {
select {
case <-stop:
Expand Down
98 changes: 98 additions & 0 deletions chains/evm/evmclient/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package evmclient

import (
"encoding/json"
"fmt"
"os"

"github.com/ChainSafe/chainbridge-core/config"
"github.com/ChainSafe/chainbridge-core/crypto/secp256k1"
"github.com/spf13/viper"
)

const DefaultGasLimit = 6721975
const DefaultGasPrice = 20000000000
const DefaultGasMultiplier = 1
const DefaultBlockConfirmations = 10

type EVMConfig struct {
SharedEVMConfig config.SharedEVMConfig
kp *secp256k1.Keypair
EgsApiKey string // API key for ethgasstation to query gas prices
EgsSpeed string // The speed which a transaction should be processed: average, fast, fastest. Default: fast
}

type RawEVMConfig struct {
config.RawSharedEVMConfig `mapstructure:",squash"`
EgsApiKey string `mapstructure:"egsApiKey"`
EgsSpeed string `mapstructure:"egsSpeed"`
}

func NewConfig() *EVMConfig {
return &EVMConfig{}
}

func GetConfig(path string, name string) (*RawEVMConfig, error) {
config := &RawEVMConfig{}

viper.AddConfigPath(path)
viper.SetConfigName(name)
viper.SetConfigType("json")

if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read in the config file, error: %w", err)
}

if err := viper.Unmarshal(config); err != nil {
return nil, fmt.Errorf("failed to unmarshal config into struct, error: %w", err)
}

if err := config.Validate(); err != nil {
return nil, err
}

return config, nil
}

func ParseConfig(rawConfig *RawEVMConfig) (*EVMConfig, error) {

cfg, err := rawConfig.RawSharedEVMConfig.ParseConfig()
if err != nil {
return nil, fmt.Errorf("failed to parse shared evm config, error: %w", err)
}

config := &EVMConfig{
SharedEVMConfig: *cfg,
EgsApiKey: "",
EgsSpeed: "",
}

return config, nil
}

func (c *RawEVMConfig) ToJSON(file string) *os.File {
var (
newFile *os.File
err error
)

var raw []byte
if raw, err = json.Marshal(&c); err != nil {
fmt.Println("error marshalling json", "err", err)
os.Exit(1)
}

newFile, err = os.Create(file)
if err != nil {
fmt.Println("error creating config file", "err", err)
}
_, err = newFile.Write(raw)
if err != nil {
fmt.Println("error writing to config file", "err", err)
}

if err := newFile.Close(); err != nil {
fmt.Println("failed to unmarshal config into struct", "err", err)
}
return newFile
}
210 changes: 210 additions & 0 deletions chains/evm/evmclient/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package evmclient

import (
"fmt"
"io/ioutil"
"math/big"
"os"
"reflect"
"testing"

"github.com/ChainSafe/chainbridge-core/config"
)

func TestLoadJSONConfig(t *testing.T) {
file, cfg := createTempConfigFile()
defer os.Remove(file.Name())

res, err := GetConfig(".", file.Name())
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(res, cfg) {
t.Errorf("did not match\ngot: %+v\nexpected: %+v", res, cfg)
}
}

func TestParseChainConfig(t *testing.T) {

generalConfig := createGeneralConfig()

input := RawEVMConfig{
RawSharedEVMConfig: config.RawSharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
Erc721Handler: "0x1234",
GenericHandler: "0x1234",
MaxGasPrice: 20,
GasMultiplier: 1,
GasLimit: 10,
Http: true,
StartBlock: 9999,
BlockConfirmations: 10,
},
}

out, err := ParseConfig(&input)
if err != nil {
t.Fatal(err)
}

expected := EVMConfig{
SharedEVMConfig: config.SharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
Erc721Handler: "0x1234",
GenericHandler: "0x1234",
MaxGasPrice: big.NewInt(20),
GasMultiplier: big.NewFloat(1),
GasLimit: big.NewInt(10),
Http: true,
StartBlock: big.NewInt(9999),
BlockConfirmations: big.NewInt(10),
},
}

if !reflect.DeepEqual(&expected, out) {
t.Fatalf("Output not expected.\n\tExpected: %#v\n\tGot: %#v\n", &expected, out)
}
}

// TestParseChainConfigWithNoBlockConfirmations Tests chain config without block confirmations
func TestParseChainConfigWithNoBlockConfirmations(t *testing.T) {
generalConfig := createGeneralConfig()

input := RawEVMConfig{
RawSharedEVMConfig: config.RawSharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
Erc721Handler: "0x1234",
GenericHandler: "0x1234",
MaxGasPrice: 20,
GasMultiplier: 1,
GasLimit: 10,
Http: true,
StartBlock: 9999,
},
}

out, err := ParseConfig(&input)

if err != nil {
t.Fatal(err)
}

expected := EVMConfig{
SharedEVMConfig: config.SharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
Erc721Handler: "0x1234",
GenericHandler: "0x1234",
MaxGasPrice: big.NewInt(20),
GasMultiplier: big.NewFloat(1),
GasLimit: big.NewInt(10),
Http: true,
StartBlock: big.NewInt(9999),
BlockConfirmations: big.NewInt(10),
},
}

if !reflect.DeepEqual(&expected, out) {
t.Fatalf("Output not expected.\n\tExpected: %#v\n\tGot: %#v\n", &expected, out)
}
}

//TestChainConfigOneContract Tests chain config providing only one contract
func TestChainConfigOneContract(t *testing.T) {

generalConfig := createGeneralConfig()

input := RawEVMConfig{
RawSharedEVMConfig: config.RawSharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
MaxGasPrice: 20,
GasMultiplier: 1,
GasLimit: 10,
Http: true,
},
}

out, err := ParseConfig(&input)

if err != nil {
t.Fatal(err)
}

expected := EVMConfig{
SharedEVMConfig: config.SharedEVMConfig{
GeneralChainConfig: generalConfig,
Bridge: "0x1234",
Erc20Handler: "0x1234",
MaxGasPrice: big.NewInt(20),
GasMultiplier: big.NewFloat(1),
GasLimit: big.NewInt(10),
Http: true,
StartBlock: big.NewInt(0),
BlockConfirmations: big.NewInt(10),
},
}

if !reflect.DeepEqual(&expected, out) {
t.Fatalf("Output not expected.\n\tExpected: %#v\n\tGot: %#v\n", &expected, out)
}
}

func TestRequiredOpts(t *testing.T) {
// No opts provided
input := RawEVMConfig{}

_, err := ParseConfig(&input)

if err == nil {
t.Error("config missing chainId field but no error reported")
}

// Empty bridgeContract provided
input = RawEVMConfig{RawSharedEVMConfig: config.RawSharedEVMConfig{Bridge: ""}}

_, err = ParseConfig(&input)

if err == nil {
t.Error("config missing bridge address field but no error reported")
}

}

func createGeneralConfig() config.GeneralChainConfig {
var id uint8 = 1
return config.GeneralChainConfig{
Name: "chain",
Type: "ethereum",
Id: &id,
Endpoint: "endpoint",
From: "0x0",
}
}

func createTempConfigFile() (*os.File, *RawEVMConfig) {
generalCfg := createGeneralConfig()
ethCfg := RawEVMConfig{
RawSharedEVMConfig: config.RawSharedEVMConfig{
GeneralChainConfig: generalCfg,
Bridge: "0x1234"},
}
tmpFile, err := ioutil.TempFile(".", "*.json")
//fmt.Println(*generalCfg.Id)
if err != nil {
fmt.Println("Cannot create temporary file", "err", err)
os.Exit(1)
}

f := ethCfg.ToJSON(tmpFile.Name())
return f, &ethCfg
}
Loading

0 comments on commit c073bc8

Please sign in to comment.