frostfs-node/pkg/morph/client/static.go
Pavel Karpy 3849d13e0b [#971] morph/client: Group Invoke and TestInvoke params
Also:
- add `validUntilBlock` and nonce calculation;
- pass notification hashes to notary calls.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2021-11-19 09:58:03 +03:00

200 lines
4.9 KiB
Go

package client
import (
"errors"
"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 {
tryNotary bool
alpha bool // use client's key to sign notary request's main TX
client *Client // neo-go client instance
scScriptHash util.Uint160 // contract script-hash
fee fixedn.Fixed8 // invocation fee
}
type staticOpts struct {
tryNotary bool
alpha bool
}
// StaticClientOption allows to set an optional
// parameter of StaticClient.
type StaticClientOption func(*staticOpts)
func defaultStaticOpts() *staticOpts {
return new(staticOpts)
}
// ErrNilStaticClient is returned by functions that expect
// a non-nil StaticClient pointer, but received nil.
var ErrNilStaticClient = errors.New("static client is nil")
// NewStatic creates, initializes and returns the StaticClient instance.
//
// If provided Client instance is nil, ErrNilClient is returned.
func NewStatic(client *Client, scriptHash util.Uint160, fee fixedn.Fixed8, opts ...StaticClientOption) (*StaticClient, error) {
if client == nil {
return nil, ErrNilClient
}
o := defaultStaticOpts()
for i := range opts {
opts[i](o)
}
return &StaticClient{
tryNotary: o.tryNotary,
alpha: o.alpha,
client: client,
scScriptHash: scriptHash,
fee: fee,
}, 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 {
// required parameters
method string
args []interface{}
// 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
}
// SetMethod sets method of the contract to call.
func (i *InvokePrm) SetMethod(method string) {
i.method = method
}
// SetArgs sets arguments of the contact call.
func (i *InvokePrm) SetArgs(args ...interface{}) {
i.args = args
}
// 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
}
// 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.
func (s StaticClient) Invoke(prm InvokePrm) error {
if s.tryNotary {
if s.alpha {
var (
nonce uint32 = 1
vubP *uint32
vub uint32
err error
)
if prm.hash != nil {
nonce, vub, err = s.client.CalculateNonceAndVUB(*prm.hash)
if err != nil {
return fmt.Errorf("could not calculate nonce and VUB for notary alphabet invoke: %w", err)
}
vubP = &vub
}
return s.client.NotaryInvoke(s.scScriptHash, s.fee, nonce, vubP, prm.method, prm.args...)
}
return s.client.NotaryInvokeNotAlpha(s.scScriptHash, s.fee, prm.method, prm.args...)
}
return s.client.Invoke(
s.scScriptHash,
s.fee,
prm.method,
prm.args...,
)
}
// TestInvokePrm groups parameters of the TestInvoke operation.
type TestInvokePrm struct {
method string
args []interface{}
}
// 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 ...interface{}) {
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
}
}