From cdecc655e3a902bd55cfbd677b1933abbdece8dc Mon Sep 17 00:00:00 2001 From: muXxer Date: Wed, 8 May 2024 15:07:09 +0200 Subject: [PATCH] Add more tools --- go.mod | 12 ++- go.sum | 16 ++-- pkg/toolset/benchmark.go | 175 +++++++++++++++++++++++++++++++++++ pkg/toolset/benchmark_cpu.go | 28 ++++++ pkg/toolset/benchmark_io.go | 49 ++++++++++ pkg/toolset/pwd_hash.go | 164 ++++++++++++++++++++++++++++++++ pkg/toolset/toolset.go | 23 +++++ tools/gendoc/go.mod | 8 +- tools/gendoc/go.sum | 16 ++-- 9 files changed, 471 insertions(+), 20 deletions(-) create mode 100644 pkg/toolset/benchmark.go create mode 100644 pkg/toolset/benchmark_cpu.go create mode 100644 pkg/toolset/benchmark_io.go create mode 100644 pkg/toolset/pwd_hash.go diff --git a/go.mod b/go.mod index b9e63ff97..3ec2f60b0 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ replace github.com/goccy/go-graphviz => github.com/alexsporn/go-graphviz v0.0.0- require ( github.com/VictoriaMetrics/fastcache v1.12.2 + github.com/dustin/go-humanize v1.0.1 github.com/fjl/memsize v0.0.2 github.com/goccy/go-graphviz v0.1.2 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -26,6 +27,7 @@ require ( github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240425095808-113b21573349 github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349 github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349 + github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701 github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240425100432-05e1bf8fc089 github.com/iotaledger/iota.go/v4 v4.0.0-20240503105040-c86882e71808 @@ -37,6 +39,7 @@ require ( github.com/multiformats/go-multiaddr v0.12.3 github.com/multiformats/go-varint v0.0.7 github.com/otiai10/copy v1.14.0 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.0 github.com/sajari/regression v1.0.1 github.com/spf13/pflag v1.0.5 @@ -45,7 +48,8 @@ require ( github.com/zyedidia/generic v1.2.1 go.uber.org/atomic v1.11.0 go.uber.org/dig v1.17.1 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 + golang.org/x/term v0.20.0 google.golang.org/grpc v1.63.2 google.golang.org/protobuf v1.33.0 gorm.io/gorm v1.25.9 @@ -63,7 +67,6 @@ require ( github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect github.com/elastic/gosigar v0.14.3 // indirect github.com/ethereum/go-ethereum v1.14.0 // indirect @@ -152,7 +155,6 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.2.1 // indirect github.com/petermattis/goid v0.0.0-20240327183114-c42a807a84ba // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pokt-network/smt v0.10.2 // indirect github.com/polydawn/refmt v0.89.0 // indirect @@ -183,8 +185,8 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect diff --git a/go.sum b/go.sum index 588c3cdc7..44a10e32b 100644 --- a/go.sum +++ b/go.sum @@ -321,6 +321,8 @@ github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349 h1:+bHqh0gC github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349/go.mod h1:nEwIUQIvNMUs2DwM2870Er3foVQTzwPDUtzEKy+evGg= github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349 h1:9cuEF+WvxB/xBLkQu6H3/pHYE5KAqY98oniUFYezvzc= github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349/go.mod h1:O4p7UmsfoeLqtAUwrKbq0lXMxjY/MLQSpZSavvvvGig= +github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349 h1:LrLWPsUcbZjR3kF5NJbuou84l+aCm8dPx4S5pQ/9bJ0= +github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349/go.mod h1:YhGg68kOBM083FzmxjhQD3n/8AeG/4xr4fgM6qq1q9I= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701 h1:YWIaqOp7+DjxG4O6797Uw+K8rPRJN56rbwX6KpWsvEc= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701/go.mod h1:oqSLTV1hlpdLdi0MWt39c+EFgERdPM34ACAYM+4g3Oc= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240425100432-05e1bf8fc089 h1:+NRPSbH6tkop8p+MhjCR9nvs8ng3Oo2yju5FXcDEkBY= @@ -750,8 +752,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= @@ -854,9 +856,11 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -864,8 +868,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/toolset/benchmark.go b/pkg/toolset/benchmark.go new file mode 100644 index 000000000..24d78f33c --- /dev/null +++ b/pkg/toolset/benchmark.go @@ -0,0 +1,175 @@ +package toolset + +import ( + "context" + "fmt" + "os" + "runtime" + "sync" + "time" + + "github.com/dustin/go-humanize" + flag "github.com/spf13/pflag" + + "github.com/iotaledger/hive.go/app/configuration" + "github.com/iotaledger/hive.go/db" + "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/iota-core/pkg/storage/database" + iotago_tpkg "github.com/iotaledger/iota.go/v4/tpkg" +) + +// estimateRemainingTime estimates the remaining time for a running operation and returns the finished percentage. +func estimateRemainingTime(timeStart time.Time, current int64, total int64) (percentage float64, remaining time.Duration) { + ratio := float64(current) / float64(total) + totalTime := time.Duration(float64(time.Since(timeStart)) / ratio) + remaining = time.Until(timeStart.Add(totalTime)) + + return ratio * 100.0, remaining +} + +func benchmarkIO(args []string) error { + fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError) + objectsCountFlag := fs.Int(FlagToolBenchmarkCount, 500000, "objects count") + objectsSizeFlag := fs.Int(FlagToolBenchmarkSize, 1000, "objects size in bytes") + + fs.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolBenchmarkIO) + fs.PrintDefaults() + println(fmt.Sprintf("\nexample: %s --%s %d --%s %d", + ToolBenchmarkIO, + FlagToolBenchmarkCount, + 500000, + FlagToolBenchmarkSize, + 1000)) + } + + if err := parseFlagSet(fs, args); err != nil { + return err + } + + objectCnt := *objectsCountFlag + size := *objectsSizeFlag + + tempDir, err := os.MkdirTemp("", "benchmarkIO") + if err != nil { + return fmt.Errorf("can't create temp dir: %w", err) + } + + defer func() { _ = os.RemoveAll(tempDir) }() + + store, err := database.StoreWithDefaultSettings(tempDir, true, db.EngineRocksDB, db.EngineRocksDB) + if err != nil { + return fmt.Errorf("database initialization failed: %w", err) + } + + batchWriter := kvstore.NewBatchedWriter(store) + writeDoneWaitGroup := &sync.WaitGroup{} + writeDoneWaitGroup.Add(objectCnt) + + ts := time.Now() + + lastStatusTime := time.Now() + for i := 0; i < objectCnt; i++ { + // one read operation and one write operation per cycle + batchWriter.Enqueue(newBenchmarkObject(store, writeDoneWaitGroup, iotago_tpkg.RandBytes(32), iotago_tpkg.RandBytes(size))) + + if time.Since(lastStatusTime) >= printStatusInterval { + lastStatusTime = time.Now() + + duration := time.Since(ts) + bytes := uint64(i * (32 + size)) + totalBytes := uint64(objectCnt * (32 + size)) + bytesPerSecond := uint64(float64(bytes) / duration.Seconds()) + objectsPerSecond := uint64(float64(i) / duration.Seconds()) + percentage, remaining := estimateRemainingTime(ts, int64(i), int64(objectCnt)) + fmt.Printf("Average IO speed: %s/s (%dx 32+%d byte chunks, total %s/%s, %d objects/s, %0.2f%%. %v left ...)\n", humanize.Bytes(bytesPerSecond), i, size, humanize.Bytes(bytes), humanize.Bytes(totalBytes), objectsPerSecond, percentage, remaining.Truncate(time.Second)) + } + } + + writeDoneWaitGroup.Wait() + + if err := store.Flush(); err != nil { + return fmt.Errorf("flush database failed: %w", err) + } + + if err := store.Close(); err != nil { + return fmt.Errorf("close database failed: %w", err) + } + + te := time.Now() + duration := te.Sub(ts) + totalBytes := uint64(objectCnt * (32 + size)) + bytesPerSecond := uint64(float64(totalBytes) / duration.Seconds()) + objectsPerSecond := uint64(float64(objectCnt) / duration.Seconds()) + + fmt.Printf("Average IO speed: %s/s (%dx 32+%d byte chunks, total %s/%s, %d objects/s, took %v)\n", humanize.Bytes(bytesPerSecond), objectCnt, size, humanize.Bytes(totalBytes), humanize.Bytes(totalBytes), objectsPerSecond, duration.Truncate(time.Millisecond)) + + return nil +} + +func benchmarkCPU(args []string) error { + fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError) + cpuThreadsFlag := fs.Int(FlagToolBenchmarkThreads, runtime.NumCPU(), "thread count") + durationFlag := fs.Duration(FlagToolBenchmarkDuration, 1*time.Minute, "duration") + + fs.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolBenchmarkCPU) + fs.PrintDefaults() + println(fmt.Sprintf("\nexample: %s --%s %d --%s 1m0s", + ToolBenchmarkCPU, + FlagToolBenchmarkThreads, + 2, + FlagToolBenchmarkDuration)) + } + + if err := parseFlagSet(fs, args); err != nil { + return err + } + + threads := *cpuThreadsFlag + duration := *durationFlag + + benchmarkCtx, benchmarkCtxCancel := context.WithTimeout(context.Background(), duration) + defer benchmarkCtxCancel() + + ts := time.Now() + + // doBenchmarkCPU mines with blake2b until the context has been canceled. + // it returns the number of calculated hashes. + doBenchmarkCPU := func(ctx context.Context, numWorkers int) uint64 { + var counter uint64 + var wg sync.WaitGroup + + // random digest + powDigest := iotago_tpkg.RandBytes(32) + + go func() { + for ctx.Err() == nil { + time.Sleep(printStatusInterval) + + elapsed := time.Since(ts) + percentage, remaining := estimateRemainingTime(ts, elapsed.Milliseconds(), duration.Milliseconds()) + megahashesPerSecond := float64(counter) / (elapsed.Seconds() * 1000000) + fmt.Printf("Average CPU speed: %0.2fMH/s (%d thread(s), %0.2f%%. %v left ...)\n", megahashesPerSecond, numWorkers, percentage, remaining.Truncate(time.Second)) + } + }() + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + cpuBenchmarkWorker(ctx, powDigest, &counter) + }() + } + wg.Wait() + + return counter + } + + hashes := doBenchmarkCPU(benchmarkCtx, threads) + megahashesPerSecond := float64(hashes) / (duration.Seconds() * 1000000) + fmt.Printf("Average CPU speed: %0.2fMH/s (%d thread(s), took %v)\n", megahashesPerSecond, threads, duration.Truncate(time.Millisecond)) + + return nil +} diff --git a/pkg/toolset/benchmark_cpu.go b/pkg/toolset/benchmark_cpu.go new file mode 100644 index 000000000..b1d0447ef --- /dev/null +++ b/pkg/toolset/benchmark_cpu.go @@ -0,0 +1,28 @@ +package toolset + +import ( + "context" + "crypto" + "sync/atomic" + + "golang.org/x/crypto/blake2b" +) + +const ( + // Hash defines the hash function that is used to compute the PoW digest. + Hash = crypto.BLAKE2b_256 +) + +func cpuBenchmarkWorker(ctx context.Context, powDigest []byte, counter *uint64) { + for { + select { + case <-ctx.Done(): + return + + default: + result := blake2b.Sum256(powDigest) + powDigest = result[:] + atomic.AddUint64(counter, 1) + } + } +} diff --git a/pkg/toolset/benchmark_io.go b/pkg/toolset/benchmark_io.go new file mode 100644 index 000000000..f1b7ed5db --- /dev/null +++ b/pkg/toolset/benchmark_io.go @@ -0,0 +1,49 @@ +package toolset + +import ( + "fmt" + "sync" + + "github.com/iotaledger/hive.go/kvstore" + iotago_tpkg "github.com/iotaledger/iota.go/v4/tpkg" +) + +type benchmarkObject struct { + store kvstore.KVStore + writeDoneWaitGroup *sync.WaitGroup + key []byte + value []byte +} + +func newBenchmarkObject(store kvstore.KVStore, writeDoneWaitGroup *sync.WaitGroup, key []byte, value []byte) *benchmarkObject { + return &benchmarkObject{ + store: store, + writeDoneWaitGroup: writeDoneWaitGroup, + key: key, + value: value, + } +} + +func (bo *benchmarkObject) BatchWrite(batchedMuts kvstore.BatchedMutations) { + if err := batchedMuts.Set(bo.key, bo.value); err != nil { + panic(fmt.Errorf("write operation failed: %w", err)) + } +} + +func (bo *benchmarkObject) BatchWriteDone() { + // do a read operation after the batchwrite is done, + // so the write and read operations are equally distributed over the whole benchmark run. + if _, err := bo.store.Has(iotago_tpkg.RandBytes(32)); err != nil { + panic(fmt.Errorf("read operation failed: %w", err)) + } + + bo.writeDoneWaitGroup.Done() +} + +func (bo *benchmarkObject) BatchWriteScheduled() bool { + return false +} + +func (bo *benchmarkObject) ResetBatchWriteScheduled() { + // do nothing +} diff --git a/pkg/toolset/pwd_hash.go b/pkg/toolset/pwd_hash.go new file mode 100644 index 000000000..140e1699c --- /dev/null +++ b/pkg/toolset/pwd_hash.go @@ -0,0 +1,164 @@ +package toolset + +import ( + "bytes" + "encoding/hex" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/pkg/errors" + flag "github.com/spf13/pflag" + "golang.org/x/term" + + "github.com/iotaledger/hive.go/app/configuration" + "github.com/iotaledger/hive.go/web/basicauth" +) + +func readPasswordFromEnv() ([]byte, error) { + passwordEnv, exists := os.LookupEnv(passwordEnvKey) + if !exists { + return []byte{}, fmt.Errorf("environment variable '%s' not set", passwordEnvKey) + } + + if len(passwordEnv) == 0 { + return []byte{}, fmt.Errorf("environment variable '%s' not set", passwordEnvKey) + } + + return []byte(passwordEnv), nil +} + +func readPasswordFromStdin() ([]byte, error) { + var password []byte + + // get terminal state to be able to restore it in case of an interrupt + originalTerminalState, err := term.GetState(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows + if err != nil { + return nil, errors.New("failed to get terminal state") + } + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt) + go func() { + <-signalChan + // reset the terminal to the original state if we receive an interrupt + _ = term.Restore(int(syscall.Stdin), originalTerminalState) //nolint:nolintlint,unconvert // int cast is needed for windows + fmt.Println("\naborted ... Bye!") + os.Exit(1) + }() + + fmt.Print("Enter a password: ") + password, err = term.ReadPassword(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows + if err != nil { + return nil, fmt.Errorf("read password failed: %w", err) + } + + fmt.Print("\nRe-enter your password: ") + passwordReenter, err := term.ReadPassword(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows + if err != nil { + return nil, fmt.Errorf("read password failed: %w", err) + } + + if !bytes.Equal(password, passwordReenter) { + return nil, errors.New("re-entered password doesn't match") + } + fmt.Println() + + return password, nil +} + +func hashPasswordAndSalt(args []string) error { + fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError) + saltFlag := fs.String(FlagToolSalt, "", "salt used to hash the password (optional)") + passwordFlag := fs.String(FlagToolPassword, "", fmt.Sprintf("password to hash. Can also be passed as %s environment variable.", passwordEnvKey)) + outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON) + + fs.Usage = func() { + _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolPwdHash) + fs.PrintDefaults() + println(fmt.Sprintf("\nexample: %s --%s %s", + ToolPwdHash, + FlagToolPassword, + "[PASSWORD]", + )) + } + + if err := parseFlagSet(fs, args); err != nil { + return err + } + + var err error + var passwordSalt []byte + if len(*saltFlag) > 0 { + // Salt passed over flag + if len(*saltFlag) != 64 { + return errors.New("the given salt must be 64 (hex encoded) in length") + } + passwordSalt, err = hex.DecodeString(*saltFlag) + if err != nil { + return fmt.Errorf("parsing given salt failed: %w", err) + } + } else { + passwordSalt, err = basicauth.SaltGenerator(32) + if err != nil { + return fmt.Errorf("generating random salt failed: %w", err) + } + } + + var password []byte + if p, err := readPasswordFromEnv(); err == nil { + // Password passed over the environment + password = p + } else if len(*passwordFlag) > 0 { + // Password passed over flag + password = []byte(*passwordFlag) + } else { + // Read from stdin + p, err := readPasswordFromStdin() + if err != nil { + return err + } + password = p + } + + passwordKey, err := basicauth.DerivePasswordKey(password, passwordSalt) + if err != nil { + return fmt.Errorf("deriving password key failed: %w", err) + } + + if *outputJSONFlag { + + result := struct { + Password string `json:"passwordHash"` + Salt string `json:"passwordSalt"` + }{ + Password: hex.EncodeToString(passwordKey), + Salt: hex.EncodeToString(passwordSalt), + } + + return printJSON(result) + } + + fmt.Printf(` +Success! + +Please add the following lines to your inx-dashboard "config.json" file: + +{ + "auth": { + "passwordHash": "%x", + "passwordSalt": "%x" + } +} + + +You can also set the following environment variables in your node-docker-setup ".env" file: + +DASHBOARD_PASSWORD=%x +DASHBOARD_SALT=%x + +`, passwordKey, passwordSalt, passwordKey, passwordSalt) + + return nil +} diff --git a/pkg/toolset/toolset.go b/pkg/toolset/toolset.go index 507373445..a60d13edc 100644 --- a/pkg/toolset/toolset.go +++ b/pkg/toolset/toolset.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" flag "github.com/spf13/pflag" @@ -22,20 +23,29 @@ const ( FlagToolHRP = "hrp" FlagToolBIP32Path = "bip32Path" FlagToolMnemonic = "mnemonic" + FlagToolPassword = "password" FlagToolSalt = "salt" FlagToolNodeURL = "nodeURL" FlagToolOutputJSON = "json" FlagToolDescriptionOutputJSON = "format output as JSON" + + FlagToolBenchmarkCount = "count" + FlagToolBenchmarkSize = "size" + FlagToolBenchmarkThreads = "threads" + FlagToolBenchmarkDuration = "duration" ) const ( + ToolPwdHash = "pwd-hash" ToolP2PIdentityGen = "p2pidentity-gen" ToolP2PExtractIdentity = "p2pidentity-extract" ToolEd25519Key = "ed25519-key" ToolEd25519Addr = "ed25519-addr" ToolJWTApi = "jwt-api" + ToolBenchmarkIO = "bench-io" + ToolBenchmarkCPU = "bench-cpu" ToolNodeInfo = "node-info" ) @@ -44,6 +54,13 @@ const ( DefaultValueIdentityPrivateKeyFilePath = "testnet/p2p/identity.key" ) +const ( + passwordEnvKey = "IOTA_CORE_TOOL_PASSWORD" + + // printStatusInterval is the interval for printing status messages. + printStatusInterval = 2 * time.Second +) + // ShouldHandleTools checks if tools were requested. func ShouldHandleTools() bool { args := os.Args[1:] @@ -66,11 +83,14 @@ func HandleTools() { } tools := map[string]func([]string) error{ + ToolPwdHash: hashPasswordAndSalt, ToolP2PIdentityGen: generateP2PIdentity, ToolP2PExtractIdentity: extractP2PIdentity, ToolEd25519Key: generateEd25519Key, ToolEd25519Addr: generateEd25519Address, ToolJWTApi: generateJWTApiToken, + ToolBenchmarkIO: benchmarkIO, + ToolBenchmarkCPU: benchmarkCPU, ToolNodeInfo: nodeInfo, } @@ -95,11 +115,14 @@ func HandleTools() { } func listTools() { + fmt.Printf("%-20s generates a scrypt hash from your password and salt\n", fmt.Sprintf("%s:", ToolPwdHash)) fmt.Printf("%-20s generates a p2p identity private key file\n", fmt.Sprintf("%s:", ToolP2PIdentityGen)) fmt.Printf("%-20s extracts the p2p identity from the private key file\n", fmt.Sprintf("%s:", ToolP2PExtractIdentity)) fmt.Printf("%-20s generates an ed25519 key pair\n", fmt.Sprintf("%s:", ToolEd25519Key)) fmt.Printf("%-20s generates an ed25519 address from a public key\n", fmt.Sprintf("%s:", ToolEd25519Addr)) fmt.Printf("%-20s generates a JWT token for REST-API access\n", fmt.Sprintf("%s:", ToolJWTApi)) + fmt.Printf("%-20s benchmarks the IO throughput\n", fmt.Sprintf("%s:", ToolBenchmarkIO)) + fmt.Printf("%-20s benchmarks the CPU performance\n", fmt.Sprintf("%s:", ToolBenchmarkCPU)) fmt.Printf("%-20s queries the info endpoint of a node\n", fmt.Sprintf("%s:", ToolNodeInfo)) } diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 125f00667..04199a2e1 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -71,6 +71,7 @@ require ( github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240425095808-113b21573349 // indirect github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349 // indirect github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349 // indirect + github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349 // indirect github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701 // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240425100432-05e1bf8fc089 // indirect github.com/iotaledger/iota-crypto-demo v0.0.0-20240419094816-40260bb800f7 // indirect @@ -172,14 +173,15 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/image v0.15.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index 623804d16..ab7ce78c7 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -325,6 +325,8 @@ github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349 h1:+bHqh0gC github.com/iotaledger/hive.go/sql v0.0.0-20240425095808-113b21573349/go.mod h1:nEwIUQIvNMUs2DwM2870Er3foVQTzwPDUtzEKy+evGg= github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349 h1:9cuEF+WvxB/xBLkQu6H3/pHYE5KAqY98oniUFYezvzc= github.com/iotaledger/hive.go/stringify v0.0.0-20240425095808-113b21573349/go.mod h1:O4p7UmsfoeLqtAUwrKbq0lXMxjY/MLQSpZSavvvvGig= +github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349 h1:LrLWPsUcbZjR3kF5NJbuou84l+aCm8dPx4S5pQ/9bJ0= +github.com/iotaledger/hive.go/web v0.0.0-20240425095808-113b21573349/go.mod h1:YhGg68kOBM083FzmxjhQD3n/8AeG/4xr4fgM6qq1q9I= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701 h1:YWIaqOp7+DjxG4O6797Uw+K8rPRJN56rbwX6KpWsvEc= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240425100742-5c85b6d16701/go.mod h1:oqSLTV1hlpdLdi0MWt39c+EFgERdPM34ACAYM+4g3Oc= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240425100432-05e1bf8fc089 h1:+NRPSbH6tkop8p+MhjCR9nvs8ng3Oo2yju5FXcDEkBY= @@ -752,8 +754,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= @@ -856,9 +858,11 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -866,8 +870,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=