[#687] neofs-adm: deploy NeoFS contracts

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-07-21 13:09:59 +03:00 committed by Alex Vanin
parent 7c3c1e9183
commit 0efc7b7fee
2 changed files with 307 additions and 2 deletions

View file

@ -29,6 +29,7 @@ type initializeContext struct {
Hashes []util.Uint256
WaitDuration time.Duration
PollInterval time.Duration
Contracts map[string]*contractState
Command *cobra.Command
}
@ -55,8 +56,18 @@ func initializeSideChainCmd(cmd *cobra.Command, args []string) error {
return err
}
// TODO 3. Deploy NNS contract with ID=0.
// TODO 4. Deploy NeoFS contracts.
// 3. Deploy NNS contract.
cmd.Println("Stage 3: deploy NNS contract.")
if err := initCtx.deployNNS(); err != nil {
return err
}
// 4. Deploy NeoFS contracts.
cmd.Println("Stage 4: deploy NeoFS contracts.")
if err := initCtx.deployContracts(); err != nil {
return err
}
// TODO 5. Setup NeoFS contracts addresses in NNS.
// TODO 6. Register candidates and call alphabet.Vote.
@ -99,6 +110,7 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
WaitDuration: time.Second * 30,
PollInterval: time.Second,
Command: cmd,
Contracts: make(map[string]*contractState),
}
return initCtx, nil

View file

