Skip to content

Commit

Permalink
statesync: refactor to fetch params from RPC server instead of API
Browse files Browse the repository at this point in the history
 * apiclient: drop DefaultAPIUrls and FetchChainHeightAndHash*
 * ci: drop redundant env vars
  • Loading branch information
altergui committed Mar 14, 2024
1 parent 93f4220 commit 0d91408
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 91 deletions.
10 changes: 0 additions & 10 deletions apiclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,6 @@ const (
DefaultTimeout = 10 * time.Second
)

// DefaultAPIUrls is a map of default API URLs for each network.
var DefaultAPIUrls = map[string]string{
"dev": "https://api-dev.vocdoni.net/v2/",
"develop": "https://api-dev.vocdoni.net/v2/",
"stg": "https://api-stg.vocdoni.net/v2/",
"stage": "https://api-stg.vocdoni.net/v2/",
"lts": "https://api.vocdoni.io/v2/",
"prod": "https://api.vocdoni.io/v2/",
}

// HTTPclient is the Vocdoni API HTTP client.
type HTTPclient struct {
c *http.Client
Expand Down
32 changes: 0 additions & 32 deletions apiclient/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,35 +337,3 @@ func UnmarshalFaucetPackage(data []byte) (*models.FaucetPackage, error) {
Signature: fpackage.Signature,
}, nil
}

// FetchChainHeightAndHashFromDefaultAPI returns the current chain height and block hash
// from the default API url for the given network.
func FetchChainHeightAndHashFromDefaultAPI(network string) (int64, types.HexBytes, error) {
apiURL, ok := DefaultAPIUrls[network]
if !ok {
return 0, nil, fmt.Errorf("no default API URL for network %s", network)
}
height, hash, err := FetchChainHeightAndHash(apiURL)
if err != nil {
return 0, nil, fmt.Errorf("couldn't fetch info: %w", err)
}
return height, hash, nil
}

// FetchChainHeightAndHash returns current chain height and block hash from an API endpoint.
func FetchChainHeightAndHash(apiURL string) (int64, types.HexBytes, error) {
log.Infow("requesting chain height and hash", "url", apiURL)
c, err := New(apiURL)
if err != nil {
return 0, nil, fmt.Errorf("couldn't init apiclient: %w", err)
}
chain, err := c.ChainInfo()
if err != nil {
return 0, nil, fmt.Errorf("couldn't fetch chain info: %w", err)
}
block, err := c.Block(chain.Height)
if err != nil {
return 0, nil, fmt.Errorf("couldn't fetch block: %w", err)
}
return int64(chain.Height), block.Hash, nil
}
45 changes: 4 additions & 41 deletions cmd/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
urlapi "go.vocdoni.io/dvote/api"
"go.vocdoni.io/dvote/api/censusdb"
"go.vocdoni.io/dvote/api/faucet"
"go.vocdoni.io/dvote/apiclient"
"go.vocdoni.io/dvote/config"
"go.vocdoni.io/dvote/crypto/ethereum"
"go.vocdoni.io/dvote/crypto/zk/circuit"
Expand Down Expand Up @@ -195,13 +194,13 @@ func loadConfig() *config.Config {
flag.StringSlice("vochainStateSyncRPCServers", []string{},
"list of RPC servers to bootstrap the StateSync (optional, defaults to using seeds)")
flag.String("vochainStateSyncTrustHash", "",
"hash of the trusted block (takes precedence over API URL and hardcoded defaults)")
"hash of the trusted block (takes precedence over RPC and hardcoded defaults)")
flag.Int64("vochainStateSyncTrustHeight", 0,
"height of the trusted block (takes precedence over API URL and hardcoded defaults)")
"height of the trusted block (takes precedence over RPC and hardcoded defaults)")
flag.Int64("vochainStateSyncChunkSize", 10*(1<<20), // 10 MB
"cometBFT chunk size in bytes")
flag.String("vochainStateSyncFetchParamsFromAPI", "",
"API URL to fetch needed params from (by default, it will use hardcoded URLs, set to 'disabled' to skip this feature)")
flag.Bool("vochainStateSyncFetchParamsFromRPC", true,
"allow statesync to fetch TrustHash and TrustHeight from the first RPCServer")

flag.Int("vochainMinerTargetBlockTimeSeconds", 10,
"vochain consensus block time target (in seconds)")
Expand Down Expand Up @@ -449,42 +448,6 @@ func main() {
}
}

// If StateSync is enabled but parameters are empty, try our best to populate them
// (cmdline flags take precedence if defined, of course)
if conf.Vochain.StateSyncEnabled &&
conf.Vochain.StateSyncTrustHeight == 0 && conf.Vochain.StateSyncTrustHash == "" {
conf.Vochain.StateSyncTrustHeight, conf.Vochain.StateSyncTrustHash = func() (int64, string) {
// first try to fetch params from remote API endpoint
switch strings.ToLower(conf.Vochain.StateSyncFetchParamsFromAPI) {
case "disabled":
// magic keyword to skip this feature, do nothing
case "":
height, hash, err := apiclient.FetchChainHeightAndHashFromDefaultAPI(conf.Vochain.Network)
if err != nil {
log.Warnw("couldn't fetch current state sync params", "err", err)
} else {
return height, hash.String()
}
default:
height, hash, err := apiclient.FetchChainHeightAndHash(conf.Vochain.StateSyncFetchParamsFromAPI)
if err != nil {
log.Warnw("couldn't fetch current state sync params", "err", err)
} else {
return height, hash.String()
}
}
// else, fallback to hardcoded params, if defined for the current network & chainID
if g, ok := genesis.Genesis[conf.Vochain.Network]; ok {
if statesync, ok := g.StateSync[g.Genesis.ChainID]; ok {
return statesync.TrustHeight, statesync.TrustHash.String()
}
}
return 0, ""
}()
log.Infow("automatically determined statesync params",
"height", conf.Vochain.StateSyncTrustHeight, "hash", conf.Vochain.StateSyncTrustHash)
}

