[#733] neofs-adm: read contracts on start

Fail early and reduce disk operations when reading from archive.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-10-25 16:19:56 +03:00 committed by Alex Vanin
parent 0b6350d463
commit 088c894f44
6 changed files with 105 additions and 107 deletions

View file

@ -6,18 +6,17 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/google/go-github/v39/github" "github.com/google/go-github/v39/github"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func downloadContractsFromGithub(cmd *cobra.Command) (string, error) { func downloadContractsFromGithub(cmd *cobra.Command) (io.ReadCloser, error) {
gcl := github.NewClient(nil) gcl := github.NewClient(nil)
release, _, err := gcl.Repositories.GetLatestRelease(context.Background(), "nspcc-dev", "neofs-contract") release, _, err := gcl.Repositories.GetLatestRelease(context.Background(), "nspcc-dev", "neofs-contract")
if err != nil { if err != nil {
return "", fmt.Errorf("can't fetch release info: %w", err) return nil, fmt.Errorf("can't fetch release info: %w", err)
} }
cmd.Printf("Found %s (%s), downloading...\n", release.GetTagName(), release.GetName()) cmd.Printf("Found %s (%s), downloading...\n", release.GetTagName(), release.GetName())
@ -30,21 +29,12 @@ func downloadContractsFromGithub(cmd *cobra.Command) (string, error) {
} }
} }
if url == "" { if url == "" {
return "", errors.New("can't find contracts archive in release assets") return nil, errors.New("can't find contracts archive in release assets")
} }
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
return "", fmt.Errorf("can't fetch contracts archive: %w", err) return nil, fmt.Errorf("can't fetch contracts archive: %w", err)
} }
defer resp.Body.Close() return resp.Body, nil
f, err := os.CreateTemp("", "neofs-contract-*.tar.gz")
if err != nil {
return "", fmt.Errorf("can't allocate temporary file: %w", err)
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return f.Name(), err
} }

View file

@ -122,18 +122,12 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
return nil, fmt.Errorf("max object size must be positive") return nil, fmt.Errorf("max object size must be positive")
} }
} }
if cmd.Name() == "update-contracts" || cmd.Name() == "init" {
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
if needContracts {
ctrPath, err = cmd.Flags().GetString(contractsInitFlag) ctrPath, err = cmd.Flags().GetString(contractsInitFlag)
if err != nil { if err != nil {
return nil, fmt.Errorf("missing contracts path: %w", err) return nil, fmt.Errorf("invalid contracts path: %w", err)
}
if ctrPath == "" {
cmd.Println("Contracts flag is missing, latest release will be fetched from Github.")
ctrPath, err = downloadContractsFromGithub(cmd)
if err != nil {
return nil, err
}
cmd.Printf("Saved to %s\n", ctrPath)
} }
} }
@ -177,6 +171,13 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
Natives: nativeHashes, Natives: nativeHashes,
} }
if needContracts {
err := initCtx.readContracts(fullContractList)
if err != nil {
return nil, err
}
}
return initCtx, nil return initCtx, nil
} }

View file

