forked from TrueCloudLab/frostfs-node
214 lines
5.9 KiB
Go
214 lines
5.9 KiB
Go
package helper
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"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/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
|
|
rpcInvoker invoker.RPCInvoke
|
|
}
|
|
|
|
type AlphabetWallets struct {
|
|
Label string
|
|
Path string
|
|
}
|
|
|
|
func (a *AlphabetWallets) GetAccount(v *viper.Viper) ([]*wallet.Account, error) {
|
|
w, err := GetAlphabetWallets(v, a.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var accounts []*wallet.Account
|
|
for _, wall := range w {
|
|
acc, err := GetWalletAccount(wall, a.Label)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
accounts = append(accounts, acc)
|
|
}
|
|
return accounts, nil
|
|
}
|
|
|
|
type RegularWallets struct{ Path string }
|
|
|
|
func (r *RegularWallets) GetAccount() ([]*wallet.Account, error) {
|
|
w, err := getRegularWallet(r.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []*wallet.Account{w.GetAccount(w.GetChangeAddress())}, nil
|
|
}
|
|
|
|
// NewLocalActor create LocalActor with accounts form provided wallets.
|
|
// In case of empty wallets provided created actor with dummy account only for read operation.
|
|
//
|
|
// If wallets are provided, the contract client will use accounts with accName name from these wallets.
|
|
// To determine which account name should be used in a contract client, refer to how the contract
|
|
// verifies the transaction signature.
|
|
func NewLocalActor(c actor.RPCActor, alphabet *AlphabetWallets, regularWallets ...*RegularWallets) (*LocalActor, error) {
|
|
var act *actor.Actor
|
|
var accounts []*wallet.Account
|
|
var signers []actor.SignerAccount
|
|
|
|
if alphabet != nil {
|
|
account, err := alphabet.GetAccount(viper.GetViper())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
accounts = append(accounts, account...)
|
|
signers = append(signers, actor.SignerAccount{
|
|
Signer: transaction.Signer{
|
|
Account: account[0].Contract.ScriptHash(),
|
|
Scopes: transaction.Global,
|
|
},
|
|
Account: account[0],
|
|
})
|
|
}
|
|
|
|
for _, w := range regularWallets {
|
|
if w == nil {
|
|
continue
|
|
}
|
|
account, err := w.GetAccount()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
accounts = append(accounts, account...)
|
|
signers = append(signers, actor.SignerAccount{
|
|
Signer: transaction.Signer{
|
|
Account: account[0].Contract.ScriptHash(),
|
|
Scopes: transaction.Global,
|
|
},
|
|
Account: account[0],
|
|
})
|
|
}
|
|
|
|
act, err := actor.New(c, signers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &LocalActor{
|
|
neoActor: act,
|
|
accounts: accounts,
|
|
Invoker: &act.Invoker,
|
|
rpcInvoker: c,
|
|
}, 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")
|
|
}
|
|
|
|
func (a *LocalActor) GetRPCInvoker() invoker.RPCInvoke {
|
|
return a.rpcInvoker
|
|
}
|