Skip to content

Commit

Permalink
feat: download-genesis command (#2791)
Browse files Browse the repository at this point in the history
Closes #2777

Note for reviewers: I'd rather not backport this PR to v1.x b/c it's a
new feature and not a bug fix. I wanted to have it ready in case ppl do
want it on v1.x

## Testing

Works for all 3 known networks. Complains on an unknown network
```shell
$ ./build/celestia-appd download-genesis mocha-4
Downloading genesis file for mocha-4 to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for mocha-4 to /Users/rootulp/.celestia-app/config/genesis.json
SHA-256 hash verified for mocha-4

$ ./build/celestia-appd download-genesis celestia
Downloading genesis file for celestia to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for celestia to /Users/rootulp/.celestia-app/config/genesis.json
SHA-256 hash verified for celestia

$ ./build/celestia-appd download-genesis arabica-10
Downloading genesis file for arabica-10 to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for arabica-10 to /Users/rootulp/.celestia-app/config/genesis.json
SHA-256 hash verified for arabica-10

$ ./build/celestia-appd download-genesis foo
Error: unknown chain-id: foo. Must be: celestia, mocha-4, or arabica-10.
```

I verified the chain ID actually differs for each file:

```shell
$ ./build/celestia-appd download-genesis mocha-4 && cat ~/.celestia-app/config/genesis.json | jq ."chain_id"
Downloading genesis file for mocha-4 to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for mocha-4 to /Users/rootulp/.celestia-app/config/genesis.json
SHA-256 hash verified for mocha-4
"mocha-4"

$ ./build/celestia-appd download-genesis celestia && cat ~/.celestia-app/config/genesis.json | jq ."chain_id"
Downloading genesis file for celestia to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for celestia to /Users/rootulp/.celestia-app/config/genesis.json
SHA-256 hash verified for celestia
"celestia"
```

I tampered with the hard-coded Arabica hash and it correctly identified
the mismatch:

```shell
$ ./build/celestia-appd download-genesis arabica-10
Downloading genesis file for arabica-10 to /Users/rootulp/.celestia-app/config/genesis.json
Downloaded genesis file for arabica-10 to /Users/rootulp/.celestia-app/config/genesis.json
Error: sha256 hash mismatch: got fad0a187669f7a2c11bb07f9dc27140d66d2448b7193e186312713856f28e3e1, expected fad0a187669f7a2c11bb07f9dc27140d66d2448b7193e186312713856f28e3e2
```

---------

Co-authored-by: Sanaz Taheri <[email protected]>
  • Loading branch information
rootulp and staheri14 authored Oct 31, 2023
1 parent e6b64a6 commit c34a93d
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
128 changes: 128 additions & 0 deletions cmd/celestia-appd/cmd/download-genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package cmd

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"

"github.com/cosmos/cosmos-sdk/server"
"github.com/spf13/cobra"
)

var chainIDToSha256 = map[string]string{
"celestia": "9727aac9bbfb021ce7fc695a92f901986421283a891b89e0af97bc9fad187793",
"mocha-4": "0846b99099271b240b638a94e17a6301423b5e4047f6558df543d6e91db7e575",
"arabica-10": "fad0a187669f7a2c11bb07f9dc27140d66d2448b7193e186312713856f28e3e1",
}

func downloadGenesisCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "download-genesis [chain-id]",
Short: "Download genesis file from https://github.com/celestiaorg/networks",
Long: "Download genesis file from https://github.com/celestiaorg/networks.\n" +
"The first argument should be a known chain-id. Ex. celestia, mocha-4, or arabica-10.\n" +
"If no argument is provided, defaults to celestia.\n",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
chainID := getChainIDOrDefault(args)
if !isKnownChainID(chainID) {
return fmt.Errorf("unknown chain-id: %s. Must be: celestia, mocha-4, or arabica-10", chainID)
}
outputFile := server.GetServerContextFromCmd(cmd).Config.GenesisFile()
fmt.Printf("Downloading genesis file for %s to %s\n", chainID, outputFile)

url := fmt.Sprintf("https://raw.githubusercontent.com/celestiaorg/networks/master/%s/genesis.json", chainID)
if err := downloadFile(outputFile, url); err != nil {
return fmt.Errorf("error downloading / persisting the genesis file: %s", err)
}
fmt.Printf("Downloaded genesis file for %s to %s\n", chainID, outputFile)

// Compute SHA-256 hash of the downloaded file
hash, err := computeSha256(outputFile)
if err != nil {
return fmt.Errorf("error computing sha256 hash: %s", err)
}

// Compare computed hash against known hash
knownHash, ok := chainIDToSha256[chainID]
if !ok {
return fmt.Errorf("unknown chain-id: %s", chainID)
}

if hash != knownHash {
return fmt.Errorf("sha256 hash mismatch: got %s, expected %s", hash, knownHash)
}

fmt.Printf("SHA-256 hash verified for %s\n", chainID)
return nil
},
}

return cmd
}

// getChainIDOrDefault returns the chainID from the command line arguments. If
// none is provided, defaults to celestia (mainnet).
func getChainIDOrDefault(args []string) string {
if len(args) == 1 {
return args[0]
}
return "celestia"
}

// isKnownChainID returns true if the chainID is known.
func isKnownChainID(chainID string) bool {
knownChainIDs := []string{
"arabica-10", // testnet
"mocha-4", // testnet
"celestia", // mainnet
}
return contains(knownChainIDs, chainID)
}

// contains checks if a string is present in a slice.
func contains(slice []string, s string) bool {
for _, v := range slice {
if v == s {
return true
}
}
return false
}

// downloadFile will download a URL to a local file.
func downloadFile(filepath string, url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()

_, err = io.Copy(out, resp.Body)
return err
}

// computeSha256 computes the SHA-256 hash of a file.
func computeSha256(filepath string) (string, error) {
f, err := os.Open(filepath)
if err != nil {
return "", err
}
defer f.Close()

hasher := sha256.New()
if _, err := io.Copy(hasher, f); err != nil {
return "", err
}

return hex.EncodeToString(hasher.Sum(nil)), nil
}
1 change: 1 addition & 0 deletions cmd/celestia-appd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig encoding.Config) {
config.Cmd(),
commands.CompactGoLevelDBCmd,
addrbookCommand(),
downloadGenesisCommand(),
)

server.AddCommands(rootCmd, app.DefaultNodeHome, NewAppServer, createAppAndExport, addModuleInitFlags)
Expand Down

0 comments on commit c34a93d

Please sign in to comment.