forked from TrueCloudLab/frostfs-node
[#932] adm: Move InitializeContext
to util
package
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
b68f7be0b6
commit
77694a2f3b
29 changed files with 809 additions and 794 deletions
|
@ -85,7 +85,7 @@ func setConfigCmd(cmd *cobra.Command, args []string) error {
|
||||||
return errors.New("empty config pairs")
|
return errors.New("empty config pairs")
|
||||||
}
|
}
|
||||||
|
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't initialize context: %w", err)
|
return fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("invalid filename: %w", err)
|
return fmt.Errorf("invalid filename: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := morphUtil.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return wCtx.AwaitTx()
|
return wCtx.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *InitializeContext, ch util.Uint160) error {
|
func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *morphUtil.InitializeContext, ch util.Uint160) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
for _, cnt := range containers {
|
for _, cnt := range containers {
|
||||||
hv := hash.Sha256(cnt.Value)
|
hv := hash.Sha256(cnt.Value)
|
||||||
|
@ -262,7 +262,7 @@ func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContainerRestored(cmd *cobra.Command, wCtx *InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
func isContainerRestored(cmd *cobra.Command, wCtx *morphUtil.InitializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
||||||
emit.AppCall(bw.BinWriter, containerHash, "get", callflag.All, hashValue.BytesBE())
|
emit.AppCall(bw.BinWriter, containerHash, "get", callflag.All, hashValue.BytesBE())
|
||||||
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -300,7 +300,7 @@ func parseContainers(filename string) ([]Container, error) {
|
||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchContainerContractHash(wCtx *InitializeContext) (util.Uint160, error) {
|
func fetchContainerContractHash(wCtx *morphUtil.InitializeContext) (util.Uint160, error) {
|
||||||
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
r := management.NewReader(wCtx.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -60,7 +60,7 @@ func init() {
|
||||||
|
|
||||||
func deployContractCmd(cmd *cobra.Command, args []string) error {
|
func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
c, err := NewInitializeContext(cmd, v)
|
c, err := util.NewInitializeContext(cmd, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialization error: %w", err)
|
return fmt.Errorf("initialization error: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := readContract(ctrPath, ctrName)
|
cs, err := util.ReadContract(ctrPath, ctrName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerNNS(nnsCs *state.Contract, c *InitializeContext, zone string, domain string, cs *util.ContractState, writer *io.BufBinWriter) error {
|
func registerNNS(nnsCs *state.Contract, c *util.InitializeContext, zone string, domain string, cs *util.ContractState, writer *io.BufBinWriter) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
||||||
|
@ -147,12 +147,12 @@ func registerNNS(nnsCs *state.Contract, c *InitializeContext, zone string, domai
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
util.FrostfsOpsEmail, int64(3600), int64(600), int64(util.DefaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
util.FrostfsOpsEmail, int64(3600), int64(600), int64(util.DefaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
} else {
|
} else {
|
||||||
s, ok, err := c.NNSRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
s, ok, err := c.NNSRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
||||||
|
|
|
@ -86,7 +86,7 @@ func dumpContractHashes(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ctrName := range contractList {
|
for _, ctrName := range morphUtil.ContractList {
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
emit.AppCall(bw.BinWriter, cs.Hash, "resolve", callflag.ReadOnly,
|
||||||
morphUtil.DomainOf(ctrName), int64(nns.TXT))
|
morphUtil.DomainOf(ctrName), int64(nns.TXT))
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util2.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func forceNewEpochCmd(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitNewEpochCall(bw *io.BufBinWriter, wCtx *InitializeContext, nmHash util.Uint160) error {
|
func emitNewEpochCall(bw *io.BufBinWriter, wCtx *util2.InitializeContext, nmHash util.Uint160) error {
|
||||||
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
curr, err := unwrap.Int64(wCtx.ReadOnlyInvoker.Call(nmHash, "epoch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("can't fetch current epoch from the netmap contract")
|
return errors.New("can't fetch current epoch from the netmap contract")
|
||||||
|
|
|
@ -416,11 +416,11 @@ type frostfsidClient struct {
|
||||||
bw *io.BufBinWriter
|
bw *io.BufBinWriter
|
||||||
contractHash util.Uint160
|
contractHash util.Uint160
|
||||||
roCli *frostfsidclient.Client // client can be used only for waiting tx, parsing and forming method params
|
roCli *frostfsidclient.Client // client can be used only for waiting tx, parsing and forming method params
|
||||||
wCtx *InitializeContext
|
wCtx *morphUtil.InitializeContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
func newFrostfsIDClient(cmd *cobra.Command) (*frostfsidClient, error) {
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := morphUtil.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't to initialize context: %w", err)
|
return nil, fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func generateAlphabetCreds(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = initializeContractWallet(v, walletDir)
|
_, err = morphUtil.InitializeContractWallet(v, walletDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := morphUtil.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ func TestGenerateAlphabet(t *testing.T) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Run("check contract group wallet", func(t *testing.T) {
|
t.Run("check contract group wallet", func(t *testing.T) {
|
||||||
p := filepath.Join(walletDir, contractWalletFilename)
|
p := filepath.Join(walletDir, util.ContractWalletFilename)
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
require.NoError(t, err, "contract wallet doesn't exist")
|
require.NoError(t, err, "contract wallet doesn't exist")
|
||||||
require.Equal(t, 1, len(w.Accounts), "contract wallet must have 1 accout")
|
require.Equal(t, 1, len(w.Accounts), "contract wallet must have 1 accout")
|
||||||
|
|
|
@ -2,80 +2,13 @@ package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
|
||||||
util2 "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
util2 "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
contractWalletFilename = "contract.json"
|
|
||||||
contractWalletPasswordKey = "contract"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initializeContractWallet(v *viper.Viper, walletDir string) (*wallet.Wallet, error) {
|
|
||||||
password, err := config.GetPassword(v, contractWalletPasswordKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := wallet.NewWallet(filepath.Join(walletDir, contractWalletFilename))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
acc, err := wallet.NewAccount()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = acc.Encrypt(password, keys.NEP2ScryptParams())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.AddAccount(acc)
|
|
||||||
if err := w.SavePretty(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func openContractWallet(v *viper.Viper, cmd *cobra.Command, walletDir string) (*wallet.Wallet, error) {
|
|
||||||
p := filepath.Join(walletDir, contractWalletFilename)
|
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
|
||||||
if err != nil {
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("can't open wallet: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Contract group wallet is missing, initialize at %s\n", p)
|
|
||||||
return initializeContractWallet(v, walletDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
password, err := config.GetPassword(v, contractWalletPasswordKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range w.Accounts {
|
|
||||||
if err := w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
|
||||||
return nil, fmt.Errorf("can't unlock wallet: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addManifestGroup(cw *wallet.Wallet, h util.Uint160, cs *util2.ContractState) error {
|
func addManifestGroup(cw *wallet.Wallet, h util.Uint160, cs *util2.ContractState) error {
|
||||||
priv := cw.Accounts[0].PrivateKey()
|
priv := cw.Accounts[0].PrivateKey()
|
||||||
pub := priv.PublicKey()
|
pub := priv.PublicKey()
|
||||||
|
|
|
@ -1,50 +1,15 @@
|
||||||
package morph
|
package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
|
||||||
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cache struct {
|
|
||||||
NNSCs *state.Contract
|
|
||||||
GroupKey *keys.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
type InitializeContext struct {
|
|
||||||
morphUtil.ClientContext
|
|
||||||
Cache
|
|
||||||
// CommitteeAcc is used for retrieving the committee address and the verification script.
|
|
||||||
CommitteeAcc *wallet.Account
|
|
||||||
// ConsensusAcc is used for retrieving the committee address and the verification script.
|
|
||||||
ConsensusAcc *wallet.Account
|
|
||||||
Wallets []*wallet.Wallet
|
|
||||||
// ContractWallet is a wallet for providing the contract group signature.
|
|
||||||
ContractWallet *wallet.Wallet
|
|
||||||
// Accounts contains simple signature accounts in the same order as in Wallets.
|
|
||||||
Accounts []*wallet.Account
|
|
||||||
Contracts map[string]*morphUtil.ContractState
|
|
||||||
Command *cobra.Command
|
|
||||||
ContractPath string
|
|
||||||
ContractURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
||||||
initCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
initCtx, err := morphUtil.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialization error: %w", err)
|
return fmt.Errorf("initialization error: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -91,289 +56,3 @@ func initializeSideChainCmd(cmd *cobra.Command, _ []string) error {
|
||||||
cmd.Println("Stage 7: set addresses in NNS.")
|
cmd.Println("Stage 7: set addresses in NNS.")
|
||||||
return setNNS(initCtx)
|
return setNNS(initCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) Close() {
|
|
||||||
if local, ok := c.Client.(*morphUtil.LocalClient); ok {
|
|
||||||
err := local.Dump()
|
|
||||||
if err != nil {
|
|
||||||
c.Command.PrintErrf("Can't write dump: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInitializeContext(cmd *cobra.Command, v *viper.Viper) (*InitializeContext, error) {
|
|
||||||
walletDir := config.ResolveHomePath(viper.GetString(morphUtil.AlphabetWalletsFlag))
|
|
||||||
wallets, err := morphUtil.GetAlphabetWallets(v, walletDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
|
||||||
|
|
||||||
var w *wallet.Wallet
|
|
||||||
w, err = getWallet(cmd, v, needContracts, walletDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := createClient(cmd, v, wallets)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
committeeAcc, err := morphUtil.GetWalletAccount(wallets[0], morphUtil.CommitteeAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't find committee account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
consensusAcc, err := morphUtil.GetWalletAccount(wallets[0], morphUtil.ConsensusAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := validateInit(cmd); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrPath, err := getContractsPath(cmd, needContracts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctrURL string
|
|
||||||
if needContracts {
|
|
||||||
ctrURL, _ = cmd.Flags().GetString(contractsURLFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkNotaryEnabled(c); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts, err := createWalletAccounts(wallets)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cliCtx, err := morphUtil.DefaultClientContext(c, committeeAcc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("client context: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
initCtx := &InitializeContext{
|
|
||||||
ClientContext: *cliCtx,
|
|
||||||
ConsensusAcc: consensusAcc,
|
|
||||||
CommitteeAcc: committeeAcc,
|
|
||||||
ContractWallet: w,
|
|
||||||
Wallets: wallets,
|
|
||||||
Accounts: accounts,
|
|
||||||
Command: cmd,
|
|
||||||
Contracts: make(map[string]*morphUtil.ContractState),
|
|
||||||
ContractPath: ctrPath,
|
|
||||||
ContractURL: ctrURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
if needContracts {
|
|
||||||
err := initCtx.readContracts(fullContractList)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initCtx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateInit(cmd *cobra.Command) error {
|
|
||||||
if cmd.Name() != "init" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
|
||||||
return fmt.Errorf("epoch duration must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
|
||||||
return fmt.Errorf("max object size must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet) (morphUtil.Client, error) {
|
|
||||||
var c morphUtil.Client
|
|
||||||
var err error
|
|
||||||
if ldf := cmd.Flags().Lookup(localDumpFlag); ldf != nil && ldf.Changed {
|
|
||||||
if cmd.Flags().Changed(morphUtil.EndpointFlag) {
|
|
||||||
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", morphUtil.EndpointFlag, localDumpFlag)
|
|
||||||
}
|
|
||||||
c, err = morphUtil.NewLocalClient(cmd, v, wallets, ldf.Value.String())
|
|
||||||
} else {
|
|
||||||
c, err = morphUtil.GetN3Client(v)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWallet(cmd *cobra.Command, v *viper.Viper, needContracts bool, walletDir string) (*wallet.Wallet, error) {
|
|
||||||
if !needContracts {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return openContractWallet(v, cmd, walletDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContractsPath(cmd *cobra.Command, needContracts bool) (string, error) {
|
|
||||||
if !needContracts {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrPath, err := cmd.Flags().GetString(contractsInitFlag)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("invalid contracts path: %w", err)
|
|
||||||
}
|
|
||||||
return ctrPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
|
||||||
for i, w := range wallets {
|
|
||||||
acc, err := morphUtil.GetWalletAccount(w, morphUtil.SingleAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
|
||||||
}
|
|
||||||
accounts[i] = acc
|
|
||||||
}
|
|
||||||
return accounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) AwaitTx() error {
|
|
||||||
return c.ClientContext.AwaitTx(c.Command)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) NNSContractState() (*state.Contract, error) {
|
|
||||||
if c.NNSCs != nil {
|
|
||||||
return c.NNSCs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
|
||||||
cs, err := r.GetContractByID(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.NNSCs = cs
|
|
||||||
return cs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) GetSigner(tryGroup bool, acc *wallet.Account) transaction.Signer {
|
|
||||||
if tryGroup && c.GroupKey != nil {
|
|
||||||
return transaction.Signer{
|
|
||||||
Account: acc.Contract.ScriptHash(),
|
|
||||||
Scopes: transaction.CustomGroups,
|
|
||||||
AllowedGroups: keys.PublicKeys{c.GroupKey},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signer := transaction.Signer{
|
|
||||||
Account: acc.Contract.ScriptHash(),
|
|
||||||
Scopes: transaction.Global, // Scope is important, as we have nested call to container contract.
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tryGroup {
|
|
||||||
return signer
|
|
||||||
}
|
|
||||||
|
|
||||||
nnsCs, err := c.NNSContractState()
|
|
||||||
if err != nil {
|
|
||||||
return signer
|
|
||||||
}
|
|
||||||
|
|
||||||
groupKey, err := morphUtil.NNSResolveKey(c.ReadOnlyInvoker, nnsCs.Hash, morphClient.NNSGroupKeyName)
|
|
||||||
if err == nil {
|
|
||||||
c.GroupKey = groupKey
|
|
||||||
|
|
||||||
signer.Scopes = transaction.CustomGroups
|
|
||||||
signer.AllowedGroups = keys.PublicKeys{groupKey}
|
|
||||||
}
|
|
||||||
return signer
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC.
|
|
||||||
// If tryGroup is false, global scope is used for the signer (useful when
|
|
||||||
// working with native contracts).
|
|
||||||
func (c *InitializeContext) SendCommitteeTx(script []byte, tryGroup bool) error {
|
|
||||||
return c.sendMultiTx(script, tryGroup, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendConsensusTx creates transaction from script, signs it by alphabet nodes and sends it to RPC.
|
|
||||||
// Not that because this is used only after the contracts were initialized and deployed,
|
|
||||||
// we always try to have a group scope.
|
|
||||||
func (c *InitializeContext) SendConsensusTx(script []byte) error {
|
|
||||||
return c.sendMultiTx(script, true, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) sendMultiTx(script []byte, tryGroup bool, withConsensus bool) error {
|
|
||||||
var act *actor.Actor
|
|
||||||
var err error
|
|
||||||
|
|
||||||
withConsensus = withConsensus && !c.ConsensusAcc.Contract.ScriptHash().Equals(c.CommitteeAcc.ScriptHash())
|
|
||||||
if tryGroup {
|
|
||||||
// Even for consensus signatures we need the committee to pay.
|
|
||||||
signers := make([]actor.SignerAccount, 1, 2)
|
|
||||||
signers[0] = actor.SignerAccount{
|
|
||||||
Signer: c.GetSigner(tryGroup, c.CommitteeAcc),
|
|
||||||
Account: c.CommitteeAcc,
|
|
||||||
}
|
|
||||||
if withConsensus {
|
|
||||||
signers = append(signers, actor.SignerAccount{
|
|
||||||
Signer: c.GetSigner(tryGroup, c.ConsensusAcc),
|
|
||||||
Account: c.ConsensusAcc,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
act, err = actor.New(c.Client, signers)
|
|
||||||
} else {
|
|
||||||
if withConsensus {
|
|
||||||
panic("BUG: should never happen")
|
|
||||||
}
|
|
||||||
act, err = c.CommitteeAct, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not create actor: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := act.MakeUnsignedRun(script, []transaction.Attribute{{Type: transaction.HighPriority}})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not perform test invocation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.MultiSign(tx, morphUtil.CommitteeAccountName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if withConsensus {
|
|
||||||
if err := c.MultiSign(tx, morphUtil.ConsensusAccountName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendTx(tx, c.Command, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkNotaryEnabled(c morphUtil.Client) error {
|
|
||||||
ns, err := c.GetNativeContracts()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't get native contract hashes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
notaryEnabled := false
|
|
||||||
nativeHashes := make(map[string]util.Uint160, len(ns))
|
|
||||||
for i := range ns {
|
|
||||||
if ns[i].Manifest.Name == nativenames.Notary {
|
|
||||||
notaryEnabled = true
|
|
||||||
}
|
|
||||||
nativeHashes[ns[i].Manifest.Name] = ns[i].Hash
|
|
||||||
}
|
|
||||||
if !notaryEnabled {
|
|
||||||
return errors.New("notary contract must be enabled")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
package morph
|
package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
|
||||||
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -35,41 +29,23 @@ import (
|
||||||
|
|
||||||
const frostfsIDAdminConfigKey = "frostfsid.admin"
|
const frostfsIDAdminConfigKey = "frostfsid.admin"
|
||||||
|
|
||||||
var (
|
var netmapConfigKeys = []string{
|
||||||
contractList = []string{
|
netmap.EpochDurationConfig,
|
||||||
morphUtil.BalanceContract,
|
netmap.MaxObjectSizeConfig,
|
||||||
morphUtil.ContainerContract,
|
netmap.ContainerFeeConfig,
|
||||||
morphUtil.FrostfsIDContract,
|
netmap.ContainerAliasFeeConfig,
|
||||||
morphUtil.NetmapContract,
|
netmap.IrCandidateFeeConfig,
|
||||||
morphUtil.PolicyContract,
|
netmap.WithdrawFeeConfig,
|
||||||
morphUtil.ProxyContract,
|
netmap.HomomorphicHashingDisabledKey,
|
||||||
}
|
netmap.MaintenanceModeAllowedConfig,
|
||||||
|
}
|
||||||
fullContractList = append([]string{
|
|
||||||
morphUtil.FrostfsContract,
|
|
||||||
morphUtil.ProcessingContract,
|
|
||||||
morphUtil.NNSContract,
|
|
||||||
morphUtil.AlphabetContract,
|
|
||||||
}, contractList...)
|
|
||||||
|
|
||||||
netmapConfigKeys = []string{
|
|
||||||
netmap.EpochDurationConfig,
|
|
||||||
netmap.MaxObjectSizeConfig,
|
|
||||||
netmap.ContainerFeeConfig,
|
|
||||||
netmap.ContainerAliasFeeConfig,
|
|
||||||
netmap.IrCandidateFeeConfig,
|
|
||||||
netmap.WithdrawFeeConfig,
|
|
||||||
netmap.HomomorphicHashingDisabledKey,
|
|
||||||
netmap.MaintenanceModeAllowedConfig,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
updateMethodName = "update"
|
updateMethodName = "update"
|
||||||
deployMethodName = "deploy"
|
deployMethodName = "deploy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func deployNNS(c *InitializeContext, method string) error {
|
func deployNNS(c *morphUtil.InitializeContext, method string) error {
|
||||||
cs := c.GetContract(morphUtil.NNSContract)
|
cs := c.GetContract(morphUtil.NNSContract)
|
||||||
h := cs.Hash
|
h := cs.Hash
|
||||||
|
|
||||||
|
@ -113,7 +89,7 @@ func deployNNS(c *InitializeContext, method string) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateContractsInternal(c *InitializeContext) error {
|
func updateContractsInternal(c *morphUtil.InitializeContext) error {
|
||||||
alphaCs := c.GetContract(morphUtil.AlphabetContract)
|
alphaCs := c.GetContract(morphUtil.AlphabetContract)
|
||||||
|
|
||||||
nnsCs, err := c.NNSContractState()
|
nnsCs, err := c.NNSContractState()
|
||||||
|
@ -166,13 +142,13 @@ func updateContractsInternal(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deployOrUpdateContracts(c *InitializeContext, w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error {
|
func deployOrUpdateContracts(c *morphUtil.InitializeContext, w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error {
|
||||||
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
||||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||||
emit.AppCall(w.BinWriter, nnsHash, "setPrice", callflag.All, 1)
|
emit.AppCall(w.BinWriter, nnsHash, "setPrice", callflag.All, 1)
|
||||||
|
|
||||||
for _, ctrName := range contractList {
|
for _, ctrName := range morphUtil.ContractList {
|
||||||
cs := c.GetContract(ctrName)
|
cs := c.GetContract(ctrName)
|
||||||
|
|
||||||
method := updateMethodName
|
method := updateMethodName
|
||||||
|
@ -233,7 +209,7 @@ func deployOrUpdateContracts(c *InitializeContext, w *io2.BufBinWriter, nnsHash
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deployAlphabetAccounts(c *InitializeContext, nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *morphUtil.ContractState) ([]any, error) {
|
func deployAlphabetAccounts(c *morphUtil.InitializeContext, nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *morphUtil.ContractState) ([]any, error) {
|
||||||
var keysParam []any
|
var keysParam []any
|
||||||
|
|
||||||
baseGroups := alphaCs.Manifest.Groups
|
baseGroups := alphaCs.Manifest.Groups
|
||||||
|
@ -272,7 +248,7 @@ func deployAlphabetAccounts(c *InitializeContext, nnsHash util.Uint160, w *io2.B
|
||||||
return keysParam, nil
|
return keysParam, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deployContracts(c *InitializeContext) error {
|
func deployContracts(c *morphUtil.InitializeContext) error {
|
||||||
alphaCs := c.GetContract(morphUtil.AlphabetContract)
|
alphaCs := c.GetContract(morphUtil.AlphabetContract)
|
||||||
|
|
||||||
var keysParam []any
|
var keysParam []any
|
||||||
|
@ -309,7 +285,7 @@ func deployContracts(c *InitializeContext) error {
|
||||||
c.SentTxs = append(c.SentTxs, morphUtil.HashVUBPair{Hash: txHash, Vub: vub})
|
c.SentTxs = append(c.SentTxs, morphUtil.HashVUBPair{Hash: txHash, Vub: vub})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ctrName := range contractList {
|
for _, ctrName := range morphUtil.ContractList {
|
||||||
cs := c.GetContract(ctrName)
|
cs := c.GetContract(ctrName)
|
||||||
|
|
||||||
ctrHash := cs.Hash
|
ctrHash := cs.Hash
|
||||||
|
@ -341,147 +317,11 @@ func deployContracts(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) IsUpdated(ctrHash util.Uint160, cs *morphUtil.ContractState) bool {
|
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
|
||||||
realCs, err := r.GetContract(ctrHash)
|
|
||||||
return err == nil && realCs != nil && realCs.NEF.Checksum == cs.NEF.Checksum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) GetContract(ctrName string) *morphUtil.ContractState {
|
|
||||||
return c.Contracts[ctrName]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) readContracts(names []string) error {
|
|
||||||
var (
|
|
||||||
fi os.FileInfo
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if c.ContractPath != "" {
|
|
||||||
fi, err = os.Stat(c.ContractPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid contracts path: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.ContractPath != "" && fi.IsDir() {
|
|
||||||
for _, ctrName := range names {
|
|
||||||
cs, err := readContract(filepath.Join(c.ContractPath, ctrName), ctrName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Contracts[ctrName] = cs
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var r io.ReadCloser
|
|
||||||
if c.ContractPath != "" {
|
|
||||||
r, err = os.Open(c.ContractPath)
|
|
||||||
} else if c.ContractURL != "" {
|
|
||||||
r, err = downloadContracts(c.Command, c.ContractURL)
|
|
||||||
} else {
|
|
||||||
r, err = downloadContractsFromRepository(c.Command)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't open contracts archive: %w", err)
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
m, err := readContractsFromArchive(r, names)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, name := range names {
|
|
||||||
if err := m[name].Parse(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Contracts[name] = m[name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ctrName := range names {
|
|
||||||
if ctrName != morphUtil.AlphabetContract {
|
|
||||||
cs := c.Contracts[ctrName]
|
|
||||||
cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
cs.NEF.Checksum, cs.Manifest.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readContract(ctrPath, ctrName string) (*morphUtil.ContractState, error) {
|
|
||||||
rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err)
|
|
||||||
}
|
|
||||||
rawManif, err := os.ReadFile(filepath.Join(ctrPath, "config.json"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cs := &morphUtil.ContractState{
|
|
||||||
RawNEF: rawNef,
|
|
||||||
RawManifest: rawManif,
|
|
||||||
}
|
|
||||||
|
|
||||||
return cs, cs.Parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
func readContractsFromArchive(file io.Reader, names []string) (map[string]*morphUtil.ContractState, error) {
|
|
||||||
m := make(map[string]*morphUtil.ContractState, len(names))
|
|
||||||
for i := range names {
|
|
||||||
m[names[i]] = new(morphUtil.ContractState)
|
|
||||||
}
|
|
||||||
|
|
||||||
gr, err := gzip.NewReader(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("contracts file must be tar.gz archive: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := tar.NewReader(gr)
|
|
||||||
for h, err := r.Next(); ; h, err = r.Next() {
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, _ := filepath.Split(h.Name)
|
|
||||||
ctrName := filepath.Base(dir)
|
|
||||||
|
|
||||||
cs, ok := m[ctrName]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(h.Name, filepath.Join(ctrName, ctrName+"_contract.nef")):
|
|
||||||
cs.RawNEF, err = io.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err)
|
|
||||||
}
|
|
||||||
case strings.HasSuffix(h.Name, "config.json"):
|
|
||||||
cs.RawManifest, err = io.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m[ctrName] = cs
|
|
||||||
}
|
|
||||||
|
|
||||||
for ctrName, cs := range m {
|
|
||||||
if cs.RawNEF == nil {
|
|
||||||
return nil, fmt.Errorf("NEF for %s contract wasn't found", ctrName)
|
|
||||||
}
|
|
||||||
if cs.RawManifest == nil {
|
|
||||||
return nil, fmt.Errorf("manifest for %s contract wasn't found", ctrName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContractDeployParameters(cs *morphUtil.ContractState, deployData []any) []any {
|
func getContractDeployParameters(cs *morphUtil.ContractState, deployData []any) []any {
|
||||||
return []any{cs.RawNEF, cs.RawManifest, deployData}
|
return []any{cs.RawNEF, cs.RawManifest, deployData}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContractDeployData(c *InitializeContext, ctrName string, keysParam []any, method string) ([]any, error) {
|
func getContractDeployData(c *morphUtil.InitializeContext, ctrName string, keysParam []any, method string) ([]any, error) {
|
||||||
items := make([]any, 0, 6)
|
items := make([]any, 0, 6)
|
||||||
|
|
||||||
switch ctrName {
|
switch ctrName {
|
||||||
|
@ -625,13 +465,3 @@ func mergeNetmapConfig(roInvoker *invoker.Invoker, md map[string]any) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) GetAlphabetDeployItems(i, n int) []any {
|
|
||||||
items := make([]any, 5)
|
|
||||||
items[0] = c.Contracts[morphUtil.NetmapContract].Hash
|
|
||||||
items[1] = c.Contracts[morphUtil.ProxyContract].Hash
|
|
||||||
items[2] = innerring.GlagoliticLetter(i).String()
|
|
||||||
items[3] = int64(i)
|
|
||||||
items[4] = int64(n)
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
|
@ -19,14 +18,9 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"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/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
|
func setNNS(c *morphUtil.InitializeContext) error {
|
||||||
|
|
||||||
const frostfsOpsEmail = "ops@frostfs.info"
|
|
||||||
|
|
||||||
func setNNS(c *InitializeContext) error {
|
|
||||||
r := management.NewReader(c.ReadOnlyInvoker)
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,7 +34,7 @@ func setNNS(c *InitializeContext) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
"frostfs", c.CommitteeAcc.Contract.ScriptHash(),
|
"frostfs", c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
morphUtil.FrostfsOpsEmail, int64(3600), int64(600), int64(morphUtil.DefaultExpirationTime), int64(3600))
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
if err := c.SendCommitteeTx(bw.Bytes(), true); err != nil {
|
if err := c.SendCommitteeTx(bw.Bytes(), true); err != nil {
|
||||||
return fmt.Errorf("can't add domain root to NNS: %w", err)
|
return fmt.Errorf("can't add domain root to NNS: %w", err)
|
||||||
|
@ -61,7 +55,7 @@ func setNNS(c *InitializeContext) error {
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", domain, alphaCs.Hash.StringLE())
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, alphaCs.Hash.StringLE())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ctrName := range contractList {
|
for _, ctrName := range morphUtil.ContractList {
|
||||||
cs := c.GetContract(ctrName)
|
cs := c.GetContract(ctrName)
|
||||||
|
|
||||||
domain := ctrName + ".frostfs"
|
domain := ctrName + ".frostfs"
|
||||||
|
@ -81,7 +75,7 @@ func setNNS(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateNNSGroup(c *InitializeContext, nnsHash util.Uint160, pub *keys.PublicKey) error {
|
func updateNNSGroup(c *morphUtil.InitializeContext, nnsHash util.Uint160, pub *keys.PublicKey) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
keyAlreadyAdded, domainRegCodeEmitted, err := c.EmitUpdateNNSGroupScript(bw, nnsHash, pub)
|
keyAlreadyAdded, domainRegCodeEmitted, err := c.EmitUpdateNNSGroupScript(bw, nnsHash, pub)
|
||||||
if keyAlreadyAdded || err != nil {
|
if keyAlreadyAdded || err != nil {
|
||||||
|
@ -99,40 +93,6 @@ func updateNNSGroup(c *InitializeContext, nnsHash util.Uint160, pub *keys.Public
|
||||||
return c.SendCommitteeTx(script, true)
|
return c.SendCommitteeTx(script, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitUpdateNNSGroupScript emits script for updating group key stored in NNS.
|
|
||||||
// First return value is true iff the key is already there and nothing should be done.
|
|
||||||
// Second return value is true iff a domain registration code was emitted.
|
|
||||||
func (c *InitializeContext) EmitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHash util.Uint160, pub *keys.PublicKey) (bool, bool, error) {
|
|
||||||
isAvail, err := morphUtil.NNSIsAvailable(c.Client, nnsHash, morphClient.NNSGroupKeyName)
|
|
||||||
if err != nil {
|
|
||||||
return false, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isAvail {
|
|
||||||
currentPub, err := morphUtil.NNSResolveKey(c.ReadOnlyInvoker, nnsHash, morphClient.NNSGroupKeyName)
|
|
||||||
if err != nil {
|
|
||||||
return false, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if pub.Equal(currentPub) {
|
|
||||||
return true, false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isAvail {
|
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
|
||||||
morphClient.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "deleteRecords", callflag.All, "group.frostfs", int64(nns.TXT))
|
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "addRecord", callflag.All,
|
|
||||||
"group.frostfs", int64(nns.TXT), hex.EncodeToString(pub.Bytes()))
|
|
||||||
|
|
||||||
return false, isAvail, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAlphabetNNSDomain(i int) string {
|
func getAlphabetNNSDomain(i int) string {
|
||||||
return morphUtil.AlphabetContract + strconv.FormatUint(uint64(i), 10) + ".frostfs"
|
return morphUtil.AlphabetContract + strconv.FormatUint(uint64(i), 10) + ".frostfs"
|
||||||
}
|
}
|
||||||
|
@ -160,33 +120,7 @@ func wrapRegisterScriptWithPrice(w *io.BufBinWriter, nnsHash util.Uint160, s []b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.Uint160, domain string) ([]byte, bool, error) {
|
func nnsRegisterDomain(c *morphUtil.InitializeContext, nnsHash, expectedHash util.Uint160, domain string) error {
|
||||||
ok, err := morphUtil.NNSIsAvailable(c.Client, nnsHash, domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
bw := io.NewBufBinWriter()
|
|
||||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
|
||||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
frostfsOpsEmail, int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
|
|
||||||
if bw.Err != nil {
|
|
||||||
panic(bw.Err)
|
|
||||||
}
|
|
||||||
return bw.Bytes(), false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := morphUtil.NNSResolveHash(c.ReadOnlyInvoker, nnsHash, domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
return nil, s == expectedHash, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nnsRegisterDomain(c *InitializeContext, nnsHash, expectedHash util.Uint160, domain string) error {
|
|
||||||
script, ok, err := c.NNSRegisterDomainScript(nnsHash, expectedHash, domain)
|
script, ok, err := c.NNSRegisterDomainScript(nnsHash, expectedHash, domain)
|
||||||
if ok || err != nil {
|
if ok || err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -204,13 +138,4 @@ func nnsRegisterDomain(c *InitializeContext, nnsHash, expectedHash util.Uint160,
|
||||||
return c.SendCommitteeTx(w.Bytes(), true)
|
return c.SendCommitteeTx(w.Bytes(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) NNSRootRegistered(nnsHash util.Uint160, zone string) (bool, error) {
|
|
||||||
res, err := c.CommitteeAct.Call(nnsHash, "isAvailable", "name."+zone)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.State == vmstate.Halt.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var errMissingNNSRecord = errors.New("missing NNS record")
|
var errMissingNNSRecord = errors.New("missing NNS record")
|
||||||
|
|
|
@ -28,7 +28,7 @@ const (
|
||||||
registerBatchSize = transaction.MaxAttributes - 1
|
registerBatchSize = transaction.MaxAttributes - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerCandidateRange(c *InitializeContext, start, end int) error {
|
func registerCandidateRange(c *morphUtil.InitializeContext, start, end int) error {
|
||||||
regPrice, err := getCandidateRegisterPrice(c)
|
regPrice, err := getCandidateRegisterPrice(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't fetch registration price: %w", err)
|
return fmt.Errorf("can't fetch registration price: %w", err)
|
||||||
|
@ -82,7 +82,7 @@ func registerCandidateRange(c *InitializeContext, start, end int) error {
|
||||||
return c.SendTx(tx, c.Command, true)
|
return c.SendTx(tx, c.Command, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerCandidates(c *InitializeContext) error {
|
func registerCandidates(c *morphUtil.InitializeContext) error {
|
||||||
cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neo.Hash, "getCandidates"))
|
cc, err := unwrap.Array(c.ReadOnlyInvoker.Call(neo.Hash, "getCandidates"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("`getCandidates`: %w", err)
|
return fmt.Errorf("`getCandidates`: %w", err)
|
||||||
|
@ -115,7 +115,7 @@ func registerCandidates(c *InitializeContext) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferNEOToAlphabetContracts(c *InitializeContext) error {
|
func transferNEOToAlphabetContracts(c *morphUtil.InitializeContext) error {
|
||||||
neoHash := neo.Hash
|
neoHash := neo.Hash
|
||||||
|
|
||||||
ok, err := transferNEOFinished(c, neoHash)
|
ok, err := transferNEOFinished(c, neoHash)
|
||||||
|
@ -141,7 +141,7 @@ func transferNEOToAlphabetContracts(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferNEOFinished(c *InitializeContext, neoHash util.Uint160) (bool, error) {
|
func transferNEOFinished(c *morphUtil.InitializeContext, neoHash util.Uint160) (bool, error) {
|
||||||
r := nep17.NewReader(c.ReadOnlyInvoker, neoHash)
|
r := nep17.NewReader(c.ReadOnlyInvoker, neoHash)
|
||||||
bal, err := r.BalanceOf(c.CommitteeAcc.Contract.ScriptHash())
|
bal, err := r.BalanceOf(c.CommitteeAcc.Contract.ScriptHash())
|
||||||
return bal.Cmp(big.NewInt(native.NEOTotalSupply)) == -1, err
|
return bal.Cmp(big.NewInt(native.NEOTotalSupply)) == -1, err
|
||||||
|
@ -149,7 +149,7 @@ func transferNEOFinished(c *InitializeContext, neoHash util.Uint160) (bool, erro
|
||||||
|
|
||||||
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")
|
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")
|
||||||
|
|
||||||
func getCandidateRegisterPrice(c *InitializeContext) (int64, error) {
|
func getCandidateRegisterPrice(c *morphUtil.InitializeContext) (int64, error) {
|
||||||
switch c.Client.(type) {
|
switch c.Client.(type) {
|
||||||
case *rpcclient.Client:
|
case *rpcclient.Client:
|
||||||
inv := invoker.New(c.Client, nil)
|
inv := invoker.New(c.Client, nil)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setNotaryAndAlphabetNodes(c *InitializeContext) error {
|
func setNotaryAndAlphabetNodes(c *util.InitializeContext) error {
|
||||||
if ok, err := setRolesFinished(c); ok || err != nil {
|
if ok, err := setRolesFinished(c); ok || err != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.Command.Println("Stage 2: already performed.")
|
c.Command.Println("Stage 2: already performed.")
|
||||||
|
@ -35,7 +35,7 @@ func setNotaryAndAlphabetNodes(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRolesFinished(c *InitializeContext) (bool, error) {
|
func setRolesFinished(c *util.InitializeContext) (bool, error) {
|
||||||
height, err := c.Client.GetBlockCount()
|
height, err := c.Client.GetBlockCount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -58,27 +58,27 @@ func testInitialize(t *testing.T, committeeSize int) {
|
||||||
v.Set(util.ProtoConfigPath, filepath.Join(testdataDir, protoFileName))
|
v.Set(util.ProtoConfigPath, filepath.Join(testdataDir, protoFileName))
|
||||||
|
|
||||||
// Set to the path or remove the next statement to download from the network.
|
// Set to the path or remove the next statement to download from the network.
|
||||||
require.NoError(t, initCmd.Flags().Set(contractsInitFlag, contractsPath))
|
require.NoError(t, initCmd.Flags().Set(util.ContractsInitFlag, contractsPath))
|
||||||
|
|
||||||
dumpPath := filepath.Join(testdataDir, "out")
|
dumpPath := filepath.Join(testdataDir, "out")
|
||||||
require.NoError(t, initCmd.Flags().Set(localDumpFlag, dumpPath))
|
require.NoError(t, initCmd.Flags().Set(util.LocalDumpFlag, dumpPath))
|
||||||
v.Set(util.AlphabetWalletsFlag, testdataDir)
|
v.Set(util.AlphabetWalletsFlag, testdataDir)
|
||||||
v.Set(epochDurationInitFlag, 1)
|
v.Set(util.EpochDurationInitFlag, 1)
|
||||||
v.Set(maxObjectSizeInitFlag, 1024)
|
v.Set(util.MaxObjectSizeInitFlag, 1024)
|
||||||
|
|
||||||
setTestCredentials(v, committeeSize)
|
setTestCredentials(v, committeeSize)
|
||||||
require.NoError(t, initializeSideChainCmd(initCmd, nil))
|
require.NoError(t, initializeSideChainCmd(initCmd, nil))
|
||||||
|
|
||||||
t.Run("force-new-epoch", func(t *testing.T) {
|
t.Run("force-new-epoch", func(t *testing.T) {
|
||||||
require.NoError(t, forceNewEpoch.Flags().Set(localDumpFlag, dumpPath))
|
require.NoError(t, forceNewEpoch.Flags().Set(util.LocalDumpFlag, dumpPath))
|
||||||
require.NoError(t, forceNewEpochCmd(forceNewEpoch, nil))
|
require.NoError(t, forceNewEpochCmd(forceNewEpoch, nil))
|
||||||
})
|
})
|
||||||
t.Run("set-config", func(t *testing.T) {
|
t.Run("set-config", func(t *testing.T) {
|
||||||
require.NoError(t, setConfig.Flags().Set(localDumpFlag, dumpPath))
|
require.NoError(t, setConfig.Flags().Set(util.LocalDumpFlag, dumpPath))
|
||||||
require.NoError(t, setConfigCmd(setConfig, []string{"MaintenanceModeAllowed=true"}))
|
require.NoError(t, setConfigCmd(setConfig, []string{"MaintenanceModeAllowed=true"}))
|
||||||
})
|
})
|
||||||
t.Run("set-policy", func(t *testing.T) {
|
t.Run("set-policy", func(t *testing.T) {
|
||||||
require.NoError(t, setPolicy.Flags().Set(localDumpFlag, dumpPath))
|
require.NoError(t, setPolicy.Flags().Set(util.LocalDumpFlag, dumpPath))
|
||||||
require.NoError(t, setPolicyCmd(setPolicy, []string{"ExecFeeFactor=1"}))
|
require.NoError(t, setPolicyCmd(setPolicy, []string{"ExecFeeFactor=1"}))
|
||||||
})
|
})
|
||||||
t.Run("remove-node", func(t *testing.T) {
|
t.Run("remove-node", func(t *testing.T) {
|
||||||
|
@ -86,7 +86,7 @@ func testInitialize(t *testing.T, committeeSize int) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pub := hex.EncodeToString(pk.PublicKey().Bytes())
|
pub := hex.EncodeToString(pk.PublicKey().Bytes())
|
||||||
require.NoError(t, removeNodes.Flags().Set(localDumpFlag, dumpPath))
|
require.NoError(t, removeNodes.Flags().Set(util.LocalDumpFlag, dumpPath))
|
||||||
require.NoError(t, removeNodesCmd(removeNodes, []string{pub}))
|
require.NoError(t, removeNodesCmd(removeNodes, []string{pub}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
scContext "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"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/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
@ -28,7 +27,7 @@ const (
|
||||||
initialProxyGASAmount = 50_000 * native.GASFactor
|
initialProxyGASAmount = 50_000 * native.GASFactor
|
||||||
)
|
)
|
||||||
|
|
||||||
func transferFunds(c *InitializeContext) error {
|
func transferFunds(c *morphUtil.InitializeContext) error {
|
||||||
ok, err := transferFundsFinished(c)
|
ok, err := transferFundsFinished(c)
|
||||||
if ok || err != nil {
|
if ok || err != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -75,7 +74,7 @@ func transferFunds(c *InitializeContext) error {
|
||||||
return c.AwaitTx()
|
return c.AwaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func transferFundsFinished(c *InitializeContext) (bool, error) {
|
func transferFundsFinished(c *morphUtil.InitializeContext) (bool, error) {
|
||||||
acc := c.Accounts[0]
|
acc := c.Accounts[0]
|
||||||
|
|
||||||
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
||||||
|
@ -83,67 +82,7 @@ func transferFundsFinished(c *InitializeContext) (bool, error) {
|
||||||
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
|
return res.Cmp(big.NewInt(initialAlphabetGASAmount/2)) == 1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InitializeContext) MultiSignAndSend(tx *transaction.Transaction, accType string) error {
|
func transferGASToProxy(c *morphUtil.InitializeContext) error {
|
||||||
if err := c.MultiSign(tx, accType); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendTx(tx, c.Command, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType string) error {
|
|
||||||
version, err := c.Client.GetVersion()
|
|
||||||
if err != nil {
|
|
||||||
// error appears only if client
|
|
||||||
// has not been initialized
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
network := version.Protocol.Network
|
|
||||||
|
|
||||||
// Use parameter context to avoid dealing with signature order.
|
|
||||||
pc := scContext.NewParameterContext("", network, tx)
|
|
||||||
h := c.CommitteeAcc.Contract.ScriptHash()
|
|
||||||
if accType == morphUtil.ConsensusAccountName {
|
|
||||||
h = c.ConsensusAcc.Contract.ScriptHash()
|
|
||||||
}
|
|
||||||
for _, w := range c.Wallets {
|
|
||||||
acc, err := morphUtil.GetWalletAccount(w, accType)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't find %s wallet account: %w", accType, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
priv := acc.PrivateKey()
|
|
||||||
sign := priv.SignHashable(uint32(network), tx)
|
|
||||||
if err := pc.AddSignature(h, acc.Contract, priv.PublicKey(), sign); err != nil {
|
|
||||||
return fmt.Errorf("can't add signature: %w", err)
|
|
||||||
}
|
|
||||||
if len(pc.Items[h].Signatures) == len(acc.Contract.Parameters) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := pc.GetWitness(h)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("incomplete signature: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range tx.Signers {
|
|
||||||
if tx.Signers[i].Account == h {
|
|
||||||
if i < len(tx.Scripts) {
|
|
||||||
tx.Scripts[i] = *w
|
|
||||||
} else if i == len(tx.Scripts) {
|
|
||||||
tx.Scripts = append(tx.Scripts, *w)
|
|
||||||
} else {
|
|
||||||
panic("BUG: invalid signing order")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%s account was not found among transaction signers", accType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func transferGASToProxy(c *InitializeContext) error {
|
|
||||||
proxyCs := c.GetContract(morphUtil.ProxyContract)
|
proxyCs := c.GetContract(morphUtil.ProxyContract)
|
||||||
|
|
||||||
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
r := nep17.NewReader(c.ReadOnlyInvoker, gas.Hash)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package morph
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -10,8 +11,8 @@ import (
|
||||||
|
|
||||||
func getDefaultNetmapContractConfigMap() map[string]any {
|
func getDefaultNetmapContractConfigMap() map[string]any {
|
||||||
m := make(map[string]any)
|
m := make(map[string]any)
|
||||||
m[netmap.EpochDurationConfig] = viper.GetInt64(epochDurationInitFlag)
|
m[netmap.EpochDurationConfig] = viper.GetInt64(util.EpochDurationInitFlag)
|
||||||
m[netmap.MaxObjectSizeConfig] = viper.GetInt64(maxObjectSizeInitFlag)
|
m[netmap.MaxObjectSizeConfig] = viper.GetInt64(util.MaxObjectSizeInitFlag)
|
||||||
m[netmap.ContainerFeeConfig] = viper.GetInt64(containerFeeInitFlag)
|
m[netmap.ContainerFeeConfig] = viper.GetInt64(containerFeeInitFlag)
|
||||||
m[netmap.ContainerAliasFeeConfig] = viper.GetInt64(containerAliasFeeInitFlag)
|
m[netmap.ContainerAliasFeeConfig] = viper.GetInt64(containerAliasFeeInitFlag)
|
||||||
m[netmap.IrCandidateFeeConfig] = viper.GetInt64(candidateFeeInitFlag)
|
m[netmap.IrCandidateFeeConfig] = viper.GetInt64(candidateFeeInitFlag)
|
||||||
|
|
|
@ -85,7 +85,7 @@ func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkNotaryEnabled(c); err != nil {
|
if err := morphUtil.CheckNotaryEnabled(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func setPolicyCmd(cmd *cobra.Command, args []string) error {
|
func setPolicyCmd(cmd *cobra.Command, args []string) error {
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func removeProxyAccount(cmd *cobra.Command, _ []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
func processAccount(cmd *cobra.Command, addr util.Uint160, method string) error {
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util2.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't to initialize context: %w", err)
|
return fmt.Errorf("can't to initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ func removeNodesCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't initialize context: %w", err)
|
return fmt.Errorf("can't initialize context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
alphabetSizeFlag = "size"
|
alphabetSizeFlag = "size"
|
||||||
storageWalletFlag = "storage-wallet"
|
storageWalletFlag = "storage-wallet"
|
||||||
storageWalletLabelFlag = "label"
|
storageWalletLabelFlag = "label"
|
||||||
storageGasCLIFlag = "initial-gas"
|
storageGasCLIFlag = "initial-gas"
|
||||||
storageGasConfigFlag = "storage.initial_gas"
|
storageGasConfigFlag = "storage.initial_gas"
|
||||||
contractsInitFlag = "contracts"
|
|
||||||
contractsInitFlagDesc = "Path to archive with compiled FrostFS contracts (the default is to fetch the latest release from the official repository)"
|
maxObjectSizeCLIFlag = "max-object-size"
|
||||||
contractsURLFlag = "contracts-url"
|
|
||||||
contractsURLFlagDesc = "URL to archive with compiled FrostFS contracts"
|
|
||||||
maxObjectSizeInitFlag = "network.max_object_size"
|
|
||||||
maxObjectSizeCLIFlag = "max-object-size"
|
|
||||||
epochDurationInitFlag = "network.epoch_duration"
|
|
||||||
epochDurationCLIFlag = "epoch-duration"
|
epochDurationCLIFlag = "epoch-duration"
|
||||||
containerFeeInitFlag = "network.fee.container"
|
containerFeeInitFlag = "network.fee.container"
|
||||||
containerAliasFeeInitFlag = "network.fee.container_alias"
|
containerAliasFeeInitFlag = "network.fee.container_alias"
|
||||||
|
@ -38,7 +34,6 @@ const (
|
||||||
refillGasAmountFlag = "gas"
|
refillGasAmountFlag = "gas"
|
||||||
walletAccountFlag = "account"
|
walletAccountFlag = "account"
|
||||||
notaryDepositTillFlag = "till"
|
notaryDepositTillFlag = "till"
|
||||||
localDumpFlag = "local-dump"
|
|
||||||
|
|
||||||
walletAddressFlag = "wallet-address"
|
walletAddressFlag = "wallet-address"
|
||||||
)
|
)
|
||||||
|
@ -66,8 +61,8 @@ var (
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(util.AlphabetWalletsFlag, cmd.Flags().Lookup(util.AlphabetWalletsFlag))
|
_ = viper.BindPFlag(util.AlphabetWalletsFlag, cmd.Flags().Lookup(util.AlphabetWalletsFlag))
|
||||||
_ = viper.BindPFlag(util.EndpointFlag, cmd.Flags().Lookup(util.EndpointFlag))
|
_ = viper.BindPFlag(util.EndpointFlag, cmd.Flags().Lookup(util.EndpointFlag))
|
||||||
_ = viper.BindPFlag(epochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
_ = viper.BindPFlag(util.EpochDurationInitFlag, cmd.Flags().Lookup(epochDurationCLIFlag))
|
||||||
_ = viper.BindPFlag(maxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
_ = viper.BindPFlag(util.MaxObjectSizeInitFlag, cmd.Flags().Lookup(maxObjectSizeCLIFlag))
|
||||||
_ = viper.BindPFlag(homomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
_ = viper.BindPFlag(homomorphicHashDisabledInitFlag, cmd.Flags().Lookup(homomorphicHashDisabledCLIFlag))
|
||||||
_ = viper.BindPFlag(candidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
_ = viper.BindPFlag(candidateFeeInitFlag, cmd.Flags().Lookup(candidateFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
||||||
|
@ -365,9 +360,9 @@ func initUpdateContractsCmd() {
|
||||||
RootCmd.AddCommand(updateContractsCmd)
|
RootCmd.AddCommand(updateContractsCmd)
|
||||||
updateContractsCmd.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
updateContractsCmd.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
updateContractsCmd.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
updateContractsCmd.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
updateContractsCmd.Flags().String(contractsInitFlag, "", contractsInitFlagDesc)
|
updateContractsCmd.Flags().String(util.ContractsInitFlag, "", util.ContractsInitFlagDesc)
|
||||||
updateContractsCmd.Flags().String(contractsURLFlag, "", contractsURLFlagDesc)
|
updateContractsCmd.Flags().String(util.ContractsURLFlag, "", util.ContractsURLFlagDesc)
|
||||||
updateContractsCmd.MarkFlagsMutuallyExclusive(contractsInitFlag, contractsURLFlag)
|
updateContractsCmd.MarkFlagsMutuallyExclusive(util.ContractsInitFlag, util.ContractsURLFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDumpBalancesCmd() {
|
func initDumpBalancesCmd() {
|
||||||
|
@ -384,7 +379,7 @@ func initSetConfigCmd() {
|
||||||
setConfig.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
setConfig.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
setConfig.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
setConfig.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
||||||
setConfig.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
setConfig.Flags().String(util.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDumpNetworkConfigCmd() {
|
func initDumpNetworkConfigCmd() {
|
||||||
|
@ -402,7 +397,7 @@ func initSetPolicyCmd() {
|
||||||
RootCmd.AddCommand(setPolicy)
|
RootCmd.AddCommand(setPolicy)
|
||||||
setPolicy.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
setPolicy.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
setPolicy.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
setPolicy.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
setPolicy.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
setPolicy.Flags().String(util.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDumpPolicyCmd() {
|
func initDumpPolicyCmd() {
|
||||||
|
@ -414,14 +409,14 @@ func initRemoveNodesCmd() {
|
||||||
RootCmd.AddCommand(removeNodes)
|
RootCmd.AddCommand(removeNodes)
|
||||||
removeNodes.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
removeNodes.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
removeNodes.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
removeNodes.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
removeNodes.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
removeNodes.Flags().String(util.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initForceNewEpochCmd() {
|
func initForceNewEpochCmd() {
|
||||||
RootCmd.AddCommand(forceNewEpoch)
|
RootCmd.AddCommand(forceNewEpoch)
|
||||||
forceNewEpoch.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
forceNewEpoch.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
forceNewEpoch.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
forceNewEpoch.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
forceNewEpoch.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
forceNewEpoch.Flags().String(util.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGenerateStorageCmd() {
|
func initGenerateStorageCmd() {
|
||||||
|
@ -437,8 +432,8 @@ func initInitCmd() {
|
||||||
RootCmd.AddCommand(initCmd)
|
RootCmd.AddCommand(initCmd)
|
||||||
initCmd.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
initCmd.Flags().String(util.AlphabetWalletsFlag, "", util.AlphabetWalletsFlagDesc)
|
||||||
initCmd.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
initCmd.Flags().StringP(util.EndpointFlag, util.EndpointFlagShort, "", util.EndpointFlagDesc)
|
||||||
initCmd.Flags().String(contractsInitFlag, "", contractsInitFlagDesc)
|
initCmd.Flags().String(util.ContractsInitFlag, "", util.ContractsInitFlagDesc)
|
||||||
initCmd.Flags().String(contractsURLFlag, "", contractsURLFlagDesc)
|
initCmd.Flags().String(util.ContractsURLFlag, "", util.ContractsURLFlagDesc)
|
||||||
initCmd.Flags().Uint(epochDurationCLIFlag, 240, "Amount of side chain blocks in one FrostFS epoch")
|
initCmd.Flags().Uint(epochDurationCLIFlag, 240, "Amount of side chain blocks in one FrostFS epoch")
|
||||||
initCmd.Flags().Uint(maxObjectSizeCLIFlag, 67108864, "Max single object size in bytes")
|
initCmd.Flags().Uint(maxObjectSizeCLIFlag, 67108864, "Max single object size in bytes")
|
||||||
initCmd.Flags().Bool(homomorphicHashDisabledCLIFlag, false, "Disable object homomorphic hashing")
|
initCmd.Flags().Bool(homomorphicHashDisabledCLIFlag, false, "Disable object homomorphic hashing")
|
||||||
|
@ -446,8 +441,8 @@ func initInitCmd() {
|
||||||
initCmd.Flags().Uint64(containerFeeCLIFlag, 1000, "Container registration fee")
|
initCmd.Flags().Uint64(containerFeeCLIFlag, 1000, "Container registration fee")
|
||||||
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
||||||
initCmd.Flags().String(util.ProtoConfigPath, "", "Path to the consensus node configuration")
|
initCmd.Flags().String(util.ProtoConfigPath, "", "Path to the consensus node configuration")
|
||||||
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
initCmd.Flags().String(util.LocalDumpFlag, "", "Path to the blocks dump file")
|
||||||
initCmd.MarkFlagsMutuallyExclusive(contractsInitFlag, contractsURLFlag)
|
initCmd.MarkFlagsMutuallyExclusive(util.ContractsInitFlag, util.ContractsURLFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGenerateAlphabetCmd() {
|
func initGenerateAlphabetCmd() {
|
||||||
|
|
|
@ -3,12 +3,13 @@ package morph
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func updateContracts(cmd *cobra.Command, _ []string) error {
|
func updateContracts(cmd *cobra.Command, _ []string) error {
|
||||||
wCtx, err := NewInitializeContext(cmd, viper.GetViper())
|
wCtx, err := util.NewInitializeContext(cmd, viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialization error: %w", err)
|
return fmt.Errorf("initialization error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConsensusAccountName = "consensus"
|
ConsensusAccountName = "consensus"
|
||||||
ProtoConfigPath = "protocol"
|
ProtoConfigPath = "protocol"
|
||||||
|
@ -14,6 +16,13 @@ const (
|
||||||
EndpointFlagShort = "r"
|
EndpointFlagShort = "r"
|
||||||
AlphabetWalletsFlag = "alphabet-wallets"
|
AlphabetWalletsFlag = "alphabet-wallets"
|
||||||
AlphabetWalletsFlagDesc = "Path to alphabet wallets dir"
|
AlphabetWalletsFlagDesc = "Path to alphabet wallets dir"
|
||||||
|
LocalDumpFlag = "local-dump"
|
||||||
|
ContractsInitFlag = "contracts"
|
||||||
|
ContractsInitFlagDesc = "Path to archive with compiled FrostFS contracts (the default is to fetch the latest release from the official repository)"
|
||||||
|
ContractsURLFlag = "contracts-url"
|
||||||
|
ContractsURLFlagDesc = "URL to archive with compiled FrostFS contracts"
|
||||||
|
EpochDurationInitFlag = "network.epoch_duration"
|
||||||
|
MaxObjectSizeInitFlag = "network.max_object_size"
|
||||||
|
|
||||||
SingleAccountName = "single"
|
SingleAccountName = "single"
|
||||||
CommitteeAccountName = "committee"
|
CommitteeAccountName = "committee"
|
||||||
|
@ -28,4 +37,29 @@ const (
|
||||||
NetmapContract = "netmap"
|
NetmapContract = "netmap"
|
||||||
PolicyContract = "policy"
|
PolicyContract = "policy"
|
||||||
ProxyContract = "proxy"
|
ProxyContract = "proxy"
|
||||||
|
|
||||||
|
ContractWalletFilename = "contract.json"
|
||||||
|
ContractWalletPasswordKey = "contract"
|
||||||
|
|
||||||
|
FrostfsOpsEmail = "ops@frostfs.info"
|
||||||
|
|
||||||
|
DefaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ContractList = []string{
|
||||||
|
BalanceContract,
|
||||||
|
ContainerContract,
|
||||||
|
FrostfsIDContract,
|
||||||
|
NetmapContract,
|
||||||
|
PolicyContract,
|
||||||
|
ProxyContract,
|
||||||
|
}
|
||||||
|
|
||||||
|
FullContractList = append([]string{
|
||||||
|
FrostfsContract,
|
||||||
|
ProcessingContract,
|
||||||
|
NNSContract,
|
||||||
|
AlphabetContract,
|
||||||
|
}, ContractList...)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package morph
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
|
@ -187,3 +188,23 @@ func NNSIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckNotaryEnabled(c Client) error {
|
||||||
|
ns, err := c.GetNativeContracts()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't get native contract hashes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notaryEnabled := false
|
||||||
|
nativeHashes := make(map[string]util.Uint160, len(ns))
|
||||||
|
for i := range ns {
|
||||||
|
if ns[i].Manifest.Name == nativenames.Notary {
|
||||||
|
notaryEnabled = true
|
||||||
|
}
|
||||||
|
nativeHashes[ns[i].Manifest.Name] = ns[i].Hash
|
||||||
|
}
|
||||||
|
if !notaryEnabled {
|
||||||
|
return errors.New("notary contract must be enabled")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,67 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
io2 "io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"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/vm/vmstate"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ContractState struct {
|
||||||
|
NEF *nef.File
|
||||||
|
RawNEF []byte
|
||||||
|
Manifest *manifest.Manifest
|
||||||
|
RawManifest []byte
|
||||||
|
Hash util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
NNSCs *state.Contract
|
||||||
|
GroupKey *keys.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitializeContext struct {
|
||||||
|
ClientContext
|
||||||
|
Cache
|
||||||
|
// CommitteeAcc is used for retrieving the committee address and the verification script.
|
||||||
|
CommitteeAcc *wallet.Account
|
||||||
|
// ConsensusAcc is used for retrieving the committee address and the verification script.
|
||||||
|
ConsensusAcc *wallet.Account
|
||||||
|
Wallets []*wallet.Wallet
|
||||||
|
// ContractWallet is a wallet for providing the contract group signature.
|
||||||
|
ContractWallet *wallet.Wallet
|
||||||
|
// Accounts contains simple signature accounts in the same order as in Wallets.
|
||||||
|
Accounts []*wallet.Account
|
||||||
|
Contracts map[string]*ContractState
|
||||||
|
Command *cobra.Command
|
||||||
|
ContractPath string
|
||||||
|
ContractURL string
|
||||||
|
}
|
||||||
|
|
||||||
func (cs *ContractState) Parse() error {
|
func (cs *ContractState) Parse() error {
|
||||||
nf, err := nef.FileFromBytes(cs.RawNEF)
|
nf, err := nef.FileFromBytes(cs.RawNEF)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,10 +78,466 @@ func (cs *ContractState) Parse() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContractState struct {
|
func NewInitializeContext(cmd *cobra.Command, v *viper.Viper) (*InitializeContext, error) {
|
||||||
NEF *nef.File
|
walletDir := config.ResolveHomePath(viper.GetString(AlphabetWalletsFlag))
|
||||||
RawNEF []byte
|
wallets, err := GetAlphabetWallets(v, walletDir)
|
||||||
Manifest *manifest.Manifest
|
if err != nil {
|
||||||
RawManifest []byte
|
return nil, err
|
||||||
Hash util.Uint160
|
}
|
||||||
|
|
||||||
|
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
||||||
|
|
||||||
|
var w *wallet.Wallet
|
||||||
|
w, err = GetWallet(cmd, v, needContracts, walletDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := createClient(cmd, v, wallets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
committeeAcc, err := GetWalletAccount(wallets[0], CommitteeAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't find committee account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
consensusAcc, err := GetWalletAccount(wallets[0], ConsensusAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateInit(cmd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrPath, err := getContractsPath(cmd, needContracts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctrURL string
|
||||||
|
if needContracts {
|
||||||
|
ctrURL, _ = cmd.Flags().GetString(ContractsURLFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := CheckNotaryEnabled(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := createWalletAccounts(wallets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cliCtx, err := DefaultClientContext(c, committeeAcc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("client context: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
initCtx := &InitializeContext{
|
||||||
|
ClientContext: *cliCtx,
|
||||||
|
ConsensusAcc: consensusAcc,
|
||||||
|
CommitteeAcc: committeeAcc,
|
||||||
|
ContractWallet: w,
|
||||||
|
Wallets: wallets,
|
||||||
|
Accounts: accounts,
|
||||||
|
Command: cmd,
|
||||||
|
Contracts: make(map[string]*ContractState),
|
||||||
|
ContractPath: ctrPath,
|
||||||
|
ContractURL: ctrURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
if needContracts {
|
||||||
|
err := readContracts(initCtx, FullContractList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateInit(cmd *cobra.Command) error {
|
||||||
|
if cmd.Name() != "init" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if viper.GetInt64(EpochDurationInitFlag) <= 0 {
|
||||||
|
return fmt.Errorf("epoch duration must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if viper.GetInt64(MaxObjectSizeInitFlag) <= 0 {
|
||||||
|
return fmt.Errorf("max object size must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet) (Client, error) {
|
||||||
|
var c Client
|
||||||
|
var err error
|
||||||
|
if ldf := cmd.Flags().Lookup(LocalDumpFlag); ldf != nil && ldf.Changed {
|
||||||
|
if cmd.Flags().Changed(EndpointFlag) {
|
||||||
|
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", EndpointFlag, LocalDumpFlag)
|
||||||
|
}
|
||||||
|
c, err = NewLocalClient(cmd, v, wallets, ldf.Value.String())
|
||||||
|
} else {
|
||||||
|
c, err = GetN3Client(v)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContractsPath(cmd *cobra.Command, needContracts bool) (string, error) {
|
||||||
|
if !needContracts {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrPath, err := cmd.Flags().GetString(ContractsInitFlag)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid contracts path: %w", err)
|
||||||
|
}
|
||||||
|
return ctrPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
||||||
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
|
for i, w := range wallets {
|
||||||
|
acc, err := GetWalletAccount(w, SingleAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
||||||
|
}
|
||||||
|
accounts[i] = acc
|
||||||
|
}
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readContracts(c *InitializeContext, names []string) error {
|
||||||
|
var (
|
||||||
|
fi os.FileInfo
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if c.ContractPath != "" {
|
||||||
|
fi, err = os.Stat(c.ContractPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid contracts path: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ContractPath != "" && fi.IsDir() {
|
||||||
|
for _, ctrName := range names {
|
||||||
|
cs, err := ReadContract(filepath.Join(c.ContractPath, ctrName), ctrName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Contracts[ctrName] = cs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var r io2.ReadCloser
|
||||||
|
if c.ContractPath != "" {
|
||||||
|
r, err = os.Open(c.ContractPath)
|
||||||
|
} else if c.ContractURL != "" {
|
||||||
|
r, err = downloadContracts(c.Command, c.ContractURL)
|
||||||
|
} else {
|
||||||
|
r, err = downloadContractsFromRepository(c.Command)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't open contracts archive: %w", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
m, err := ReadContractsFromArchive(r, names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
if err := m[name].Parse(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Contracts[name] = m[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ctrName := range names {
|
||||||
|
if ctrName != AlphabetContract {
|
||||||
|
cs := c.Contracts[ctrName]
|
||||||
|
cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
cs.NEF.Checksum, cs.Manifest.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) Close() {
|
||||||
|
if local, ok := c.Client.(*LocalClient); ok {
|
||||||
|
err := local.Dump()
|
||||||
|
if err != nil {
|
||||||
|
c.Command.PrintErrf("Can't write dump: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) AwaitTx() error {
|
||||||
|
return c.ClientContext.AwaitTx(c.Command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) NNSContractState() (*state.Contract, error) {
|
||||||
|
if c.NNSCs != nil {
|
||||||
|
return c.NNSCs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
|
cs, err := r.GetContractByID(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.NNSCs = cs
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) GetSigner(tryGroup bool, acc *wallet.Account) transaction.Signer {
|
||||||
|
if tryGroup && c.GroupKey != nil {
|
||||||
|
return transaction.Signer{
|
||||||
|
Account: acc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.CustomGroups,
|
||||||
|
AllowedGroups: keys.PublicKeys{c.GroupKey},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signer := transaction.Signer{
|
||||||
|
Account: acc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.Global, // Scope is important, as we have nested call to container contract.
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tryGroup {
|
||||||
|
return signer
|
||||||
|
}
|
||||||
|
|
||||||
|
nnsCs, err := c.NNSContractState()
|
||||||
|
if err != nil {
|
||||||
|
return signer
|
||||||
|
}
|
||||||
|
|
||||||
|
groupKey, err := NNSResolveKey(c.ReadOnlyInvoker, nnsCs.Hash, client.NNSGroupKeyName)
|
||||||
|
if err == nil {
|
||||||
|
c.GroupKey = groupKey
|
||||||
|
|
||||||
|
signer.Scopes = transaction.CustomGroups
|
||||||
|
signer.AllowedGroups = keys.PublicKeys{groupKey}
|
||||||
|
}
|
||||||
|
return signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCommitteeTx creates transaction from script, signs it by committee nodes and sends it to RPC.
|
||||||
|
// If tryGroup is false, global scope is used for the signer (useful when
|
||||||
|
// working with native contracts).
|
||||||
|
func (c *InitializeContext) SendCommitteeTx(script []byte, tryGroup bool) error {
|
||||||
|
return c.sendMultiTx(script, tryGroup, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendConsensusTx creates transaction from script, signs it by alphabet nodes and sends it to RPC.
|
||||||
|
// Not that because this is used only after the contracts were initialized and deployed,
|
||||||
|
// we always try to have a group scope.
|
||||||
|
func (c *InitializeContext) SendConsensusTx(script []byte) error {
|
||||||
|
return c.sendMultiTx(script, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) sendMultiTx(script []byte, tryGroup bool, withConsensus bool) error {
|
||||||
|
var act *actor.Actor
|
||||||
|
var err error
|
||||||
|
|
||||||
|
withConsensus = withConsensus && !c.ConsensusAcc.Contract.ScriptHash().Equals(c.CommitteeAcc.ScriptHash())
|
||||||
|
if tryGroup {
|
||||||
|
// Even for consensus signatures we need the committee to pay.
|
||||||
|
signers := make([]actor.SignerAccount, 1, 2)
|
||||||
|
signers[0] = actor.SignerAccount{
|
||||||
|
Signer: c.GetSigner(tryGroup, c.CommitteeAcc),
|
||||||
|
Account: c.CommitteeAcc,
|
||||||
|
}
|
||||||
|
if withConsensus {
|
||||||
|
signers = append(signers, actor.SignerAccount{
|
||||||
|
Signer: c.GetSigner(tryGroup, c.ConsensusAcc),
|
||||||
|
Account: c.ConsensusAcc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
act, err = actor.New(c.Client, signers)
|
||||||
|
} else {
|
||||||
|
if withConsensus {
|
||||||
|
panic("BUG: should never happen")
|
||||||
|
}
|
||||||
|
act, err = c.CommitteeAct, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create actor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := act.MakeUnsignedRun(script, []transaction.Attribute{{Type: transaction.HighPriority}})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not perform test invocation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.MultiSign(tx, CommitteeAccountName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if withConsensus {
|
||||||
|
if err := c.MultiSign(tx, ConsensusAccountName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendTx(tx, c.Command, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) MultiSignAndSend(tx *transaction.Transaction, accType string) error {
|
||||||
|
if err := c.MultiSign(tx, accType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendTx(tx, c.Command, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) MultiSign(tx *transaction.Transaction, accType string) error {
|
||||||
|
version, err := c.Client.GetVersion()
|
||||||
|
if err != nil {
|
||||||
|
// error appears only if client
|
||||||
|
// has not been initialized
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
network := version.Protocol.Network
|
||||||
|
|
||||||
|
// Use parameter context to avoid dealing with signature order.
|
||||||
|
pc := context.NewParameterContext("", network, tx)
|
||||||
|
h := c.CommitteeAcc.Contract.ScriptHash()
|
||||||
|
if accType == ConsensusAccountName {
|
||||||
|
h = c.ConsensusAcc.Contract.ScriptHash()
|
||||||
|
}
|
||||||
|
for _, w := range c.Wallets {
|
||||||
|
acc, err := GetWalletAccount(w, accType)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't find %s wallet account: %w", accType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv := acc.PrivateKey()
|
||||||
|
sign := priv.SignHashable(uint32(network), tx)
|
||||||
|
if err := pc.AddSignature(h, acc.Contract, priv.PublicKey(), sign); err != nil {
|
||||||
|
return fmt.Errorf("can't add signature: %w", err)
|
||||||
|
}
|
||||||
|
if len(pc.Items[h].Signatures) == len(acc.Contract.Parameters) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := pc.GetWitness(h)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("incomplete signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tx.Signers {
|
||||||
|
if tx.Signers[i].Account == h {
|
||||||
|
if i < len(tx.Scripts) {
|
||||||
|
tx.Scripts[i] = *w
|
||||||
|
} else if i == len(tx.Scripts) {
|
||||||
|
tx.Scripts = append(tx.Scripts, *w)
|
||||||
|
} else {
|
||||||
|
panic("BUG: invalid signing order")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%s account was not found among transaction signers", accType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmitUpdateNNSGroupScript emits script for updating group key stored in NNS.
|
||||||
|
// First return value is true iff the key is already there and nothing should be done.
|
||||||
|
// Second return value is true iff a domain registration code was emitted.
|
||||||
|
func (c *InitializeContext) EmitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHash util.Uint160, pub *keys.PublicKey) (bool, bool, error) {
|
||||||
|
isAvail, err := NNSIsAvailable(c.Client, nnsHash, client.NNSGroupKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isAvail {
|
||||||
|
currentPub, err := NNSResolveKey(c.ReadOnlyInvoker, nnsHash, client.NNSGroupKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pub.Equal(currentPub) {
|
||||||
|
return true, false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAvail {
|
||||||
|
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||||
|
client.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
FrostfsOpsEmail, int64(3600), int64(600), int64(DefaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit.AppCall(bw.BinWriter, nnsHash, "deleteRecords", callflag.All, "group.frostfs", int64(nns.TXT))
|
||||||
|
emit.AppCall(bw.BinWriter, nnsHash, "addRecord", callflag.All,
|
||||||
|
"group.frostfs", int64(nns.TXT), hex.EncodeToString(pub.Bytes()))
|
||||||
|
|
||||||
|
return false, isAvail, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.Uint160, domain string) ([]byte, bool, error) {
|
||||||
|
ok, err := NNSIsAvailable(c.Client, nnsHash, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
bw := io.NewBufBinWriter()
|
||||||
|
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||||
|
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
FrostfsOpsEmail, int64(3600), int64(600), int64(DefaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
|
if bw.Err != nil {
|
||||||
|
panic(bw.Err)
|
||||||
|
}
|
||||||
|
return bw.Bytes(), false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := NNSResolveHash(c.ReadOnlyInvoker, nnsHash, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return nil, s == expectedHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) NNSRootRegistered(nnsHash util.Uint160, zone string) (bool, error) {
|
||||||
|
res, err := c.CommitteeAct.Call(nnsHash, "isAvailable", "name."+zone)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.State == vmstate.Halt.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) IsUpdated(ctrHash util.Uint160, cs *ContractState) bool {
|
||||||
|
r := management.NewReader(c.ReadOnlyInvoker)
|
||||||
|
realCs, err := r.GetContract(ctrHash)
|
||||||
|
return err == nil && realCs != nil && realCs.NEF.Checksum == cs.NEF.Checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) GetContract(ctrName string) *ContractState {
|
||||||
|
return c.Contracts[ctrName]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *InitializeContext) GetAlphabetDeployItems(i, n int) []any {
|
||||||
|
items := make([]any, 5)
|
||||||
|
items[0] = c.Contracts[NetmapContract].Hash
|
||||||
|
items[1] = c.Contracts[ProxyContract].Hash
|
||||||
|
items[2] = innerring.GlagoliticLetter(i).String()
|
||||||
|
items[3] = int64(i)
|
||||||
|
items[4] = int64(n)
|
||||||
|
return items
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
|
@ -84,3 +88,72 @@ func NewActor(c actor.RPCActor, committeeAcc *wallet.Account) (*actor.Actor, err
|
||||||
Account: committeeAcc,
|
Account: committeeAcc,
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadContract(ctrPath, ctrName string) (*ContractState, error) {
|
||||||
|
rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err)
|
||||||
|
}
|
||||||
|
rawManif, err := os.ReadFile(filepath.Join(ctrPath, "config.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &ContractState{
|
||||||
|
RawNEF: rawNef,
|
||||||
|
RawManifest: rawManif,
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs, cs.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadContractsFromArchive(file io.Reader, names []string) (map[string]*ContractState, error) {
|
||||||
|
m := make(map[string]*ContractState, len(names))
|
||||||
|
for i := range names {
|
||||||
|
m[names[i]] = new(ContractState)
|
||||||
|
}
|
||||||
|
|
||||||
|
gr, err := gzip.NewReader(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("contracts file must be tar.gz archive: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := tar.NewReader(gr)
|
||||||
|
for h, err := r.Next(); ; h, err = r.Next() {
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, _ := filepath.Split(h.Name)
|
||||||
|
ctrName := filepath.Base(dir)
|
||||||
|
|
||||||
|
cs, ok := m[ctrName]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(h.Name, filepath.Join(ctrName, ctrName+"_contract.nef")):
|
||||||
|
cs.RawNEF, err = io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err)
|
||||||
|
}
|
||||||
|
case strings.HasSuffix(h.Name, "config.json"):
|
||||||
|
cs.RawManifest, err = io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[ctrName] = cs
|
||||||
|
}
|
||||||
|
|
||||||
|
for ctrName, cs := range m {
|
||||||
|
if cs.RawNEF == nil {
|
||||||
|
return nil, fmt.Errorf("NEF for %s contract wasn't found", ctrName)
|
||||||
|
}
|
||||||
|
if cs.RawManifest == nil {
|
||||||
|
return nil, fmt.Errorf("manifest for %s contract wasn't found", ctrName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
75
cmd/frostfs-adm/internal/modules/morph/util/wallet.go
Normal file
75
cmd/frostfs-adm/internal/modules/morph/util/wallet.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitializeContractWallet(v *viper.Viper, walletDir string) (*wallet.Wallet, error) {
|
||||||
|
password, err := config.GetPassword(v, ContractWalletPasswordKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := wallet.NewWallet(filepath.Join(walletDir, ContractWalletFilename))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
acc, err := wallet.NewAccount()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = acc.Encrypt(password, keys.NEP2ScryptParams())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.AddAccount(acc)
|
||||||
|
if err := w.SavePretty(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenContractWallet(v *viper.Viper, cmd *cobra.Command, walletDir string) (*wallet.Wallet, error) {
|
||||||
|
p := filepath.Join(walletDir, ContractWalletFilename)
|
||||||
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("can't open wallet: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Printf("Contract group wallet is missing, initialize at %s\n", p)
|
||||||
|
return InitializeContractWallet(v, walletDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
password, err := config.GetPassword(v, ContractWalletPasswordKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range w.Accounts {
|
||||||
|
if err := w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't unlock wallet: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWallet(cmd *cobra.Command, v *viper.Viper, needContracts bool, walletDir string) (*wallet.Wallet, error) {
|
||||||
|
if !needContracts {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return OpenContractWallet(v, cmd, walletDir)
|
||||||
|
}
|
Loading…
Reference in a new issue