@ -54,7 +54,8 @@ const (
defaultEigenTrustAlpha = "0.1" defaultEigenTrustAlpha = "0.1"
) )
var contractList = []string{ var (
contractList = []string{
auditContract, auditContract,
balanceContract, balanceContract,
containerContract, containerContract,
@ -62,7 +63,15 @@ var contractList = []string{
netmapContract, netmapContract,
proxyContract, proxyContract,
reputationContract, reputationContract,
} }
fullContractList = append([]string{
neofsContract,
processingContract,
nnsContract,
alphabetContract,
}, contractList...)
)
type contractState struct { type contractState struct {
NEF *nef.File NEF *nef.File
@ -75,10 +84,7 @@ type contractState struct {
const updateMethodName = "update" const updateMethodName = "update"
func (c *initializeContext) deployNNS(method string) error { func (c *initializeContext) deployNNS(method string) error {
cs, err := c.readContract(nnsContract) cs := c.getContract(nnsContract)
if err != nil {
return err
}
if _, err := c.Client.GetContractStateByHash(cs.Hash); err == nil && method != updateMethodName { if _, err := c.Client.GetContractStateByHash(cs.Hash); err == nil && method != updateMethodName {
return nil return nil
@ -128,17 +134,7 @@ func (c *initializeContext) deployNNS(method string) error {
func (c *initializeContext) deployContracts(method string) error { func (c *initializeContext) deployContracts(method string) error {
mgmtHash := c.nativeHash(nativenames.Management) mgmtHash := c.nativeHash(nativenames.Management)
for _, ctrName := range contractList { alphaCs := c.getContract(alphabetContract)
_, err := c.readContract(ctrName)
if err != nil {
return err
}
}
alphaCs, err := c.readContract(alphabetContract)
if err != nil {
return err
}
nnsCs, err := c.Client.GetContractStateByID(1) nnsCs, err := c.Client.GetContractStateByID(1)
if err != nil { if err != nil {
@ -245,58 +241,85 @@ func (c *initializeContext) deployContracts(method string) error {
return c.awaitTx() return c.awaitTx()
} }
func (c *initializeContext) readContract(ctrName string) (*contractState, error) { func (c *initializeContext) getContract(ctrName string) *contractState {
if cs, ok := c.Contracts[ctrName]; ok { return c.Contracts[ctrName]
return cs, nil }
}
fi, err := os.Stat(c.ContractPath) 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 { if err != nil {
return nil, fmt.Errorf("invalid contracts path: %w", err) return fmt.Errorf("invalid contracts path: %w", err)
}
} }
var cs *contractState if c.ContractPath != "" && fi.IsDir() {
if fi.IsDir() { for _, ctrName := range names {
cs = new(contractState) cs := new(contractState)
cs.RawNEF, err = ioutil.ReadFile(path.Join(c.ContractPath, ctrName, ctrName+"_contract.nef")) cs.RawNEF, err = ioutil.ReadFile(path.Join(c.ContractPath, ctrName, ctrName+"_contract.nef"))
if err != nil { if err != nil {
return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err) return fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err)
} }
cs.RawManifest, err = ioutil.ReadFile(path.Join(c.ContractPath, ctrName, "config.json")) cs.RawManifest, err = ioutil.ReadFile(path.Join(c.ContractPath, ctrName, "config.json"))
if err != nil { if err != nil {
return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err) return fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err)
}
c.Contracts[ctrName] = cs
} }
} else { } else {
f, err := os.Open(c.ContractPath) var r io.ReadCloser
if err != nil { if c.ContractPath == "" {
return nil, fmt.Errorf("can't open contracts archive: %w", err) c.Command.Println("Contracts flag is missing, latest release will be fetched from Github.")
r, err = downloadContractsFromGithub(c.Command)
} else {
r, err = os.Open(c.ContractPath)
} }
defer f.Close() if err != nil {
return fmt.Errorf("can't open contracts archive: %w", err)
}
defer r.Close()
m, err := readContractsFromArchive(f, []string{ctrName}) m, err := readContractsFromArchive(r, names)
if err != nil { if err != nil {
return nil, err return err
}
for _, name := range names {
c.Contracts[name] = m[name]
} }
cs = m[ctrName]
} }
nf, err := nef.FileFromBytes(cs.RawNEF) for _, ctrName := range names {
if err != nil { cs := c.Contracts[ctrName]
return nil, fmt.Errorf("can't parse NEF file: %w", err) if err := cs.parse(); err != nil {
} return err
cs.NEF = &nf
cs.Manifest = new(manifest.Manifest)
if err := json.Unmarshal(cs.RawManifest, cs.Manifest); err != nil {
return nil, fmt.Errorf("can't parse manifest file: %w", err)
} }
if ctrName != alphabetContract { if ctrName != alphabetContract {
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)
} }
c.Contracts[ctrName] = cs }
return cs, nil return nil
}
func (cs *contractState) parse() error {
nf, err := nef.FileFromBytes(cs.RawNEF)
if err != nil {
return fmt.Errorf("can't parse NEF file: %w", err)
}
m := new(manifest.Manifest)
if err := json.Unmarshal(cs.RawManifest, m); err != nil {
return fmt.Errorf("can't parse manifest file: %w", err)
}
cs.NEF = &nf
cs.Manifest = m
return nil
} }
func readContractsFromArchive(file io.Reader, names []string) (map[string]*contractState, error) { func readContractsFromArchive(file io.Reader, names []string) (map[string]*contractState, error) {

View file

@ -24,10 +24,7 @@ const defaultNameServiceSysfee = 4000_0000
const defaultRegisterSysfee = 10_0000_0000 + defaultNameServiceDomainPrice const defaultRegisterSysfee = 10_0000_0000 + defaultNameServiceDomainPrice
func (c *initializeContext) setNNS() error { func (c *initializeContext) setNNS() error {
nnsCs, err := c.readContract(nnsContract) nnsCs := c.getContract(nnsContract)
if err != nil {
return err
}
ok, err := c.nnsRootRegistered(nnsCs.Hash) ok, err := c.nnsRootRegistered(nnsCs.Hash)
if err != nil { if err != nil {
@ -43,10 +40,7 @@ func (c *initializeContext) setNNS() error {
} }
} }
alphaCs, err := c.readContract(alphabetContract) alphaCs := c.getContract(alphabetContract)
if err != nil {
return fmt.Errorf("can't read alphabet contract: %w", err)
}
for i, acc := range c.Accounts { for i, acc := range c.Accounts {
alphaCs.Hash = state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name) alphaCs.Hash = state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name)
@ -58,10 +52,7 @@ func (c *initializeContext) setNNS() error {
} }
for _, ctrName := range contractList { for _, ctrName := range contractList {
cs, err := c.readContract(ctrName) cs := c.getContract(ctrName)
if err != nil {
return err
}
domain := ctrName + ".neofs" domain := ctrName + ".neofs"
if err := c.nnsRegisterDomain(nnsCs.Hash, cs.Hash, domain); err != nil { if err := c.nnsRegisterDomain(nnsCs.Hash, cs.Hash, domain); err != nil {

View file

@ -71,11 +71,7 @@ func (c *initializeContext) transferNEOToAlphabetContracts() error {
return err return err
} }
cs, err := c.readContract(alphabetContract) cs := c.getContract(alphabetContract)
if err != nil {
return fmt.Errorf("can't read alphabet contract: %w", err)
}
amount := initialAlphabetNEOAmount / len(c.Wallets) amount := initialAlphabetNEOAmount / len(c.Wallets)
bw := io.NewBufBinWriter() bw := io.NewBufBinWriter()

View file

@ -132,10 +132,7 @@ func (c *initializeContext) multiSign(tx *transaction.Transaction, accType strin
func (c *initializeContext) transferGASToProxy() error { func (c *initializeContext) transferGASToProxy() error {
gasHash := c.nativeHash(nativenames.Gas) gasHash := c.nativeHash(nativenames.Gas)
proxyCs, err := c.readContract(proxyContract) proxyCs := c.getContract(proxyContract)
if err != nil {
return err
}
bal, err := c.Client.NEP17BalanceOf(gasHash, proxyCs.Hash) bal, err := c.Client.NEP17BalanceOf(gasHash, proxyCs.Hash)
if err != nil || bal > 0 { if err != nil || bal > 0 {