forked from TrueCloudLab/frostfs-node
238 lines
5.9 KiB
Go
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("could not 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
|
|
}
|
|
}
|