//
// Vochain and Indexer
//
Expand Down
5 changes: 2 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,8 @@ type VochainCfg struct {
StateSyncTrustHash string
// StateSyncChunkSize defines the size of the chunks when splitting a Snapshot for sending via StateSync
StateSyncChunkSize int64
// StateSyncFetchParamsFromAPI defines an API URL to fetch the params from.
// If empty it will use default API urls, special keyword "disable" means to skip this feature altogether
StateSyncFetchParamsFromAPI string
// StateSyncFetchParamsFromRPC allows statesync to fetch TrustHash and TrustHeight from the first RPCServer
StateSyncFetchParamsFromRPC bool
}

// IndexerCfg handles the configuration options of the indexer
Expand Down
4 changes: 0 additions & 4 deletions dockerfiles/testsuite/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@ services:
environment:
- GOCOVERDIR=/app/run/gocoverage
- LOG_PANIC_ON_INVALIDCHARS
- VOCDONI_LOGLEVEL=debug
- VOCDONI_VOCHAIN_STATESYNCENABLED=true
- VOCDONI_VOCHAIN_STATESYNCRPCSERVERS=miner0:26657,miner0:26657
- VOCDONI_VOCHAIN_STATESYNCFETCHPARAMSFROMAPI

networks:
blockchain:
Expand Down
1 change: 0 additions & 1 deletion dockerfiles/testsuite/start_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ e2etest_ballotelection() {
}

test_statesync() {
export VOCDONI_VOCHAIN_STATESYNCFETCHPARAMSFROMAPI=$APIHOST
$COMPOSE_CMD up gatewaySync -d
# watch logs for 2 minutes, until catching 'startup complete'. in case of timeout, or panic, or whatever, test will fail
timeout 120 sh -c "($COMPOSE_CMD logs gatewaySync -f | grep -m 1 'startup complete')"
Expand Down
14 changes: 14 additions & 0 deletions vochain/apputils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math"
"os"
"path/filepath"
"strings"
"time"

"go.vocdoni.io/dvote/crypto/ethereum"
Expand All @@ -19,6 +20,7 @@ import (
crypto256k1 "github.com/cometbft/cometbft/crypto/secp256k1"
cometp2p "github.com/cometbft/cometbft/p2p"
cometprivval "github.com/cometbft/cometbft/privval"
cometrpchttp "github.com/cometbft/cometbft/rpc/client/http"
comettypes "github.com/cometbft/cometbft/types"
ethcommon "github.com/ethereum/go-ethereum/common"
)
Expand Down Expand Up @@ -183,3 +185,15 @@ func NewTemplateGenesisFile(dir string, validators int) (*comettypes.GenesisDoc,
gd.AppState = appStateBytes
return &gd, gd.SaveAs(filepath.Join(dir, "genesis.json"))
}

// newCometRPCClient sets up a new cometbft RPC client
func newCometRPCClient(server string) (*cometrpchttp.HTTP, error) {
if !strings.Contains(server, "://") {
server = "http://" + server
}
c, err := cometrpchttp.New(server)
if err != nil {
return nil, err
}
return c, nil
}
33 changes: 33 additions & 0 deletions vochain/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,39 @@ func newTendermint(app *BaseApplication,

tconfig.StateSync.TrustHeight = localConfig.StateSyncTrustHeight
tconfig.StateSync.TrustHash = localConfig.StateSyncTrustHash

// If StateSync is enabled but parameters are empty, try our best to populate them
// first try to fetch params from remote API endpoint
if localConfig.StateSyncFetchParamsFromRPC &&
tconfig.StateSync.TrustHeight == 0 && tconfig.StateSync.TrustHash == "" {
tconfig.StateSync.TrustHeight, tconfig.StateSync.TrustHash = func() (int64, string) {
cli, err := newCometRPCClient(tconfig.StateSync.RPCServers[0])
if err != nil {
log.Warnf("cannot connect to remote RPC server: %v", err)
return 0, ""
}
status, err := cli.Status(context.TODO())
if err != nil {
log.Warnf("cannot fetch status from remote RPC server: %v", err)
return 0, ""
}
log.Infow("fetched statesync params from remote RPC",
"height", status.SyncInfo.LatestBlockHeight, "hash", status.SyncInfo.LatestBlockHash.String())
return status.SyncInfo.LatestBlockHeight, status.SyncInfo.LatestBlockHash.String()
}()
}

// if still empty, fallback to hardcoded params, if defined for the current network & chainID
if tconfig.StateSync.TrustHeight == 0 && tconfig.StateSync.TrustHash == "" {
if g, ok := vocdoniGenesis.Genesis[localConfig.Network]; ok {
if statesync, ok := g.StateSync[g.Genesis.ChainID]; ok {
tconfig.StateSync.TrustHeight = statesync.TrustHeight
tconfig.StateSync.TrustHash = statesync.TrustHash.String()
log.Infow("using hardcoded statesync params",
"height", tconfig.StateSync.TrustHeight, "hash", tconfig.StateSync.TrustHash)
}
}
}
}
tconfig.RPC.ListenAddress = "tcp://0.0.0.0:26657"

Expand Down

0 comments on commit 0d91408

Please sign in to comment.