forked from TrueCloudLab/frostfs-node
[#932] adm: Add custom Actor
to sign tx by all committee accounts
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
61c58e2f92
commit
bc9dbb26ec
4 changed files with 177 additions and 17 deletions
|
@ -11,7 +11,6 @@ import (
|
||||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -84,22 +83,19 @@ func parseChainName(cmd *cobra.Command) apechain.Name {
|
||||||
return apeChainName
|
return apeChainName
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *actor.Actor) {
|
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *helper.LocalActor) {
|
||||||
v := viper.GetViper()
|
c, err := helper.GetN3Client(viper.GetViper())
|
||||||
c, err := helper.GetN3Client(v)
|
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
committeeAcc := helper.GetComitteAcc(cmd, v)
|
ac, err := helper.NewLocalActor(cmd, c)
|
||||||
ac, err := helper.NewActor(c, committeeAcc)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
inv := &ac.Invoker
|
|
||||||
var ch util.Uint160
|
var ch util.Uint160
|
||||||
r := management.NewReader(inv)
|
r := management.NewReader(ac.Invoker)
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||||
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
||||||
|
|
||||||
return morph.NewContractStorage(ac, ch), ac
|
return morph.NewContractStorage(ac, ch), ac
|
||||||
|
|
169
cmd/frostfs-adm/internal/modules/morph/helper/actor.go
Normal file
169
cmd/frostfs-adm/internal/modules/morph/helper/actor.go
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"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/neorpc/result"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LocalActor is a kludge, do not use it outside of the morph commands.
|
||||||
|
type LocalActor struct {
|
||||||
|
neoActor *actor.Actor
|
||||||
|
accounts []*wallet.Account
|
||||||
|
Invoker *invoker.Invoker
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLocalActor create LocalActor with accounts form provided wallets.
|
||||||
|
// In case of empty wallets provided created actor with dummy account only for read operation.
|
||||||
|
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
||||||
|
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||||
|
var act *actor.Actor
|
||||||
|
var accounts []*wallet.Account
|
||||||
|
if walletDir == "" {
|
||||||
|
account, err := wallet.NewAccount()
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to create dummy account: %w", err)
|
||||||
|
act, err = actor.New(c, []actor.SignerAccount{{
|
||||||
|
Signer: transaction.Signer{
|
||||||
|
Account: account.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.Global,
|
||||||
|
},
|
||||||
|
Account: account,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wallets, err := GetAlphabetWallets(viper.GetViper(), walletDir)
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||||
|
|
||||||
|
for _, w := range wallets {
|
||||||
|
acc, err := GetWalletAccount(w, constants.CommitteeAccountName)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||||
|
accounts = append(accounts, acc)
|
||||||
|
}
|
||||||
|
act, err = actor.New(c, []actor.SignerAccount{{
|
||||||
|
Signer: transaction.Signer{
|
||||||
|
Account: accounts[0].Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.Global,
|
||||||
|
},
|
||||||
|
Account: accounts[0],
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &LocalActor{
|
||||||
|
neoActor: act,
|
||||||
|
accounts: accounts,
|
||||||
|
Invoker: &act.Invoker,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) {
|
||||||
|
tx, err := a.neoActor.MakeCall(contract, method, params...)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
err = a.resign(tx)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
return a.neoActor.Send(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) SendRun(script []byte) (util.Uint256, uint32, error) {
|
||||||
|
tx, err := a.neoActor.MakeRun(script)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
err = a.resign(tx)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, 0, err
|
||||||
|
}
|
||||||
|
return a.neoActor.Send(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resign is used to sign tx with committee accounts.
|
||||||
|
// Inside the methods `MakeCall` and `SendRun` of the NeoGO's actor transaction is signing by committee account,
|
||||||
|
// because actor uses committee wallet.
|
||||||
|
// But it is not enough, need to sign with another committee accounts.
|
||||||
|
func (a *LocalActor) resign(tx *transaction.Transaction) error {
|
||||||
|
if len(a.accounts[0].Contract.Parameters) > 1 {
|
||||||
|
// Use parameter context to avoid dealing with signature order.
|
||||||
|
network := a.neoActor.GetNetwork()
|
||||||
|
pc := context.NewParameterContext("", network, tx)
|
||||||
|
h := a.accounts[0].Contract.ScriptHash()
|
||||||
|
for _, acc := range a.accounts {
|
||||||
|
priv := acc.PrivateKey()
|
||||||
|
sign := priv.SignHashable(uint32(network), tx)
|
||||||
|
if err := pc.AddSignature(h, acc.Contract, priv.PublicKey(), sign); err != nil {
|
||||||
|
return fmt.Errorf("can't add signature: %w", err)
|
||||||
|
}
|
||||||
|
if len(pc.Items[h].Signatures) == len(acc.Contract.Parameters) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := pc.GetWitness(h)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("incomplete signature: %w", err)
|
||||||
|
}
|
||||||
|
tx.Scripts[0] = *w
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||||
|
return a.neoActor.Wait(h, vub, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) Sender() util.Uint160 {
|
||||||
|
return a.neoActor.Sender()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) {
|
||||||
|
return a.neoActor.Call(contract, operation, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) CallAndExpandIterator(_ util.Uint160, _ string, _ int, _ ...any) (*result.Invoke, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) TerminateSession(_ uuid.UUID) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) {
|
||||||
|
return a.neoActor.TraverseIterator(sessionID, iterator, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) MakeRun(_ []byte) (*transaction.Transaction, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) MakeUnsignedCall(_ util.Uint160, _ string, _ []transaction.Attribute, _ ...any) (*transaction.Transaction, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) MakeUnsignedRun(_ []byte, _ []transaction.Attribute) (*transaction.Transaction, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LocalActor) MakeCall(_ util.Uint160, _ string, _ ...any) (*transaction.Transaction, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
|
@ -4,24 +4,21 @@ import (
|
||||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getRPCClient(cmd *cobra.Command) (*client.Contract, *actor.Actor, util.Uint160) {
|
func getRPCClient(cmd *cobra.Command) (*client.Contract, *helper.LocalActor, util.Uint160) {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
c, err := helper.GetN3Client(v)
|
c, err := helper.GetN3Client(v)
|
||||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
committeeAcc := helper.GetComitteAcc(cmd, v)
|
ac, err := helper.NewLocalActor(cmd, c)
|
||||||
ac, err := helper.NewActor(c, committeeAcc)
|
|
||||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
inv := &ac.Invoker
|
r := management.NewReader(ac.Invoker)
|
||||||
r := management.NewReader(inv)
|
|
||||||
nnsCs, err := r.GetContractByID(1)
|
nnsCs, err := r.GetContractByID(1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
||||||
|
|
|
@ -30,7 +30,6 @@ var (
|
||||||
Short: "List all registered domain names",
|
Short: "List all registered domain names",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
},
|
},
|
||||||
Run: listTokens,
|
Run: listTokens,
|
||||||
}
|
}
|
||||||
|
@ -75,7 +74,6 @@ var (
|
||||||
Short: "Returns domain record of the specified type",
|
Short: "Returns domain record of the specified type",
|
||||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
|
||||||
},
|
},
|
||||||
Run: getRecords,
|
Run: getRecords,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue