[#1035] neofs-adm: reuse NEF and manifest during alphabet contract update

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2021-12-27 14:13:48 +03:00 committed by Alex Vanin
parent 4673c81451
commit 549546dea1

View file

@ -19,10 +19,13 @@ import (
io2 "github.com/nspcc-dev/neo-go/pkg/io" io2 "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/client" "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"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"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" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neofs-node/pkg/innerring" "github.com/nspcc-dev/neofs-node/pkg/innerring"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -154,6 +157,20 @@ func (c *initializeContext) updateContracts() error {
var keysParam []smartcontract.Parameter var keysParam []smartcontract.Parameter
// 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.
// 1. Initialize static slots for alphabet NEF and manifest.
// 2. Store NEF and manifest into static slots.
// 3. Push parameters for each alphabet contract on stack.
// 4. For each alphabet contract, invoke `update` using parameters on stack and
// NEF and manifest from step 2.
// 5. Update other contracts as usual.
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{2})
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
emit.Opcodes(w.BinWriter, opcode.STSFLD0, opcode.STSFLD1)
// alphabet contracts should be deployed by individual nodes to get different hashes. // alphabet contracts should be deployed by individual nodes to get different hashes.
for i, acc := range c.Accounts { for i, acc := range c.Accounts {
ctrHash, err := nnsResolveHash(c.Client, nnsHash, getAlphabetNNSDomain(i)) ctrHash, err := nnsResolveHash(c.Client, nnsHash, getAlphabetNNSDomain(i))
@ -166,25 +183,29 @@ func (c *initializeContext) updateContracts() error {
Value: acc.PrivateKey().PublicKey().Bytes(), Value: acc.PrivateKey().PublicKey().Bytes(),
}) })
params := getContractDeployParameters(alphaCs.RawNEF, alphaCs.RawManifest, params := c.getAlphabetDeployItems(i, len(c.Wallets))
c.getAlphabetDeployParameters(i, len(c.Wallets))) emit.Array(w.BinWriter, params...)
signer := transaction.Signer{ emit.Opcodes(w.BinWriter, opcode.LDSFLD1, opcode.LDSFLD0)
Account: c.CommitteeAcc.Contract.ScriptHash(), emit.Int(w.BinWriter, 3)
Scopes: transaction.CalledByEntry, emit.Opcodes(w.BinWriter, opcode.PACK)
} emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
res, err := c.Client.InvokeFunction(ctrHash, updateMethodName, params, []transaction.Signer{signer})
if err != nil {
return fmt.Errorf("can't deploy alphabet #%d contract: %w", i, err)
}
if res.State != vm.HaltState.String() {
return fmt.Errorf("can't deploy alpabet #%d contract: %s", i, res.FaultException)
}
totalGasCost += res.GasConsumed
w.WriteBytes(res.Script)
} }
res, err := c.Client.InvokeScript(w.Bytes(), []transaction.Signer{{
Account: c.CommitteeAcc.Contract.ScriptHash(),
Scopes: transaction.Global,
}})
if err != nil {
return fmt.Errorf("can't update alphabet contracts: %w", err)
}
if res.State != vm.HaltState.String() {
return fmt.Errorf("can't update alphabet contracts: %s", res.FaultException)
}
w.Reset()
totalGasCost += res.GasConsumed
w.WriteBytes(res.Script)
for _, ctrName := range contractList { for _, ctrName := range contractList {
cs := c.getContract(ctrName) cs := c.getContract(ctrName)
@ -557,16 +578,28 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []sm
} }
func (c *initializeContext) getAlphabetDeployParameters(i, n int) []smartcontract.Parameter { func (c *initializeContext) getAlphabetDeployParameters(i, n int) []smartcontract.Parameter {
items := c.getAlphabetDeployItems(i, n)
return []smartcontract.Parameter{ return []smartcontract.Parameter{
newContractParameter(smartcontract.BoolType, false), newContractParameter(smartcontract.BoolType, items[0]),
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash), newContractParameter(smartcontract.Hash160Type, items[1]),
newContractParameter(smartcontract.Hash160Type, c.Contracts[proxyContract].Hash), newContractParameter(smartcontract.Hash160Type, items[2]),
newContractParameter(smartcontract.StringType, innerring.GlagoliticLetter(i).String()), newContractParameter(smartcontract.StringType, items[3]),
newContractParameter(smartcontract.IntegerType, int64(i)), newContractParameter(smartcontract.IntegerType, items[4]),
newContractParameter(smartcontract.IntegerType, int64(n)), newContractParameter(smartcontract.IntegerType, items[5]),
} }
} }
func (c *initializeContext) getAlphabetDeployItems(i, n int) []interface{} {
items := make([]interface{}, 6)
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
}
func newContractParameter(typ smartcontract.ParamType, value interface{}) smartcontract.Parameter { func newContractParameter(typ smartcontract.ParamType, value interface{}) smartcontract.Parameter {
return smartcontract.Parameter{ return smartcontract.Parameter{
Type: typ, Type: typ,