[#1633] neofs-adm: Allow to deploy arbitrary contracts

Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
This commit is contained in:
Evgenii Stratonikov 2022-07-27 17:57:30 +03:00 committed by fyrchik
parent 38558a3238
commit fc06b6e89a
3 changed files with 147 additions and 12 deletions

View file

@ -0,0 +1,121 @@
package morph
import (
"fmt"
"os"
"strings"
"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/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
contractPathFlag = "contract"
)
var deployCmd = &cobra.Command{
Use: "deploy",
Short: "Deploy additional smart-contracts",
Long: `Deploy additional smart-contract which are not related to core.
All contracts are deployed by the committee, so access to the alphabet wallets is required.`,
PreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
},
RunE: deployContractCmd,
}
func init() {
ff := deployCmd.Flags()
ff.String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
_ = deployCmd.MarkFlagFilename(alphabetWalletsFlag)
ff.StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
ff.String(contractPathFlag, "", "Path to the contract directory")
_ = deployCmd.MarkFlagFilename(contractPathFlag)
}
func deployContractCmd(cmd *cobra.Command, _ []string) error {
v := viper.GetViper()
c, err := newInitializeContext(cmd, v)
if err != nil {
return fmt.Errorf("initialization error: %w", err)
}
defer c.close()
ctrPath, _ := cmd.Flags().GetString(contractPathFlag)
ctrName, err := probeContractName(ctrPath)
if err != nil {
return err
}
cs, err := readContract(ctrPath, ctrName)
if err != nil {
return err
}
cs.Hash = state.CreateContractHash(
c.CommitteeAcc.Contract.ScriptHash(),
cs.NEF.Checksum,
cs.Manifest.Name)
err = c.addManifestGroup(cs.Hash, cs)
if err != nil {
return fmt.Errorf("can't sign manifest group: %v", err)
}
params := getContractDeployParameters(cs, nil)
callHash := c.nativeHash(nativenames.Management)
nnsCs, err := c.Client.GetContractStateByID(1)
if err != nil {
return fmt.Errorf("can't fetch NNS contract state: %w", err)
}
domain := ctrName + ".neofs"
s, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
if err != nil {
return err
}
w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, callHash, deployMethodName, callflag.All, params...)
emit.Opcodes(w.BinWriter, opcode.DROP) // contract state on stack
w.WriteBytes(s)
if w.Err != nil {
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
}
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
if err := c.sendCommitteeTx(w.Bytes(), -1, false); err != nil {
return err
}
return c.awaitTx()
}
func probeContractName(ctrPath string) (string, error) {
ds, err := os.ReadDir(ctrPath)
if err != nil {
return "", fmt.Errorf("can't read directory: %w", err)
}
var ctrName string
for i := range ds {
if strings.HasSuffix(ds[i].Name(), "_contract.nef") {
ctrName = strings.TrimSuffix(ds[i].Name(), "_contract.nef")
break
}
}
if ctrName == "" {
return "", fmt.Errorf("can't find any NEF files in %s", ctrPath)
}
return ctrName, nil
}

View file

@ -379,14 +379,9 @@ func (c *initializeContext) readContracts(names []string) error {
if c.ContractPath != "" && fi.IsDir() { if c.ContractPath != "" && fi.IsDir() {
for _, ctrName := range names { for _, ctrName := range names {
cs := new(contractState) cs, err := readContract(filepath.Join(c.ContractPath, ctrName), ctrName)
cs.RawNEF, err = os.ReadFile(filepath.Join(c.ContractPath, ctrName, ctrName+"_contract.nef"))
if err != nil { if err != nil {
return fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err) return err
}
cs.RawManifest, err = os.ReadFile(filepath.Join(c.ContractPath, ctrName, "config.json"))
if err != nil {
return fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
} }
c.Contracts[ctrName] = cs c.Contracts[ctrName] = cs
} }
@ -408,17 +403,16 @@ func (c *initializeContext) readContracts(names []string) error {
return err return err
} }
for _, name := range names { for _, name := range names {
if err := m[name].parse(); err != nil {
return err
}
c.Contracts[name] = m[name] c.Contracts[name] = m[name]
} }
} }
for _, ctrName := range names { for _, ctrName := range names {
cs := c.Contracts[ctrName]
if err := cs.parse(); err != nil {
return err
}
if ctrName != alphabetContract { if ctrName != alphabetContract {
cs := c.Contracts[ctrName]
cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(), cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(),
cs.NEF.Checksum, cs.Manifest.Name) cs.NEF.Checksum, cs.Manifest.Name)
} }
@ -426,6 +420,24 @@ func (c *initializeContext) readContracts(names []string) error {
return nil return nil
} }
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 (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 {

View file

@ -235,6 +235,8 @@ func init() {
initCmd.Flags().String(protoConfigPath, "", "path to the consensus node configuration") initCmd.Flags().String(protoConfigPath, "", "path to the consensus node configuration")
initCmd.Flags().String(localDumpFlag, "", "path to the blocks dump file") initCmd.Flags().String(localDumpFlag, "", "path to the blocks dump file")
RootCmd.AddCommand(deployCmd)
RootCmd.AddCommand(generateStorageCmd) RootCmd.AddCommand(generateStorageCmd)
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir") generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir")
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")