Skip to content

Commit

Permalink
Merge pull request #469 from iotaledger/feat/toolset
Browse files Browse the repository at this point in the history
Port toolset from HORNET and enable Docker healthcheck
  • Loading branch information
muXxer authored Oct 26, 2023
2 parents 81f828b + 4578d11 commit b707ff1
Show file tree
Hide file tree
Showing 18 changed files with 737 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ RUN cp ./peering.json /app/peering.json
# using distroless cc "nonroot" image, which includes everything in the base image (glibc, libssl and openssl)
FROM gcr.io/distroless/cc-debian12:nonroot

HEALTHCHECK --interval=10s --timeout=5s --retries=30 CMD ["/app/iota-core", "tools", "node-info"]

# Copy the app dir into distroless image
COPY --chown=nonroot:nonroot --from=build /app /app

Expand Down
2 changes: 2 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ RUN mkdir -p /app/data/peerdb
# using distroless cc "nonroot" image, which includes everything in the base image (glibc, libssl and openssl)
FROM gcr.io/distroless/cc-debian12:nonroot

HEALTHCHECK --interval=10s --timeout=5s --retries=30 CMD ["/app/iota-core", "tools", "node-info"]

# Copy the app dir into distroless image
COPY --chown=nonroot:nonroot --from=build /app /app

Expand Down
20 changes: 20 additions & 0 deletions components/app/app.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package app

