2021-07-21 10:09:59 +00:00
|
|
|
package morph
|
|
|
|
|
|
|
|
import (
|
2021-10-21 14:57:38 +00:00
|
|
|
"archive/tar"
|
|
|
|
"compress/gzip"
|
2022-02-02 14:19:27 +00:00
|
|
|
"encoding/hex"
|
2021-07-21 10:09:59 +00:00
|
|
|
"encoding/json"
|
2021-11-29 10:15:03 +00:00
|
|
|
"errors"
|
2021-07-21 10:09:59 +00:00
|
|
|
"fmt"
|
2021-10-21 14:57:38 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2022-02-02 13:28:08 +00:00
|
|
|
"path/filepath"
|
2021-10-21 14:57:38 +00:00
|
|
|
"strings"
|
2021-07-21 10:09:59 +00:00
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
|
|
|
morphClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
2023-04-11 11:59:24 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
2021-07-21 10:09:59 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2022-09-05 10:04:24 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
2021-12-27 10:26:22 +00:00
|
|
|
io2 "github.com/nspcc-dev/neo-go/pkg/io"
|
2022-07-28 16:22:32 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
2022-08-29 19:31:32 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
2022-11-23 14:44:03 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
2023-04-13 14:42:59 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
2021-07-21 10:09:59 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2021-12-27 11:13:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2021-07-21 10:09:59 +00:00
|
|
|
"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"
|
2021-12-27 11:13:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2023-04-13 14:42:59 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2022-07-18 08:33:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
2021-07-21 10:09:59 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
nnsContract = "nns"
|
2023-01-10 12:50:17 +00:00
|
|
|
frostfsContract = "frostfs" // not deployed in side-chain.
|
2021-07-21 10:09:59 +00:00
|
|
|
processingContract = "processing" // not deployed in side-chain.
|
|
|
|
alphabetContract = "alphabet"
|
|
|
|
auditContract = "audit"
|
|
|
|
balanceContract = "balance"
|
|
|
|
containerContract = "container"
|
2023-01-10 13:01:54 +00:00
|
|
|
frostfsIDContract = "frostfsid"
|
2021-07-21 10:09:59 +00:00
|
|
|
netmapContract = "netmap"
|
|
|
|
proxyContract = "proxy"
|
|
|
|
)
|
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
var (
|
|
|
|
contractList = []string{
|
|
|
|
auditContract,
|
|
|
|
balanceContract,
|
|
|
|
containerContract,
|
2023-01-10 13:01:54 +00:00
|
|
|
frostfsIDContract,
|
2021-10-25 13:19:56 +00:00
|
|
|
netmapContract,
|
|
|
|
proxyContract,
|
|
|
|
}
|
|
|
|
|
|
|
|
fullContractList = append([]string{
|
2023-01-10 12:50:17 +00:00
|
|
|
frostfsContract,
|
2021-10-25 13:19:56 +00:00
|
|
|
processingContract,
|
|
|
|
nnsContract,
|
|
|
|
alphabetContract,
|
|
|
|
}, contractList...)
|
2023-04-13 14:42:59 +00:00
|
|
|
|
|
|
|
netmapConfigKeys = []string{
|
2023-04-11 11:59:24 +00:00
|
|
|
netmap.EpochDurationConfig,
|
|
|
|
netmap.MaxObjectSizeConfig,
|
|
|
|
netmap.AuditFeeConfig,
|
|
|
|
netmap.ContainerFeeConfig,
|
|
|
|
netmap.ContainerAliasFeeConfig,
|
|
|
|
netmap.BasicIncomeRateConfig,
|
|
|
|
netmap.IrCandidateFeeConfig,
|
|
|
|
netmap.WithdrawFeeConfig,
|
|
|
|
netmap.HomomorphicHashingDisabledKey,
|
|
|
|
netmap.MaintenanceModeAllowedConfig,
|
2023-04-13 14:42:59 +00:00
|
|
|
}
|
2021-10-25 13:19:56 +00:00
|
|
|
)
|
2021-07-21 10:09:59 +00:00
|
|
|
|
|
|
|
type contractState struct {
|
|
|
|
NEF *nef.File
|
|
|
|
RawNEF []byte
|
|
|
|
Manifest *manifest.Manifest
|
|
|
|
RawManifest []byte
|
|
|
|
Hash util.Uint160
|
|
|
|
}
|
|
|
|
|
2021-11-29 10:15:03 +00:00
|
|
|
const (
|
|
|
|
updateMethodName = "update"
|
|
|
|
deployMethodName = "deploy"
|
|
|
|
)
|
2021-09-30 15:11:35 +00:00
|
|
|
|
2021-09-21 12:21:38 +00:00
|
|
|
func (c *initializeContext) deployNNS(method string) error {
|
2021-10-25 13:19:56 +00:00
|
|
|
cs := c.getContract(nnsContract)
|
2021-11-29 12:33:46 +00:00
|
|
|
h := cs.Hash
|
2021-07-21 10:09:59 +00:00
|
|
|
|
2022-02-02 14:19:27 +00:00
|
|
|
nnsCs, err := c.nnsContractState()
|
2021-11-29 12:33:46 +00:00
|
|
|
if err == nil {
|
2022-02-02 14:19:27 +00:00
|
|
|
if nnsCs.NEF.Checksum == cs.NEF.Checksum {
|
2022-11-08 12:34:02 +00:00
|
|
|
if method == deployMethodName {
|
|
|
|
c.Command.Println("NNS contract is already deployed.")
|
|
|
|
} else {
|
|
|
|
c.Command.Println("NNS contract is already updated.")
|
|
|
|
}
|
2021-11-29 12:33:46 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-02-02 14:19:27 +00:00
|
|
|
h = nnsCs.Hash
|
2021-11-29 12:33:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = c.addManifestGroup(h, cs)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
2022-04-08 13:23:34 +00:00
|
|
|
params := getContractDeployParameters(cs, nil)
|
2021-07-21 10:09:59 +00:00
|
|
|
signer := transaction.Signer{
|
|
|
|
Account: c.CommitteeAcc.Contract.ScriptHash(),
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
}
|
|
|
|
|
2022-11-23 14:44:03 +00:00
|
|
|
invokeHash := management.Hash
|
2021-10-07 13:32:11 +00:00
|
|
|
if method == updateMethodName {
|
2022-04-08 13:23:34 +00:00
|
|
|
invokeHash = nnsCs.Hash
|
2021-09-21 12:21:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-08 13:23:34 +00:00
|
|
|
res, err := invokeFunction(c.Client, invokeHash, method, params, []transaction.Signer{signer})
|
2021-07-21 10:09:59 +00:00
|
|
|
if err != nil {
|
2021-09-21 12:15:20 +00:00
|
|
|
return fmt.Errorf("can't deploy NNS contract: %w", err)
|
|
|
|
}
|
2022-07-18 08:33:53 +00:00
|
|
|
if res.State != vmstate.Halt.String() {
|
2021-09-21 12:15:20 +00:00
|
|
|
return fmt.Errorf("can't deploy NNS contract: %s", res.FaultException)
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
2022-07-28 16:22:32 +00:00
|
|
|
tx, err := c.Client.CreateTxFromScript(res.Script, c.CommitteeAcc, res.GasConsumed, 0, []rpcclient.SignerAccount{{
|
2021-07-21 10:09:59 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2021-12-27 10:26:22 +00:00
|
|
|
func (c *initializeContext) updateContracts() error {
|
2021-10-25 13:19:56 +00:00
|
|
|
alphaCs := c.getContract(alphabetContract)
|
2021-07-30 11:29:05 +00:00
|
|
|
|
2022-02-02 14:19:27 +00:00
|
|
|
nnsCs, err := c.nnsContractState()
|
2021-08-06 11:28:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nnsHash := nnsCs.Hash
|
|
|
|
|
2021-12-27 10:26:22 +00:00
|
|
|
w := io2.NewBufBinWriter()
|
|
|
|
|
2021-12-27 11:13:48 +00:00
|
|
|
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
|
|
|
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
|
|
|
// The generated script is as following.
|
2022-07-27 08:06:58 +00:00
|
|
|
// 1. Initialize static slot for alphabet NEF.
|
|
|
|
// 2. Store NEF into the static slot.
|
2021-12-27 11:13:48 +00:00
|
|
|
// 3. Push parameters for each alphabet contract on stack.
|
2022-07-27 08:06:58 +00:00
|
|
|
// 4. Add contract group to the manifest.
|
|
|
|
// 5. For each alphabet contract, invoke `update` using parameters on stack and
|
|
|
|
// NEF from step 2 and manifest from step 4.
|
|
|
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
2021-12-27 11:13:48 +00:00
|
|
|
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
2022-07-27 08:06:58 +00:00
|
|
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
keysParam, err := c.deployAlphabetAccounts(nnsHash, w, alphaCs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-12-27 10:26:22 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
w.Reset()
|
2022-07-27 08:06:58 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
if err = c.deployOrUpdateContracts(w, nnsHash, keysParam); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-07-27 08:06:58 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
|
|
|
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-12-27 11:13:48 +00:00
|
|
|
}
|
2023-03-22 15:29:52 +00:00
|
|
|
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
|
|
|
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
|
|
emit.Int(w.BinWriter, 1)
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
|
|
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
2021-12-27 10:26:22 +00:00
|
|
|
|
2022-08-29 19:31:32 +00:00
|
|
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
2023-03-22 15:29:52 +00:00
|
|
|
return err
|
2022-07-27 08:06:58 +00:00
|
|
|
}
|
2023-03-22 15:29:52 +00:00
|
|
|
return c.awaitTx()
|
|
|
|
}
|
2021-12-27 10:26:22 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error {
|
2022-09-02 08:16:59 +00:00
|
|
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
|
|
|
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
|
|
|
emit.AppCall(w.BinWriter, nnsHash, "setPrice", callflag.All, 1)
|
|
|
|
|
2021-12-27 10:26:22 +00:00
|
|
|
for _, ctrName := range contractList {
|
|
|
|
cs := c.getContract(ctrName)
|
|
|
|
|
|
|
|
method := updateMethodName
|
2022-12-23 17:35:35 +00:00
|
|
|
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, ctrName+".frostfs")
|
2021-12-27 10:26:22 +00:00
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, errMissingNNSRecord) {
|
|
|
|
// if contract not found we deploy it instead of update
|
|
|
|
method = deployMethodName
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("can't resolve hash for contract update: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 07:49:19 +00:00
|
|
|
err = c.addManifestGroup(ctrHash, cs)
|
2021-11-29 12:33:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-11-23 14:44:03 +00:00
|
|
|
invokeHash := management.Hash
|
2021-11-24 06:19:39 +00:00
|
|
|
if method == updateMethodName {
|
2021-12-27 10:26:22 +00:00
|
|
|
invokeHash = ctrHash
|
|
|
|
}
|
|
|
|
|
2023-04-13 14:42:59 +00:00
|
|
|
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam, updateMethodName))
|
2022-10-17 08:46:17 +00:00
|
|
|
res, err := c.CommitteeAct.MakeCall(invokeHash, method, params...)
|
2021-12-27 10:26:22 +00:00
|
|
|
if err != nil {
|
2022-11-08 12:34:02 +00:00
|
|
|
if method != updateMethodName || !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
|
|
|
return fmt.Errorf("deploy contract: %w", err)
|
|
|
|
}
|
|
|
|
c.Command.Printf("%s contract is already updated.\n", ctrName)
|
|
|
|
continue
|
2021-12-27 10:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteBytes(res.Script)
|
|
|
|
|
|
|
|
if method == deployMethodName {
|
|
|
|
// same actions are done in initializeContext.setNNS, can be unified
|
2022-12-23 17:35:35 +00:00
|
|
|
domain := ctrName + ".frostfs"
|
2022-09-02 08:16:59 +00:00
|
|
|
script, ok, err := c.nnsRegisterDomainScript(nnsHash, cs.Hash, domain)
|
2021-11-24 06:19:39 +00:00
|
|
|
if err != nil {
|
2021-12-27 10:26:22 +00:00
|
|
|
return err
|
2021-11-24 06:19:39 +00:00
|
|
|
}
|
2022-09-02 07:59:09 +00:00
|
|
|
if !ok {
|
2022-09-02 08:16:59 +00:00
|
|
|
w.WriteBytes(script)
|
2022-09-05 09:54:24 +00:00
|
|
|
emit.AppCall(w.BinWriter, nnsHash, "deleteRecords", callflag.All, domain, int64(nns.TXT))
|
2022-09-02 07:59:09 +00:00
|
|
|
emit.AppCall(w.BinWriter, nnsHash, "addRecord", callflag.All,
|
|
|
|
domain, int64(nns.TXT), cs.Hash.StringLE())
|
2022-09-05 10:04:24 +00:00
|
|
|
emit.AppCall(w.BinWriter, nnsHash, "addRecord", callflag.All,
|
|
|
|
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
2021-12-27 10:26:22 +00:00
|
|
|
}
|
|
|
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
2021-11-24 06:19:39 +00:00
|
|
|
}
|
2021-12-27 10:26:22 +00:00
|
|
|
}
|
2023-03-22 15:29:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-12-27 10:26:22 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *contractState) ([]any, error) {
|
|
|
|
var keysParam []any
|
2021-11-29 12:58:45 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
baseGroups := alphaCs.Manifest.Groups
|
|
|
|
|
|
|
|
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
|
|
|
for i, acc := range c.Accounts {
|
|
|
|
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't resolve hash for contract update: %w", err)
|
|
|
|
}
|
2022-09-02 08:16:59 +00:00
|
|
|
|
2023-03-22 15:29:52 +00:00
|
|
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
|
|
|
|
|
|
|
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
|
|
|
emit.Array(w.BinWriter, params...)
|
|
|
|
|
|
|
|
alphaCs.Manifest.Groups = baseGroups
|
|
|
|
err = c.addManifestGroup(ctrHash, alphaCs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't sign manifest group: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
|
|
emit.Int(w.BinWriter, 3)
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
|
|
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
|
|
|
}
|
2022-08-29 19:31:32 +00:00
|
|
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
2023-03-22 15:29:52 +00:00
|
|
|
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c.Command.Println("Alphabet contracts are already updated.")
|
2021-12-27 10:26:22 +00:00
|
|
|
}
|
2023-03-22 15:29:52 +00:00
|
|
|
|
|
|
|
return keysParam, nil
|
2021-12-27 10:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *initializeContext) deployContracts() error {
|
|
|
|
alphaCs := c.getContract(alphabetContract)
|
|
|
|
|
2023-02-21 11:42:45 +00:00
|
|
|
var keysParam []any
|
2021-12-27 10:26:22 +00:00
|
|
|
|
2022-06-07 14:32:58 +00:00
|
|
|
baseGroups := alphaCs.Manifest.Groups
|
|
|
|
|
2021-12-27 10:26:22 +00:00
|
|
|
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
|
|
|
for i, acc := range c.Accounts {
|
|
|
|
ctrHash := state.CreateContractHash(acc.Contract.ScriptHash(), alphaCs.NEF.Checksum, alphaCs.Manifest.Name)
|
2021-11-24 06:19:39 +00:00
|
|
|
if c.isUpdated(ctrHash, alphaCs) {
|
2021-08-06 11:28:07 +00:00
|
|
|
c.Command.Printf("Alphabet contract #%d is already deployed.\n", i)
|
2021-07-21 10:09:59 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-06-07 14:32:58 +00:00
|
|
|
alphaCs.Manifest.Groups = baseGroups
|
|
|
|
err := c.addManifestGroup(ctrHash, alphaCs)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-04-07 12:47:13 +00:00
|
|
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
2022-04-08 13:23:34 +00:00
|
|
|
params := getContractDeployParameters(alphaCs, c.getAlphabetDeployItems(i, len(c.Wallets)))
|
2021-07-21 10:09:59 +00:00
|
|
|
|
2022-08-29 19:31:32 +00:00
|
|
|
act, err := actor.NewSimple(c.Client, acc)
|
2021-07-21 10:09:59 +00:00
|
|
|
if err != nil {
|
2022-08-29 19:31:32 +00:00
|
|
|
return fmt.Errorf("could not create actor: %w", err)
|
2021-09-21 12:15:20 +00:00
|
|
|
}
|
|
|
|
|
2022-11-23 14:44:03 +00:00
|
|
|
txHash, vub, err := act.SendCall(management.Hash, deployMethodName, params...)
|
2022-08-29 19:31:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't deploy alphabet #%d contract: %w", i, err)
|
2021-09-21 12:19:22 +00:00
|
|
|
}
|
2022-08-29 19:31:32 +00:00
|
|
|
|
2022-09-14 10:11:53 +00:00
|
|
|
c.SentTxs = append(c.SentTxs, hashVUBPair{hash: txHash, vub: vub})
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, ctrName := range contractList {
|
2021-11-29 10:50:49 +00:00
|
|
|
cs := c.getContract(ctrName)
|
2021-11-24 06:19:39 +00:00
|
|
|
|
|
|
|
ctrHash := cs.Hash
|
|
|
|
if c.isUpdated(ctrHash, cs) {
|
2021-08-06 11:28:07 +00:00
|
|
|
c.Command.Printf("%s contract is already deployed.\n", ctrName)
|
2021-07-21 10:09:59 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-11-29 12:33:46 +00:00
|
|
|
err := c.addManifestGroup(ctrHash, cs)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't sign manifest group: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-04-13 14:42:59 +00:00
|
|
|
params := getContractDeployParameters(cs, c.getContractDeployData(ctrName, keysParam, deployMethodName))
|
2022-11-23 14:44:03 +00:00
|
|
|
res, err := c.CommitteeAct.MakeCall(management.Hash, deployMethodName, params...)
|
2021-07-21 10:09:59 +00:00
|
|
|
if err != nil {
|
2021-09-21 12:15:20 +00:00
|
|
|
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
|
|
|
}
|
2021-07-21 10:09:59 +00:00
|
|
|
|
2022-08-29 19:31:32 +00:00
|
|
|
if err := c.sendCommitteeTx(res.Script, false); err != nil {
|
2021-07-21 10:09:59 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.awaitTx()
|
|
|
|
}
|
|
|
|
|
2021-11-24 06:19:39 +00:00
|
|
|
func (c *initializeContext) isUpdated(ctrHash util.Uint160, cs *contractState) bool {
|
|
|
|
realCs, err := c.Client.GetContractStateByHash(ctrHash)
|
|
|
|
return err == nil && realCs.NEF.Checksum == cs.NEF.Checksum
|
|
|
|
}
|
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
func (c *initializeContext) getContract(ctrName string) *contractState {
|
|
|
|
return c.Contracts[ctrName]
|
|
|
|
}
|
2021-10-21 14:57:38 +00:00
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
func (c *initializeContext) readContracts(names []string) error {
|
|
|
|
var (
|
|
|
|
fi os.FileInfo
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
if c.ContractPath != "" {
|
|
|
|
fi, err = os.Stat(c.ContractPath)
|
2021-10-21 14:57:38 +00:00
|
|
|
if err != nil {
|
2021-10-25 13:19:56 +00:00
|
|
|
return fmt.Errorf("invalid contracts path: %w", err)
|
2021-10-21 14:57:38 +00:00
|
|
|
}
|
2021-10-25 13:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.ContractPath != "" && fi.IsDir() {
|
|
|
|
for _, ctrName := range names {
|
2022-07-27 14:57:30 +00:00
|
|
|
cs, err := readContract(filepath.Join(c.ContractPath, ctrName), ctrName)
|
2021-10-25 13:19:56 +00:00
|
|
|
if err != nil {
|
2022-07-27 14:57:30 +00:00
|
|
|
return err
|
2021-10-25 13:19:56 +00:00
|
|
|
}
|
|
|
|
c.Contracts[ctrName] = cs
|
2021-10-21 14:57:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-10-25 13:19:56 +00:00
|
|
|
var r io.ReadCloser
|
|
|
|
if c.ContractPath == "" {
|
|
|
|
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)
|
|
|
|
}
|
2021-10-21 14:57:38 +00:00
|
|
|
if err != nil {
|
2021-10-25 13:19:56 +00:00
|
|
|
return fmt.Errorf("can't open contracts archive: %w", err)
|
2021-10-21 14:57:38 +00:00
|
|
|
}
|
2021-10-25 13:19:56 +00:00
|
|
|
defer r.Close()
|
2021-10-21 14:57:38 +00:00
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
m, err := readContractsFromArchive(r, names)
|
2021-10-21 14:57:38 +00:00
|
|
|
if err != nil {
|
2021-10-25 13:19:56 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, name := range names {
|
2022-07-27 14:57:30 +00:00
|
|
|
if err := m[name].parse(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-25 13:19:56 +00:00
|
|
|
c.Contracts[name] = m[name]
|
2021-10-21 14:57:38 +00:00
|
|
|
}
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
2021-10-21 14:57:38 +00:00
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
for _, ctrName := range names {
|
|
|
|
if ctrName != alphabetContract {
|
2022-07-27 14:57:30 +00:00
|
|
|
cs := c.Contracts[ctrName]
|
2021-10-25 13:19:56 +00:00
|
|
|
cs.Hash = state.CreateContractHash(c.CommitteeAcc.Contract.ScriptHash(),
|
|
|
|
cs.NEF.Checksum, cs.Manifest.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-07-27 14:57:30 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
func (cs *contractState) parse() error {
|
2021-10-21 14:57:38 +00:00
|
|
|
nf, err := nef.FileFromBytes(cs.RawNEF)
|
2021-07-21 10:09:59 +00:00
|
|
|
if err != nil {
|
2021-10-25 13:19:56 +00:00
|
|
|
return fmt.Errorf("can't parse NEF file: %w", err)
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
2021-10-21 14:57:38 +00:00
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
m := new(manifest.Manifest)
|
|
|
|
if err := json.Unmarshal(cs.RawManifest, m); err != nil {
|
|
|
|
return fmt.Errorf("can't parse manifest file: %w", err)
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 13:19:56 +00:00
|
|
|
cs.NEF = &nf
|
|
|
|
cs.Manifest = m
|
|
|
|
return nil
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
2021-10-21 14:57:38 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-02-02 13:28:08 +00:00
|
|
|
dir, _ := filepath.Split(h.Name)
|
|
|
|
ctrName := filepath.Base(dir)
|
2021-10-21 14:57:38 +00:00
|
|
|
|
|
|
|
cs, ok := m[ctrName]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
2022-02-02 13:28:08 +00:00
|
|
|
case strings.HasSuffix(h.Name, filepath.Join(ctrName, ctrName+"_contract.nef")):
|
2022-03-18 11:12:58 +00:00
|
|
|
cs.RawNEF, err = io.ReadAll(r)
|
2021-10-21 14:57:38 +00:00
|
|
|
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"):
|
2022-03-18 11:12:58 +00:00
|
|
|
cs.RawManifest, err = io.ReadAll(r)
|
2021-10-21 14:57:38 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-02-21 11:42:45 +00:00
|
|
|
func getContractDeployParameters(cs *contractState, deployData []any) []any {
|
|
|
|
return []any{cs.RawNEF, cs.RawManifest, deployData}
|
2021-07-21 10:09:59 +00:00
|
|
|
}
|
|
|
|
|
2023-04-13 14:42:59 +00:00
|
|
|
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []any, method string) []any {
|
2023-02-21 11:42:45 +00:00
|
|
|
items := make([]any, 1, 6)
|
2022-04-07 12:47:13 +00:00
|
|
|
items[0] = false // notaryDisabled is false
|
2021-07-21 10:09:59 +00:00
|
|
|
|
|
|
|
switch ctrName {
|
2023-01-10 12:50:17 +00:00
|
|
|
case frostfsContract:
|
2021-07-21 10:09:59 +00:00
|
|
|
items = append(items,
|
2022-04-07 12:47:13 +00:00
|
|
|
c.Contracts[processingContract].Hash,
|
|
|
|
keysParam,
|
|
|
|
smartcontract.Parameter{})
|
2021-07-21 10:09:59 +00:00
|
|
|
case processingContract:
|
2023-01-10 12:50:17 +00:00
|
|
|
items = append(items, c.Contracts[frostfsContract].Hash)
|
2021-07-21 10:09:59 +00:00
|
|
|
return items[1:] // no notary info
|
|
|
|
case auditContract:
|
2022-04-07 12:47:13 +00:00
|
|
|
items = append(items, c.Contracts[netmapContract].Hash)
|
2021-07-21 10:09:59 +00:00
|
|
|
case balanceContract:
|
|
|
|
items = append(items,
|
2022-04-07 12:47:13 +00:00
|
|
|
c.Contracts[netmapContract].Hash,
|
|
|
|
c.Contracts[containerContract].Hash)
|
2021-07-21 10:09:59 +00:00
|
|
|
case containerContract:
|
2021-09-30 14:42:16 +00:00
|
|
|
// In case if NNS is updated multiple times, we can't calculate
|
|
|
|
// it's actual hash based on local data, thus query chain.
|
|
|
|
nnsCs, err := c.Client.GetContractStateByID(1)
|
|
|
|
if err != nil {
|
|
|
|
panic("NNS is not yet deployed")
|
|
|
|
}
|
2021-07-21 10:09:59 +00:00
|
|
|
items = append(items,
|
2022-04-07 12:47:13 +00:00
|
|
|
c.Contracts[netmapContract].Hash,
|
|
|
|
c.Contracts[balanceContract].Hash,
|
2023-01-10 13:01:54 +00:00
|
|
|
c.Contracts[frostfsIDContract].Hash,
|
2022-04-07 12:47:13 +00:00
|
|
|
nnsCs.Hash,
|
|
|
|
"container")
|
2023-01-10 13:01:54 +00:00
|
|
|
case frostfsIDContract:
|
2021-07-21 10:09:59 +00:00
|
|
|
items = append(items,
|
2022-04-07 12:47:13 +00:00
|
|
|
c.Contracts[netmapContract].Hash,
|
|
|
|
c.Contracts[containerContract].Hash)
|
2021-07-21 10:09:59 +00:00
|
|
|
case netmapContract:
|
2023-04-13 14:42:59 +00:00
|
|
|
md := getDefaultNetmapContractConfigMap()
|
|
|
|
if method == updateMethodName {
|
|
|
|
arr, err := c.getNetConfigFromNetmapContract()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
m, err := parseConfigFromNetmapContract(arr)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for k, v := range m {
|
|
|
|
for _, key := range netmapConfigKeys {
|
|
|
|
if k == key {
|
|
|
|
md[k] = v
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var configParam []any
|
|
|
|
for k, v := range md {
|
|
|
|
configParam = append(configParam, k, v)
|
2021-07-29 10:06:25 +00:00
|
|
|
}
|
2023-04-13 14:42:59 +00:00
|
|
|
|
2021-07-21 10:09:59 +00:00
|
|
|
items = append(items,
|
2022-04-07 12:47:13 +00:00
|
|
|
c.Contracts[balanceContract].Hash,
|
|
|
|
c.Contracts[containerContract].Hash,
|
|
|
|
keysParam,
|
|
|
|
configParam)
|
2021-07-21 10:09:59 +00:00
|
|
|
case proxyContract:
|
2021-12-30 07:45:48 +00:00
|
|
|
items = nil
|
2021-07-21 10:09:59 +00:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("invalid contract name: %s", ctrName))
|
|
|
|
}
|
|
|
|
return items
|
|
|
|
}
|
|
|
|
|
2023-04-13 14:42:59 +00:00
|
|
|
func (c *initializeContext) getNetConfigFromNetmapContract() ([]stackitem.Item, error) {
|
|
|
|
cs, err := c.Client.GetContractStateByID(1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("NNS is not yet deployed: %w", err)
|
|
|
|
}
|
|
|
|
nmHash, err := nnsResolveHash(c.ReadOnlyInvoker, cs.Hash, netmapContract+".frostfs")
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't get netmap contract hash: %w", err)
|
|
|
|
}
|
|
|
|
arr, err := unwrap.Array(c.ReadOnlyInvoker.Call(nmHash, "listConfig"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't fetch list of network config keys from the netmap contract")
|
|
|
|
}
|
|
|
|
return arr, err
|
|
|
|
}
|
|
|
|
|
2023-02-21 11:42:45 +00:00
|
|
|
func (c *initializeContext) getAlphabetDeployItems(i, n int) []any {
|
|
|
|
items := make([]any, 6)
|
2021-12-27 11:13:48 +00:00
|
|
|
items[0] = false
|
|
|
|
items[1] = c.Contracts[netmapContract].Hash
|
|
|
|
items[2] = c.Contracts[proxyContract].Hash
|
|
|
|
items[3] = innerring.GlagoliticLetter(i).String()
|
|
|
|
items[4] = int64(i)
|
|
|
|
items[5] = int64(n)
|
|
|
|
return items
|
|
|
|
}
|