Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Rosetta to work with MyCelo testnets #201

Merged
merged 3 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,19 @@ go run examples/generate_balances/main.go \
rosetta-cli-conf/testnet/bootstrap_balances.json
```

### Running Rosetta with a mycelo testnet

- Set `--geth.genesis` to point to the genesis file for the testnet.
- Set `--geth.networkid` to the network ID (if this is a deployed testnet, this may be different from the `ChainID` in the genesis file). If this value is the same as the `ChainID`, it is not necessary to set this parameter.
- Set the `--monitor.initcontracts` flag (at least on the first run), which fetches necessary state from the genesis block and updates the Rosetta DB accordingly.

To run reconciliation tests on this network:

- Generate `bootstrap_balances.json` using the network's genesis block.
eelanagaraj marked this conversation as resolved.
Show resolved Hide resolved
- In the `cli-config.json`:
- Set `network` to match the `ChainID` in the genesis file (not the network ID).
- Point `bootstrap_balances` to the generated `bootstrap_balances.json`.

## Releasing rosetta

### Versioning
Expand Down
15 changes: 14 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func init() {

flagSet.String("geth.genesis", "", "(Optional) path to the genesis.json, for use with custom chains")
utils.ExitOnError(serveCmd.MarkFlagFilename("geth.genesis", "json"))
flagSet.String("geth.networkid", "", "(Optional) Network ID, for use with custom chains")
// Note that we do not set any default here because it would clash with geth.genesis if that was defined.
flagSet.String("geth.network", "", "Network to use, either 'mainnet', 'alfajores', or 'baklava'")

Expand All @@ -96,6 +97,9 @@ func init() {
flagSet.String("geth.syncmode", "fast", "Geth blockchain sync mode (fast, full, light)")
flagSet.String("geth.gcmode", "full", "Geth garbage collection mode (full, archive)")
flagSet.String("geth.maxpeers", "1100", "Maximum number of network peers (network disabled if set to 0)")

// Monitor Service Flags
flagSet.Bool("monitor.initcontracts", false, "Set to true to properly initialize contract state, i.e. when running MyCelo testnets")
}

func getDatadir(cmd *cobra.Command) string {
Expand All @@ -114,6 +118,7 @@ func readGethOption(cmd *cobra.Command, datadir string) *geth.GethOpts {
GethBinary: viper.GetString("geth.binary"),
GenesisPath: viper.GetString("geth.genesis"),
Network: viper.GetString("geth.network"),
NetworkId: viper.GetString("geth.networkid"),
Datadir: filepath.Join(datadir, "celo"),
LogsPath: viper.GetString("geth.logfile"),
IpcPath: viper.GetString("geth.ipcpath"),
Expand All @@ -138,6 +143,8 @@ func readGethOption(cmd *cobra.Command, datadir string) *geth.GethOpts {
printUsageAndExit(cmd, "Missing config option for 'geth.genesis' or 'geth.network'")
} else if opts.GenesisPath != "" && opts.Network != "" {
printUsageAndExit(cmd, "Must provide exactly one of 'geth.genesis' or 'geth.network'")
} else if opts.NetworkId != "" && opts.GenesisPath == "" {
printUsageAndExit(cmd, "Must provide 'geth.genesis' when using 'geth.networkid'")
}

return opts
Expand Down Expand Up @@ -238,8 +245,14 @@ loop:
}
return nil
})

grp.Go(func() error {
err = monitor.NewMonitorService(cc, celoStore, chainParams.IsGingerbread).Start(ctx)
err = monitor.NewMonitorService(
cc,
celoStore,
chainParams.IsGingerbread,
viper.GetBool("monitor.initcontracts"),
).Start(ctx)
if err != nil {
fmt.Println("error running mon serrvice")
ec.Add(fmt.Errorf("error running monitor service : %w", err))
Expand Down
11 changes: 10 additions & 1 deletion service/geth/geth.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type GethOpts struct {
GethBinary string
GenesisPath string
Network string
NetworkId string
IpcPath string
LogsPath string
Datadir string
Expand Down Expand Up @@ -236,7 +237,15 @@ func (gs *gethService) startGeth(stdErr *os.File) error {
case params.MainnetChainConfig.ChainID.String():
break
default:
gethArgs = append(gethArgs, "--networkid", gs.chainParams.ChainId.String())
gs.logger.Info("Setting networkId")
var networkId string
if gs.opts.NetworkId != "" {
networkId = gs.opts.NetworkId
} else {
gs.logger.Info("Using genesis ChainId as NetworkId")
networkId = gs.chainParams.ChainId.String()
}
gethArgs = append(gethArgs, "--networkid", networkId)
}

if gs.opts.Verbosity != "" {
Expand Down
10 changes: 9 additions & 1 deletion service/monitor/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ type monitorService struct {
db db.RosettaDB
logger log.Logger
isGingerbread func(*big.Int) bool
initContracts bool // Necessary for running with mycelo testnets
}

const srvName = "celo-monitor"

func NewMonitorService(cc *client.CeloClient, db db.RosettaDB, isGingerbread func(*big.Int) bool) *monitorService {
func NewMonitorService(cc *client.CeloClient, db db.RosettaDB, isGingerbread func(*big.Int) bool, initContracts bool) *monitorService {
return &monitorService{
cc: cc,
db: db,
logger: log.New("srv", srvName),
isGingerbread: isGingerbread,
initContracts: initContracts,
}
}

Expand Down Expand Up @@ -70,6 +72,12 @@ func (ms *monitorService) Start(ctx context.Context) error {
return err
}

if ms.initContracts && startBlock.Cmp(big.NewInt(1)) < 0 {
if err := UpdateDBForBlock(ctx, startBlock, ms.cc, ms.db, ms.logger); err != nil {
return err
}
}

ms.logger.Info("Resuming operation from last persisted block", "block", startBlock)
startBlock = utils.Inc(startBlock)

Expand Down
144 changes: 144 additions & 0 deletions service/monitor/static_processor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2023 Celo Org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package monitor

import (
"context"
"math/big"

"github.com/celo-org/celo-blockchain/accounts/abi/bind"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/log"
"github.com/celo-org/kliento/client"
"github.com/celo-org/kliento/contracts"
"github.com/celo-org/kliento/registry"
"github.com/celo-org/rosetta/db"
)

type staticProcessor struct {
callOpts *bind.CallOpts
boundRegistry *contracts.Registry
cc *client.CeloClient
logger log.Logger
}

func newStaticProcessor(
ctx context.Context,
blockNumber *big.Int,
cc *client.CeloClient,
logger log.Logger,
) (*staticProcessor, error) {
boundRegistry, err := contracts.NewRegistry(registry.RegistryAddress, cc.Eth)
if err != nil {
return nil, err
}
callOpts := &bind.CallOpts{
Context: ctx,
BlockNumber: blockNumber,
}
return &staticProcessor{
callOpts: callOpts,
boundRegistry: boundRegistry,
logger: logger.New("pipe", "static_processor"),
cc: cc,
}, nil
}

func (sf *staticProcessor) fetchContractAddresses() ([]db.RegistryChange, error) {
contractAddresses := []db.RegistryChange{}

for _, contractId := range registry.RegisteredContractIDs {
contractStrName := contractId.String()
contractAddress, err := sf.boundRegistry.GetAddressForString(sf.callOpts, contractStrName)
if err != nil {
return nil, err
}
contractAddresses = append(contractAddresses, db.RegistryChange{
TxIndex: 0,
Contract: contractStrName,
NewAddress: contractAddress,
})
sf.logger.Info("Found Core Contract address", "name", contractStrName, "newAddress", contractAddress.Hex())
}
return contractAddresses, nil
}

func (sf *staticProcessor) fetchGasPriceMinimum() (*big.Int, error) {
gpmAddress, err := sf.boundRegistry.GetAddressForString(sf.callOpts, registry.GasPriceMinimumContractID.String())
if err != nil || gpmAddress == common.ZeroAddress {
return nil, err
}
gpmContract, err := contracts.NewGasPriceMinimum(gpmAddress, sf.cc.Eth)
if err != nil {
return nil, err
}
return gpmContract.GetGasPriceMinimum(sf.callOpts, common.ZeroAddress)
}

func (sf *staticProcessor) fetchCarbonOffsetPartner() (*db.CarbonOffsetPartnerChange, error) {
epochRewardsAddress, err := sf.boundRegistry.GetAddressForString(sf.callOpts, registry.EpochRewardsContractID.String())
if err != nil || epochRewardsAddress == common.ZeroAddress {
return nil, err
}
epochRewards, err := contracts.NewEpochRewards(epochRewardsAddress, sf.cc.Eth)
if err != nil {
return nil, err
}
offsettingPartner, err := epochRewards.CarbonOffsettingPartner(sf.callOpts)
if err != nil {
return nil, err
}
return &db.CarbonOffsetPartnerChange{TxIndex: 0, Address: offsettingPartner}, nil
}

// Fetches relevant state from block and updates the Rosetta DB accordingly.
// This is necessary for running Rosetta on networks that have Core Contracts
// already deployed at genesis (e.g. myCelo networks).
func UpdateDBForBlock(
ctx context.Context,
blockNumber *big.Int,
cc *client.CeloClient,
rosettaDB db.RosettaDB,
logger log.Logger,
) error {
sf, err := newStaticProcessor(ctx, blockNumber, cc, logger)
if err != nil {
return err
}
sf.logger.Info("Fetching contract state", "block", blockNumber)
contractAddresses, err := sf.fetchContractAddresses()
if err != nil {
return err
}
gpm, err := sf.fetchGasPriceMinimum()
if err != nil {
return err
}
sf.logger.Info("Found GasPriceMinimum", "block", blockNumber, "GPM", gpm)
carbonOffsetPartner, err := sf.fetchCarbonOffsetPartner()
if err != nil {
return err
}
if carbonOffsetPartner != nil {
sf.logger.Info("Found CarbonOffsetPartner", "block", blockNumber, "address", carbonOffsetPartner.Address)
}

return rosettaDB.ApplyChanges(ctx, &db.BlockChangeSet{
BlockNumber: blockNumber,
RegistryChanges: contractAddresses,
GasPriceMinimum: gpm,
CarbonOffsetPartnerChange: *carbonOffsetPartner,
})
}