frostfs-node/pkg/morph/client/static.go
2024-12-12 15:30:12 +03:00

238 lines
5.9 KiB
Go

package client
import (
"context"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// StaticClient is a wrapper over Neo:Morph client
// that invokes single smart contract methods with fixed fee.
//
// Working static client must be created via constructor NewStatic.
// Using the StaticClient that has been created with new(StaticClient)
// expression (or just declaring a StaticClient variable) is unsafe
// and can lead to panic.
type StaticClient struct {
staticOpts
client *Client // neo-go client instance
scScriptHash util.Uint160 // contract script-hash
}
type staticOpts struct {
tryNotary bool
alpha bool // use client's key to sign notary request's main TX
fee fixedn.Fixed8
}
// WithNotary returns notary status of the client.
//
// See also TryNotary.
func (s *StaticClient) WithNotary() bool {
return s.client.IsNotaryEnabled()
}
// IsAlpha returns Alphabet status of the client.
//
// See also AsAlphabet.
func (s *StaticClient) IsAlpha() bool {
return s.alpha
}
// StaticClientOption allows to set an optional
// parameter of StaticClient.
type StaticClientOption func(*staticOpts)
// NewStatic creates, initializes and returns the StaticClient instance.
//
// If provided Client instance is nil, ErrNilClient is returned.
//
// Specified fee is used by default. Per-operation fees can be customized via WithCustomFee option.
func NewStatic(client *Client, scriptHash util.Uint160, fee fixedn.Fixed8, opts ...StaticClientOption) (*StaticClient, error) {
if client == nil {
return nil, ErrNilClient
}
c := &StaticClient{
client: client,
scScriptHash: scriptHash,
}
c.fee = fee
for i := range opts {
opts[i](&c.staticOpts)
}
return c, nil
}
// Morph return wrapped raw morph client.
func (s StaticClient) Morph() *Client {
return s.client
}
// InvokePrm groups parameters of the Invoke operation.
type InvokePrm struct {
TestInvokePrm
// optional parameters
InvokePrmOptional
}
// InvokePrmOptional groups optional parameters of the Invoke operation.
type InvokePrmOptional struct {
// hash is an optional hash of the transaction
// that generated the notification that required
// to invoke notary request.
// It is used to generate same but unique nonce and
// `validUntilBlock` values by all notification
// receivers.
hash *util.Uint256
// controlTX controls whether the invoke method will use a rounded
// block height value, which is useful for control transactions which
// are required to be produced by all nodes with very high probability.
// It's only used by notary transactions and it affects only the
// computation of `validUntilBlock` values.
controlTX bool
// vub is used to set custom valid until block value.
vub uint32
}
// SetHash sets optional hash of the transaction.
// If hash is set and notary is enabled, StaticClient
// uses it for notary nonce and `validUntilBlock`
// calculation.
func (i *InvokePrmOptional) SetHash(hash util.Uint256) {
i.hash = &hash
}
// SetControlTX sets whether a control transaction will be used.
func (i *InvokePrmOptional) SetControlTX(b bool) {
i.controlTX = b
}
// IsControl gets whether a control transaction will be used.
func (i *InvokePrmOptional) IsControl() bool {
return i.controlTX
}
// SetVUB sets valid until block value.
func (i *InvokePrmOptional) SetVUB(v uint32) {
i.vub = v
}
type InvokeRes struct {
Hash util.Uint256
VUB uint32
}
// Invoke calls Invoke method of Client with static internal script hash and fee.
// Supported args types are the same as in Client.
//
// If TryNotary is provided:
// - if AsAlphabet is provided, calls NotaryInvoke;
// - otherwise, calls NotaryInvokeNotAlpha.
//
// If fee for the operation executed using specified method is customized, then StaticClient uses it.
// Otherwise, default fee is used.
func (s StaticClient) Invoke(ctx context.Context, prm InvokePrm) (InvokeRes, error) {
var vubP *uint32
if s.tryNotary {
if s.alpha {
var (
nonce uint32 = 1
vub uint32
err error
)
if prm.hash != nil {
if prm.controlTX {
nonce, vub, err = s.client.CalculateNonceAndVUBControl(prm.hash)
} else {
nonce, vub, err = s.client.CalculateNonceAndVUB(prm.hash)
}
if err != nil {
return InvokeRes{}, fmt.Errorf("calculate nonce and VUB for notary alphabet invoke: %w", err)
}
vubP = &vub
}
if prm.vub > 0 {
vubP = &prm.vub
}
return s.client.NotaryInvoke(ctx, s.scScriptHash, s.fee, nonce, vubP, prm.method, prm.args...)
}
if prm.vub > 0 {
vubP = &prm.vub
}
return s.client.NotaryInvokeNotAlpha(ctx, s.scScriptHash, s.fee, vubP, prm.method, prm.args...)
}
return s.client.Invoke(
ctx,
s.scScriptHash,
s.fee,
prm.method,
prm.args...,
)
}
// TestInvokePrm groups parameters of the TestInvoke operation.
type TestInvokePrm struct {
method string
args []any
}
// SetMethod sets method of the contract to call.
func (ti *TestInvokePrm) SetMethod(method string) {
ti.method = method
}
// SetArgs sets arguments of the contact call.
func (ti *TestInvokePrm) SetArgs(args ...any) {
ti.args = args
}
// TestInvoke calls TestInvoke method of Client with static internal script hash.
func (s StaticClient) TestInvoke(prm TestInvokePrm) ([]stackitem.Item, error) {
return s.client.TestInvoke(
s.scScriptHash,
prm.method,
prm.args...,
)
}
// ContractAddress returns the address of the associated contract.
func (s StaticClient) ContractAddress() util.Uint160 {
return s.scScriptHash
}
// TryNotary returns option to enable
// notary invocation tries.
func TryNotary() StaticClientOption {
return func(o *staticOpts) {
o.tryNotary = true
}
}
// AsAlphabet returns option to sign main TX
// of notary requests with client's private
// key.
//
// Considered to be used by IR nodes only.
func AsAlphabet() StaticClientOption {
return func(o *staticOpts) {
o.alpha = true
}
}