import (
"fmt"
"os"

"github.com/iotaledger/hive.go/app"
"github.com/iotaledger/hive.go/app/components/profiling"
"github.com/iotaledger/hive.go/app/components/shutdown"
Expand All @@ -15,6 +18,7 @@ import (
"github.com/iotaledger/iota-core/components/restapi"
coreapi "github.com/iotaledger/iota-core/components/restapi/core"
"github.com/iotaledger/iota-core/components/validator"
"github.com/iotaledger/iota-core/pkg/toolset"
)

var (
Expand All @@ -28,6 +32,12 @@ var (
func App() *app.App {
return app.New(Name, Version,
// app.WithVersionCheck("iotaledger", "iota-core"),
app.WithUsageText(fmt.Sprintf(`Usage of %s (%s %s):
Run '%s tools' to list all available tools.
Command line flags:
`, os.Args[0], Name, Version, os.Args[0])),
app.WithInitComponent(InitComponent),
app.WithComponents(
shutdown.Component,
Expand Down Expand Up @@ -63,5 +73,15 @@ func init() {
AdditionalConfigs: []*app.ConfigurationSet{
app.NewConfigurationSet("peering", "peering", "peeringConfigFilePath", "peeringConfig", false, true, false, "peering.json", "n"),
},
Init: initialize,
}
}

func initialize(_ *app.App) error {
if toolset.ShouldHandleTools() {
toolset.HandleTools()
// HandleTools will call os.Exit
}

return nil
}
2 changes: 1 addition & 1 deletion components/p2p/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func provide(c *dig.Container) error {
if err := c.Provide(func(deps p2pDeps) p2pResult {
res := p2pResult{}

privKeyFilePath := filepath.Join(deps.P2PDatabasePath, "identity.key")
privKeyFilePath := filepath.Join(deps.P2PDatabasePath, IdentityPrivateKeyFileName)

// make sure nobody copies around the peer store since it contains the private key of the node
Component.LogInfof(`WARNING: never share your "%s" folder as it contains your node's private key!`, deps.P2PDatabasePath)
Expand Down
3 changes: 2 additions & 1 deletion components/p2p/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (

const (
// CfgPeers defines the static peers this node should retain a connection to (CLI).
CfgPeers = "peers"
CfgPeers = "peers"
IdentityPrivateKeyFileName = "identity.key"
)

// ParametersP2P contains the definition of configuration parameters used by the p2p plugin.
Expand Down
2 changes: 1 addition & 1 deletion components/restapi/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type ParametersRestAPI struct {
// Enabled defines whether the REST API plugin is enabled.
Enabled bool `default:"true" usage:"whether the REST API plugin is enabled"`
// the bind address on which the REST API listens on
BindAddress string `default:"0.0.0.0:8080" usage:"the bind address on which the REST API listens on"`
BindAddress string `default:"0.0.0.0:14265" usage:"the bind address on which the REST API listens on"`
// the HTTP REST routes which can be called without authorization. Wildcards using * are allowed
PublicRoutes []string `usage:"the HTTP REST routes which can be called without authorization. Wildcards using * are allowed"`
// the HTTP REST routes which need to be called with authorization. Wildcards using * are allowed
Expand Down
2 changes: 1 addition & 1 deletion config_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
},
"restAPI": {
"enabled": true,
"bindAddress": "0.0.0.0:8080",
"bindAddress": "0.0.0.0:14265",
"publicRoutes": [
"/health",
"/api/routes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ services:
ports:
- "14666:14666/tcp" # P2P
- "6061:6061/tcp" # pprof
- "8080:8080/tcp" # REST-API
- "8080:14265/tcp" # REST-API
- "8081:8081/tcp" # Dashboard
- "9311:9311/tcp" # Prometheus
- "9029:9029/tcp" # INX
Expand All @@ -46,7 +46,7 @@ services:
--p2p.db.path=/app/data/peerdb
--profiling.enabled=true
--profiling.bindAddress=0.0.0.0:6061
--restAPI.bindAddress=0.0.0.0:8080
--restAPI.bindAddress=0.0.0.0:14265
--database.path=/app/data/database
--protocol.snapshot.path=/app/data/snapshot.bin
{% if 'node-01' in inventory_hostname or 'node-02' in inventory_hostname or 'node-03' in inventory_hostname %}
Expand All @@ -73,7 +73,7 @@ services:
restart: unless-stopped
depends_on:
iota-core:
condition: service_started
condition: service_healthy
ulimits:
nofile:
soft: 16384
Expand All @@ -93,7 +93,7 @@ services:
restart: unless-stopped
depends_on:
iota-core:
condition: service_started
condition: service_healthy
inx-indexer:
condition: service_started
environment:
Expand All @@ -111,7 +111,7 @@ services:
restart: unless-stopped
depends_on:
iota-core:
condition: service_started
condition: service_healthy
inx-indexer:
condition: service_started
inx-blockissuer:
Expand Down
4 changes: 2 additions & 2 deletions documentation/docs/references/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Example:
| Name | Description | Type | Default value |
| ------------------------------ | ---------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enabled | Whether the REST API plugin is enabled | boolean | true |
| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:8080" |
| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:14265" |
| publicRoutes | The HTTP REST routes which can be called without authorization. Wildcards using \* are allowed | array | /health<br/>/api/routes<br/>/api/core/v3/info<br/>/api/core/v3/blocks\*<br/>/api/core/v3/transactions\*<br/>/api/core/v3/commitments\*<br/>/api/core/v3/outputs\*<br/>/api/core/v3/accounts\*<br/>/api/core/v3/validators\*<br/>/api/core/v3/rewards\*<br/>/api/core/v3/committee<br/>/api/debug/v2/\*<br/>/api/indexer/v2/\*<br/>/api/mqtt/v2 |
| protectedRoutes | The HTTP REST routes which need to be called with authorization. Wildcards using \* are allowed | array | /api/\* |
| debugRequestLoggerEnabled | Whether the debug logging for requests should be enabled | boolean | false |
Expand Down Expand Up @@ -204,7 +204,7 @@ Example:
{
"restAPI": {
"enabled": true,
"bindAddress": "0.0.0.0:8080",
"bindAddress": "0.0.0.0:14265",
"publicRoutes": [
"/health",
"/api/routes",
Expand Down
172 changes: 172 additions & 0 deletions pkg/toolset/ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package toolset

import (
"crypto/ed25519"
"encoding/hex"
"fmt"
"os"

flag "github.com/spf13/pflag"
"github.com/wollac/iota-crypto-demo/pkg/bip32path"
"github.com/wollac/iota-crypto-demo/pkg/bip39"
"github.com/wollac/iota-crypto-demo/pkg/slip10"
"github.com/wollac/iota-crypto-demo/pkg/slip10/eddsa"

"github.com/iotaledger/hive.go/app/configuration"
"github.com/iotaledger/hive.go/crypto"
iotago "github.com/iotaledger/iota.go/v4"
)

func printEd25519Info(mnemonic bip39.Mnemonic, path bip32path.Path, prvKey ed25519.PrivateKey, pubKey ed25519.PublicKey, hrp iotago.NetworkPrefix, outputJSON bool) error {
addr := iotago.Ed25519AddressFromPubKey(pubKey)

type keys struct {
BIP39 string `json:"mnemonic,omitempty"`
BIP32 string `json:"path,omitempty"`
PrivateKey string `json:"privateKey,omitempty"`
PublicKey string `json:"publicKey"`
Ed25519Address string `json:"ed25519"`
Bech32Address string `json:"bech32"`
}

k := keys{
PublicKey: hex.EncodeToString(pubKey),
Ed25519Address: hex.EncodeToString(addr[:]),
Bech32Address: addr.Bech32(hrp),
}

if prvKey != nil {
k.PrivateKey = hex.EncodeToString(prvKey)
}

if mnemonic != nil {
k.BIP39 = mnemonic.String()
k.BIP32 = path.String()
}

if outputJSON {
return printJSON(k)
}

if len(k.BIP39) > 0 {
fmt.Println("Your seed BIP39 mnemonic: ", k.BIP39)
fmt.Println()
fmt.Println("Your BIP32 path: ", k.BIP32)
}

if k.PrivateKey != "" {
fmt.Println("Your ed25519 private key: ", k.PrivateKey)
}

fmt.Println("Your ed25519 public key: ", k.PublicKey)
fmt.Println("Your ed25519 address: ", k.Ed25519Address)
fmt.Println("Your bech32 address: ", k.Bech32Address)

return nil
}

func generateEd25519Key(args []string) error {
fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
hrpFlag := fs.String(FlagToolHRP, string(iotago.PrefixTestnet), "the HRP which should be used for the Bech32 address")
bip32Path := fs.String(FlagToolBIP32Path, "m/44'/4218'/0'/0'/0'", "the BIP32 path that should be used to derive keys from seed")
mnemonicFlag := fs.String(FlagToolMnemonic, "", "the BIP-39 mnemonic sentence that should be used to derive the seed from (optional)")
outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)

fs.Usage = func() {
_, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolEd25519Key)
fs.PrintDefaults()
println(fmt.Sprintf("\nexample: %s --%s %s",
ToolEd25519Key,
FlagToolHRP,
string(iotago.PrefixTestnet)))
}

if err := parseFlagSet(fs, args); err != nil {
return err
}

if len(*hrpFlag) == 0 {
return fmt.Errorf("'%s' not specified", FlagToolHRP)
}

if len(*bip32Path) == 0 {
return fmt.Errorf("'%s' not specified", FlagToolBIP32Path)
}

var mnemonicSentence bip39.Mnemonic
if len(*mnemonicFlag) == 0 {
// Generate random entropy by using ed25519 key generation and using the private key seed (32 bytes)
_, random, err := ed25519.GenerateKey(nil)
if err != nil {
return err
}
entropy := random.Seed()

mnemonicSentence, err = bip39.EntropyToMnemonic(entropy)
if err != nil {
return err
}
} else {
mnemonicSentence = bip39.ParseMnemonic(*mnemonicFlag)
if len(mnemonicSentence) != 24 {
return fmt.Errorf("'%s' contains an invalid sentence length. Mnemonic should be 24 words", FlagToolMnemonic)
}
}

path, err := bip32path.ParsePath(*bip32Path)
if err != nil {
return err
}

seed, err := bip39.MnemonicToSeed(mnemonicSentence, "")
if err != nil {
return err
}

key, err := slip10.DeriveKeyFromPath(seed, eddsa.Ed25519(), path)
if err != nil {
return err
}
pubKey, prvKey := key.Key.(eddsa.Seed).Ed25519Key()

return printEd25519Info(mnemonicSentence, path, ed25519.PrivateKey(prvKey), ed25519.PublicKey(pubKey), iotago.NetworkPrefix(*hrpFlag), *outputJSONFlag)
}

func generateEd25519Address(args []string) error {
fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
hrpFlag := fs.String(FlagToolHRP, string(iotago.PrefixTestnet), "the HRP which should be used for the Bech32 address")
publicKeyFlag := fs.String(FlagToolPublicKey, "", "an ed25519 public key")
outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)

fs.Usage = func() {
_, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolEd25519Addr)
fs.PrintDefaults()
println(fmt.Sprintf("\nexample: %s --%s %s --%s %s",
ToolEd25519Addr,
FlagToolHRP,
string(iotago.PrefixTestnet),
FlagToolPublicKey,
"[PUB_KEY]",
))
}

if err := parseFlagSet(fs, args); err != nil {
return err
}

if len(*hrpFlag) == 0 {
return fmt.Errorf("'%s' not specified", FlagToolHRP)
}

if len(*publicKeyFlag) == 0 {
return fmt.Errorf("'%s' not specified", FlagToolPublicKey)
}

// parse pubkey
pubKey, err := crypto.ParseEd25519PublicKeyFromString(*publicKeyFlag)
if err != nil {
return fmt.Errorf("can't decode '%s': %w", FlagToolPublicKey, err)
}

return printEd25519Info(nil, nil, nil, pubKey, iotago.NetworkPrefix(*hrpFlag), *outputJSONFlag)
}
Loading

0 comments on commit b707ff1

Please sign in to comment.