Skip to content

Commit

Permalink
adm: Allow generation more than one SN wallet per call
Browse files Browse the repository at this point in the history
Add `wallets-number` flag that "duplicates" requests to the `neofs-adm`.
Passwords and labels are the same for all the wallets being created. Names
differ with the suffix-numbers. Closes #2425.

Signed-off-by: Pavel Karpy <[email protected]>
  • Loading branch information
carpawell committed Dec 19, 2023
1 parent c1b8cac commit 419ce54
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 63 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog for NeoFS Node
## [Unreleased]

### Added
- `neofs-adm morph generate-storage-wallet` now supports more than one wallet generation per call (#2425)

### Fixed
- Fund transfer deadlock in NeoFS chain auto-deploy/update procedure (#2681)
Expand Down
145 changes: 84 additions & 61 deletions cmd/neofs-adm/internal/modules/morph/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package morph
import (
"errors"
"fmt"
"math/big"
"os"
"path"
"path/filepath"
"strings"

"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-node/cmd/neofs-adm/internal/modules/config"
"github.com/nspcc-dev/neofs-node/pkg/util/glagolitsa"
Expand Down Expand Up @@ -128,81 +127,67 @@ func addMultisigAccount(w *wallet.Wallet, m int, name, password string, pubs key
}

func generateStorageCreds(cmd *cobra.Command, _ []string) error {
return refillGas(cmd, storageGasConfigFlag, true)
}

func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error) {
// storage wallet path is not part of the config
storageWalletPath, _ := cmd.Flags().GetString(storageWalletFlag)
// wallet address is not part of the config
walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)

var gasReceiver util.Uint160

if len(walletAddress) != 0 {
gasReceiver, err = address.StringToUint160(walletAddress)
if err != nil {
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
}
} else {
if storageWalletPath == "" {
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
}

var w *wallet.Wallet

if createWallet {
w, err = wallet.NewWallet(storageWalletPath)
} else {
w, err = wallet.NewWalletFromFile(storageWalletPath)
}

if err != nil {
return fmt.Errorf("can't create wallet: %w", err)
}

if createWallet {
var password string

label, _ := cmd.Flags().GetString(storageWalletLabelFlag)
password, err := config.GetStoragePassword(viper.GetViper(), label)
if err != nil {
return fmt.Errorf("can't fetch password: %w", err)
}
if storageWalletPath == "" {
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)

Check warning on line 133 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L132-L133

Added lines #L132 - L133 were not covered by tests
}

if label == "" {
label = singleAccountName
}
walletsNumber, err := cmd.Flags().GetUint32(storageWalletsNumber)
if err != nil {
return err

Check warning on line 138 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L136-L138

Added lines #L136 - L138 were not covered by tests
}
if walletsNumber == 0 {
walletsNumber = 1

Check warning on line 141 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L140-L141

Added lines #L140 - L141 were not covered by tests
}

if err := w.CreateAccount(label, password); err != nil {
return fmt.Errorf("can't create account: %w", err)
}
}
label, _ := cmd.Flags().GetString(storageWalletLabelFlag)
password, err := config.GetStoragePassword(viper.GetViper(), label)
if err != nil {
return fmt.Errorf("can't fetch password: %w", err)

Check warning on line 147 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L144-L147

Added lines #L144 - L147 were not covered by tests
}

gasReceiver = w.Accounts[0].Contract.ScriptHash()
if label == "" {
label = singleAccountName

Check warning on line 151 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L150-L151

Added lines #L150 - L151 were not covered by tests
}

gasStr := viper.GetString(gasFlag)
hashes, err := createWallets(storageWalletPath, label, password, walletsNumber)
if err != nil {
return err

Check warning on line 156 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L154-L156

Added lines #L154 - L156 were not covered by tests
}

gasAmount, err := parseGASAmount(gasStr)
gasAmount, err := parseGASAmount(viper.GetString(refillGasAmountFlag))

Check warning on line 159 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L159

Added line #L159 was not covered by tests
if err != nil {
return err
}

return refillGas(cmd, int64(gasAmount), hashes)

Check warning on line 164 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L164

Added line #L164 was not covered by tests
}

func refillGas(cmd *cobra.Command, gasAmount int64, receivers []util.Uint160) (err error) {

Check warning on line 167 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L167

Added line #L167 was not covered by tests
wCtx, err := newInitializeContext(cmd, viper.GetViper())
if err != nil {
return err
}

bw := io.NewBufBinWriter()
emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All,
wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil)
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
if bw.Err != nil {
return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err)
committeeScriptHash := wCtx.CommitteeAcc.Contract.ScriptHash()

Check warning on line 173 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L173

Added line #L173 was not covered by tests

var pp []nep17.TransferParameters
for _, receiver := range receivers {
pp = append(pp, nep17.TransferParameters{
From: committeeScriptHash,
To: receiver,
Amount: big.NewInt(gasAmount),
})

Check warning on line 181 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L175-L181

Added lines #L175 - L181 were not covered by tests
}

if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil {
gToken := nep17.New(wCtx.CommitteeAct, gas.Hash)
tx, err := gToken.MultiTransferUnsigned(pp)
if err != nil {
return err

Check warning on line 187 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L184-L187

Added lines #L184 - L187 were not covered by tests
}

if err := wCtx.multiSignAndSend(tx, committeeAccountName); err != nil {

Check warning on line 190 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L190

Added line #L190 was not covered by tests
return err
}

Expand All @@ -219,3 +204,41 @@ func parseGASAmount(s string) (fixedn.Fixed8, error) {
}
return gasAmount, nil
}

func createWallets(fileNameTemplate, label, password string, number uint32) ([]util.Uint160, error) {
var res []util.Uint160
ext := path.Ext(fileNameTemplate)
base := strings.TrimSuffix(fileNameTemplate, ext)
walletNumberFormat := fmt.Sprintf("%%0%dd", digitsNum(number))

for i := 0; i < int(number); i++ {
filename := fileNameTemplate
if number != 1 {
filename = base + "_" + fmt.Sprintf(walletNumberFormat, i) + ext
}

w, err := wallet.NewWallet(filename)
if err != nil {
return nil, fmt.Errorf("wallet creation: %w", err)

Check warning on line 222 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L222

Added line #L222 was not covered by tests
}

err = w.CreateAccount(label, password)
if err != nil {
return nil, fmt.Errorf("account creation: %w", err)

Check warning on line 227 in cmd/neofs-adm/internal/modules/morph/generate.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/generate.go#L227

Added line #L227 was not covered by tests
}

res = append(res, w.Accounts[0].Contract.ScriptHash())
}

return res, nil
}

func digitsNum(val uint32) int {
var res int
for val != 0 {
val /= 10
res += 1

Check warning on line 240 in cmd/neofs-adm/internal/modules/morph/generate.go

View workflow job for this annotation

GitHub Actions / lint

increment-decrement: should replace res += 1 with res++ (revive)
}

return res
}
33 changes: 33 additions & 0 deletions cmd/neofs-adm/internal/modules/morph/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package morph

import (
"bytes"
"fmt"
"io"
"math/rand"
"os"
"path"
"path/filepath"
"strconv"
"testing"
Expand Down Expand Up @@ -90,3 +92,34 @@ func setupTestTerminal(t *testing.T) *bytes.Buffer {

return in
}

func TestCreateWallets(t *testing.T) {
dir := t.TempDir()

const label = "label"
const password = "pass"

hashes, err := createWallets(dir+"/test.json", label, password, 11)
require.NoError(t, err)

entries, err := os.ReadDir(dir)
require.NoError(t, err)

for i, entry := range entries {
if i < 10 {
require.Equal(t, fmt.Sprintf("test_0%d.json", i), entry.Name())
} else {
require.Equal(t, fmt.Sprintf("test_%d.json", i), entry.Name())
}
}

for i, entry := range entries {
w, err := wallet.NewWalletFromFile(path.Join(dir, entry.Name()))
require.NoError(t, err)

acc := w.GetAccount(hashes[i])
require.NoError(t, acc.Decrypt(password, keys.NEP2ScryptParams()))

require.Equal(t, label, acc.Label)
}
}
42 changes: 40 additions & 2 deletions cmd/neofs-adm/internal/modules/morph/root.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package morph

import (
"fmt"

"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -11,6 +16,7 @@ const (
endpointFlag = "rpc-endpoint"
storageWalletFlag = "storage-wallet"
storageWalletLabelFlag = "label"
storageWalletsNumber = "wallets-number"
storageGasCLIFlag = "initial-gas"
storageGasConfigFlag = "storage.initial_gas"
contractsInitFlag = "contracts"
Expand Down Expand Up @@ -106,7 +112,38 @@ var (
_ = viper.BindPFlag(refillGasAmountFlag, cmd.Flags().Lookup(refillGasAmountFlag))
},
RunE: func(cmd *cobra.Command, args []string) error {
return refillGas(cmd, refillGasAmountFlag, false)
var gasReceiver util.Uint160
var err error

Check warning on line 116 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L115-L116

Added lines #L115 - L116 were not covered by tests

// wallet address is not part of the config
walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)

Check warning on line 119 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L119

Added line #L119 was not covered by tests

if len(walletAddress) != 0 {
gasReceiver, err = address.StringToUint160(walletAddress)
if err != nil {
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)

Check warning on line 124 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L121-L124

Added lines #L121 - L124 were not covered by tests
}
} else {

Check warning on line 126 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L126

Added line #L126 was not covered by tests
// storage wallet path is not part of the config
storageWalletPath, _ := cmd.Flags().GetString(storageWalletFlag)
if storageWalletPath == "" {
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)

Check warning on line 130 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L128-L130

Added lines #L128 - L130 were not covered by tests
}

w, err := wallet.NewWallet(storageWalletPath)
if err != nil {
return fmt.Errorf("can't open wallet: %w", err)

Check warning on line 135 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L133-L135

Added lines #L133 - L135 were not covered by tests
}

gasReceiver = w.Accounts[0].Contract.ScriptHash()

Check warning on line 138 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L138

Added line #L138 was not covered by tests
}

gasAmount, err := parseGASAmount(viper.GetString(refillGasAmountFlag))
if err != nil {
return err

Check warning on line 143 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L141-L143

Added lines #L141 - L143 were not covered by tests
}

return refillGas(cmd, int64(gasAmount), []util.Uint160{gasReceiver})

Check warning on line 146 in cmd/neofs-adm/internal/modules/morph/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-adm/internal/modules/morph/root.go#L146

Added line #L146 was not covered by tests
},
}

Expand Down Expand Up @@ -319,8 +356,9 @@ func init() {
RootCmd.AddCommand(generateStorageCmd)
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet")
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet(s)")
generateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
generateStorageCmd.Flags().Uint32(storageWalletsNumber, 1, "Number of wallets to generate, if more than 1, suffix-number will be added to the filename")
generateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")

RootCmd.AddCommand(forceNewEpoch)
Expand Down

0 comments on commit 419ce54

Please sign in to comment.