@ -0,0 +1,293 @@
package morph
import (
"encoding/json"
"fmt"
"io/ioutil"
"path"
"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/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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/util"
"github.com/nspcc-dev/neofs-node/pkg/innerring"
)
const (
nnsContract = "nns"
neofsContract = "neofs" // not deployed in side-chain.
processingContract = "processing" // not deployed in side-chain.
alphabetContract = "alphabet"
auditContract = "audit"
balanceContract = "balance"
containerContract = "container"
neofsIDContract = "neofsid"
netmapContract = "netmap"
proxyContract = "proxy"
reputationContract = "reputation"
)
var contractList = []string{
alphabetContract,
auditContract,
balanceContract,
containerContract,
neofsIDContract,
netmapContract,
proxyContract,
reputationContract,
}
type contractState struct {
NEF *nef.File
RawNEF []byte
Manifest *manifest.Manifest
RawManifest []byte
Hash util.Uint160
}
func (c *initializeContext) deployNNS() error {
ctrPath, err := c.Command.Flags().GetString(contractsInitFlag)
if err != nil {
return fmt.Errorf("missing contracts path: %w", err)
}
cs, err := c.readContract(ctrPath, nnsContract)
if err != nil {
return err
}
h := state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name)
if _, err := c.Client.GetContractStateByHash(h); err == nil {
return nil
}
params := getContractDeployParameters(cs.RawNEF, cs.RawManifest, nil)
signer := transaction.Signer{
Account: c.CommitteeAcc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}
mgmtHash, _ := c.Client.GetNativeContractHash(nativenames.Management)
res, err := c.Client.InvokeFunction(mgmtHash, "deploy", params, []transaction.Signer{signer})
if err != nil {
return fmt.Errorf("can't deploy contract: %w", err)
}
tx, err := c.Client.CreateTxFromScript(res.Script, c.CommitteeAcc, res.GasConsumed, 0, []client.SignerAccount{{
Signer: signer,
Account: c.CommitteeAcc,
}})
if err != nil {
return fmt.Errorf("failed to create deploy tx for %s: %w", nnsContract, err)
}
if err := c.multiSignAndSend(tx, committeeAccountName); err != nil {
return fmt.Errorf("can't send deploy transaction: %w", err)
}
return c.awaitTx()
}
func (c *initializeContext) deployContracts() error {
ctrPath, err := c.Command.Flags().GetString(contractsInitFlag)
if err != nil {
return fmt.Errorf("missing contracts path: %w", err)
}
mgmtHash, _ := c.Client.GetNativeContractHash(nativenames.Management)
sender := c.CommitteeAcc.Contract.ScriptHash()
for _, ctrName := range contractList {
cs, err := c.readContract(ctrPath, ctrName)
if err != nil {
return err
}
cs.Hash = state.CreateContractHash(sender, cs.NEF.Checksum, cs.Manifest.Name)
}
var keysParam []smartcontract.Parameter
// alphabet contracts should be deployed by individual nodes to get different hashes.
for i, w := range c.Wallets {
acc, err := getWalletAccount(w, singleAccountName)
if err != nil {
return err
}
cs := c.Contracts[alphabetContract]
ctrHash := state.CreateContractHash(acc.Contract.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name)
if _, err := c.Client.GetContractStateByHash(ctrHash); err == nil {
c.Command.Printf("Stage 4: alphabet contract #%d is already deployed.\n", i)
continue
}
keysParam = append(keysParam, smartcontract.Parameter{
Type: smartcontract.PublicKeyType,
Value: acc.PrivateKey().PublicKey().Bytes(),
})
params := getContractDeployParameters(cs.RawNEF, cs.RawManifest,
c.getAlphabetDeployParameters(i, len(c.Wallets)))
signer := transaction.Signer{
Account: acc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}
res, err := c.Client.InvokeFunction(mgmtHash, "deploy", params, []transaction.Signer{signer})
if err != nil {
return fmt.Errorf("can't deploy contract: %w", err)
}
h, err := c.Client.SignAndPushInvocationTx(res.Script, acc, -1, 0, []client.SignerAccount{{
Signer: signer,
Account: acc,
}})
if err != nil {
return fmt.Errorf("can't push deploy transaction: %w", err)
}
c.Hashes = append(c.Hashes, h)
}
for _, ctrName := range contractList {
if ctrName == alphabetContract {
continue
}
cs := c.Contracts[ctrName]
if _, err := c.Client.GetContractStateByHash(cs.Hash); err == nil {
c.Command.Printf("Stage 4: %s contract is already deployed.\n", ctrName)
continue
}
params := getContractDeployParameters(cs.RawNEF, cs.RawManifest,
c.getContractDeployData(ctrName, keysParam))
signer := transaction.Signer{
Account: c.CommitteeAcc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
}
res, err := c.Client.InvokeFunction(mgmtHash, "deploy", params, []transaction.Signer{signer})
if err != nil {
return fmt.Errorf("can't deploy contract: %w", err)
}
if err := c.sendCommitteeTx(res.Script, res.GasConsumed); err != nil {
return err
}
}
return c.awaitTx()
}
func (c *initializeContext) readContract(ctrPath, ctrName string) (*contractState, error) {
if cs, ok := c.Contracts[ctrName]; ok {
return cs, nil
}
rawNef, err := ioutil.ReadFile(path.Join(ctrPath, ctrName, ctrName+"_contract.nef"))
if err != nil {
return nil, fmt.Errorf("can't read NEF file: %w", err)
}
nf, err := nef.FileFromBytes(rawNef)
if err != nil {
return nil, fmt.Errorf("can't parse NEF file: %w", err)
}
rawManif, err := ioutil.ReadFile(path.Join(ctrPath, ctrName, "config.json"))
if err != nil {
return nil, fmt.Errorf("can't read manifest file: %w", err)
}
m := new(manifest.Manifest)
if err := json.Unmarshal(rawManif, m); err != nil {
return nil, fmt.Errorf("can't parse manifest file: %w", err)
}
c.Contracts[ctrName] = &contractState{
NEF: &nf,
RawNEF: rawNef,
Manifest: m,
RawManifest: rawManif,
}
return c.Contracts[ctrName], nil
}
func getContractDeployParameters(rawNef, rawManif []byte, deployData []smartcontract.Parameter) []smartcontract.Parameter {
return []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: rawNef,
},
{
Type: smartcontract.ByteArrayType,
Value: rawManif,
},
{
Type: smartcontract.ArrayType,
Value: deployData,
},
}
}
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []smartcontract.Parameter) []smartcontract.Parameter {
items := make([]smartcontract.Parameter, 2, 7)
items[0] = newContractParameter(smartcontract.BoolType, false) // notaryDisabled is false
items[1] = newContractParameter(smartcontract.Hash160Type, c.CommitteeAcc.Contract.ScriptHash()) // owner is committee
switch ctrName {
case neofsContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[processingContract].Hash),
newContractParameter(smartcontract.ArrayType, keysParam))
case processingContract:
items = append(items, newContractParameter(smartcontract.Hash160Type, c.Contracts[neofsContract].Hash))
return items[1:] // no notary info
case auditContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash))
case balanceContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash))
case containerContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[balanceContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[neofsIDContract].Hash))
case neofsIDContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash))
case netmapContract:
items = append(items,
newContractParameter(smartcontract.Hash160Type, c.Contracts[balanceContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash),
newContractParameter(smartcontract.ArrayType, keysParam))
case proxyContract:
items = append(items, newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash))
case reputationContract:
default:
panic(fmt.Sprintf("invalid contract name: %s", ctrName))
}
return items
}
func (c *initializeContext) getAlphabetDeployParameters(i, n int) []smartcontract.Parameter {
return []smartcontract.Parameter{
newContractParameter(smartcontract.BoolType, false),
newContractParameter(smartcontract.Hash160Type, c.CommitteeAcc.Contract.ScriptHash()),
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
newContractParameter(smartcontract.Hash160Type, c.Contracts[proxyContract].Hash),
newContractParameter(smartcontract.StringType, innerring.GlagoliticLetter(i).String()),
newContractParameter(smartcontract.IntegerType, int64(i)),
newContractParameter(smartcontract.IntegerType, int64(n)),
}
}
func newContractParameter(typ smartcontract.ParamType, value interface{}) smartcontract.Parameter {
return smartcontract.Parameter{
Type: typ,
Value: value,
}
}