package helper import ( "context" "errors" "fmt" "time" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" "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/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient" "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/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/cobra" "github.com/spf13/viper" ) // Client represents N3 client interface capable of test-invoking scripts // and sending signed transactions to chain. type Client interface { invoker.RPCInvoke GetBlockCount() (uint32, error) GetNativeContracts() ([]state.Contract, error) GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error) GetVersion() (*result.Version, error) SendRawTransaction(*transaction.Transaction) (util.Uint256, error) GetCommittee() (keys.PublicKeys, error) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) } type HashVUBPair struct { Hash util.Uint256 Vub uint32 } type ClientContext struct { Client Client // a raw neo-go client OR a local chain implementation CommitteeAct *actor.Actor // committee actor with the Global witness scope ReadOnlyInvoker *invoker.Invoker // R/O contract invoker, does not contain any signer SentTxs []HashVUBPair } func GetN3Client(v *viper.Viper) (Client, error) { // number of opened connections // by neo-go client per one host const ( maxConnsPerHost = 10 requestTimeout = time.Second * 10 ) ctx := context.Background() endpoint := v.GetString(commonflags.EndpointFlag) if endpoint == "" { return nil, errors.New("missing endpoint") } c, err := rpcclient.New(ctx, endpoint, rpcclient.Options{ MaxConnsPerHost: maxConnsPerHost, RequestTimeout: requestTimeout, }) if err != nil { return nil, err } if err := c.Init(); err != nil { return nil, err } return c, nil } func DefaultClientContext(c Client, committeeAcc *wallet.Account) (*ClientContext, error) { commAct, err := NewActor(c, committeeAcc) if err != nil { return nil, err } return &ClientContext{ Client: c, CommitteeAct: commAct, ReadOnlyInvoker: invoker.New(c, nil), }, nil } func (c *ClientContext) SendTx(tx *transaction.Transaction, cmd *cobra.Command, await bool) error { h, err := c.Client.SendRawTransaction(tx) if err != nil { return err } if h != tx.Hash() { return fmt.Errorf("sent and actual tx hashes mismatch:\n\tsent: %v\n\tactual: %v", tx.Hash().StringLE(), h.StringLE()) } c.SentTxs = append(c.SentTxs, HashVUBPair{Hash: h, Vub: tx.ValidUntilBlock}) if await { return c.AwaitTx(cmd) } return nil } func (c *ClientContext) AwaitTx(cmd *cobra.Command) error { if len(c.SentTxs) == 0 { return nil } if local, ok := c.Client.(*LocalClient); ok { if err := local.putTransactions(); err != nil { return fmt.Errorf("can't persist transactions: %w", err) } } err := AwaitTx(cmd, c.Client, c.SentTxs) c.SentTxs = c.SentTxs[:0] return err }