rpcclient: drop deprecated Client APIs

Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
Roman Khimov 2023-10-09 22:34:33 +03:00
parent cef70091f6
commit 413caac941
14 changed files with 36 additions and 1831 deletions

View file

@ -26,20 +26,6 @@ APIs/commands/configurations will be removed and here is a list of scheduled
breaking changes. Consider changing your code/scripts/configurations if you're
using anything mentioned here.
## Old RPC client APIs
A huge set of RPC client APIs was deprecated in versions 0.99.2 and 0.99.3
(August-September 2022), including very frequently used ones like
SignAndPushInvocationTx, AddNetworkFee, TransferNEP17. A new set of
invoker/actor/unwrap/nep17/etc packages was introduced decoupling these
functions from RPC client and simplifying typical backend code. Please refer
to rpcclient package documentation for specific replacements for each of these
APIs and convert your code to using them.
While a lot of the code is already converted to new APIs, old ones still can
be used in some code not known to us. Therefore we will remove old APIs not
earlier than May 2023, with 0.103.0 release.
## WSClient Notifications channel and SubscribeFor* APIs
Version 0.99.5 of NeoGo introduces a new set of subscription APIs that gives

View file

@ -30,6 +30,14 @@ import (
"github.com/urfave/cli"
)
// transferTarget represents target address, token amount and data for transfer.
type transferTarget struct {
Token util.Uint160
Address util.Uint160
Amount int64
Data any
}
var (
tokenFlag = cli.StringFlag{
Name: "token",
@ -531,7 +539,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
return cli.NewExitError("empty recipients list", 1)
}
var (
recipients []rpcclient.TransferTarget
recipients []transferTarget
cosignersOffset = ctx.NArg()
)
cache := make(map[string]*wallet.Token)
@ -564,7 +572,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
}
recipients = append(recipients, rpcclient.TransferTarget{
recipients = append(recipients, transferTarget{
Token: token.Hash,
Address: addr,
Amount: amount.Int64(),
@ -690,7 +698,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
return txctx.SignAndSend(ctx, act, acc, tx)
}
func makeMultiTransferNEP17(act *actor.Actor, recipients []rpcclient.TransferTarget) (*transaction.Transaction, error) {
func makeMultiTransferNEP17(act *actor.Actor, recipients []transferTarget) (*transaction.Transaction, error) {
scr := smartcontract.NewBuilder()
for i := range recipients {
scr.InvokeWithAssert(recipients[i].Token, "transfer", act.Sender(),

View file

@ -392,34 +392,17 @@ subpackage with an example written in Go doc.
that includes simple-signature accounts and multisignature accounts where
the client has one of the keys (in which case an invocation script is
created that pushes just one signature onto the stack).
10. Define lifetime for the fallback transaction. Let the `fallbackValidFor` be
the lifetime. Let `N` be the current chain's height and `VUB` be
`ValidUntilBlock` value estimated at step 3. Then, the notary node is trying to
collect signatures for the main transaction from `N` up to
`VUB-fallbackValidFor`. In case of failure after `VUB-fallbackValidFor`-th
block is accepted, the notary node abandons attempts to complete the main transaction and
tries to push all associated fallbacks. Use the following rules to define
`fallbackValidFor`:
- `fallbackValidFor` shouldn't be more than `MaxNotValidBeforeDelta` value.
- Use notary package's GetMaxNotValidBeforeDelta to check `MaxNotValidBefore` value.
11. Construct a script for the fallback transaction. The script may do something useful,
i.g. invoke method of a contract. However, if you don't need to perform anything
special on fallback invocation, you can use simple `opcode.RET` script.
12. Sign and submit P2P notary request. Use
[func (*Client) SignAndPushP2PNotaryRequest](https://pkg.go.dev/github.com/nspcc-dev/neo-go@v0.97.2/pkg/rpcclient#Client.SignAndPushP2PNotaryRequest) for it.
10. Sign and submit P2P notary request. Use
[func (*Actor) Notarize](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/rpcclient/notary#Actor.Notarize) for it.
- Use the signed main transaction from step 9 as `mainTx` argument.
- Use the fallback script from step 10 as `fallbackScript` argument.
- Use `-1` as `fallbackSysFee` argument to define system fee by test
invocation or provide any custom value.
- Use `0` as `fallbackNetFee` argument not to add extra network fee to the
fallback.
- Use the `fallbackValidFor` estimated at step 9 as `fallbackValidFor` argument.
- Use your account you'd like to send request (and fallback transaction) from
to sign the request (and fallback transaction).
`SignAndPushP2PNotaryRequest` will construct and sign a fallback transaction,
construct and sign a P2PNotaryRequest and submit it to the RPC node. The
resulting notary request and an error are returned.
`Notarize` will construct and sign a fallback transaction using `Actor`
configuration (just a simple `RET` script by default), pack both transactions
into a P2PNotaryRequest and submit it to the RPC node. It returns hashes of
the main and fallback transactions as well as their `ValidUntilBlock` value.
If you need more control over fallback transction use `Actor` options or
[func (*Actor) SendRequest](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/rpcclient/notary#Actor.SendRequest)
API.
After P2PNotaryRequests are sent, participants should wait for one of their
transactions (main or fallback) to get accepted into one of subsequent blocks.

View file

@ -22,8 +22,6 @@ import (
const (
defaultDialTimeout = 4 * time.Second
defaultRequestTimeout = 4 * time.Second
// Number of blocks after which cache is expired.
cacheTimeout = 100
)
// Client represents the middleman for executing JSON RPC calls
@ -77,17 +75,9 @@ type cache struct {
initDone bool
network netmode.Magic
stateRootInHeader bool
calculateValidUntilBlock calculateValidUntilBlockCache
nativeHashes map[string]util.Uint160
}
// calculateValidUntilBlockCache stores a cached number of validators and
// cache expiration value in blocks.
type calculateValidUntilBlockCache struct {
validatorsCount uint32
expiresAt uint32
}
// New returns a new Client ready to use. You should call Init method to
// initialize stateroot setting for the network the client is operating on if
// you plan using GetBlock*.

View file

@ -1,44 +0,0 @@
package rpcclient
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/config"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// InvokeAndPackIteratorResults creates a script containing System.Contract.Call
// of the specified contract with the specified arguments. It assumes that the
// specified operation will return iterator. The script traverses the resulting
// iterator, packs all its values into array and pushes the resulting array on
// stack. Constructed script is invoked via `invokescript` JSON-RPC API using
// the provided signers. The result of the script invocation contains single array
// stackitem on stack if invocation HALTed. InvokeAndPackIteratorResults can be
// used to interact with JSON-RPC server where iterator sessions are disabled to
// retrieve iterator values via single `invokescript` JSON-RPC call. It returns
// maxIteratorResultItems items at max which is set to
// config.DefaultMaxIteratorResultItems by default.
//
// Deprecated: please use more convenient and powerful invoker.Invoker interface with
// CallAndExpandIterator method. This method will be removed in future versions.
func (c *Client) InvokeAndPackIteratorResults(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer, maxIteratorResultItems ...int) (*result.Invoke, error) {
max := config.DefaultMaxIteratorResultItems
if len(maxIteratorResultItems) != 0 {
max = maxIteratorResultItems[0]
}
values, err := smartcontract.ExpandParameterToEmitable(smartcontract.Parameter{
Type: smartcontract.ArrayType,
Value: params,
})
if err != nil {
return nil, fmt.Errorf("expanding params to emitable: %w", err)
}
bytes, err := smartcontract.CreateCallAndUnwrapIteratorScript(contract, operation, max, values.([]any)...)
if err != nil {
return nil, fmt.Errorf("failed to create iterator unwrapper script: %w", err)
}
return c.InvokeScript(bytes, signers)
}

View file

@ -1,172 +0,0 @@
package rpcclient
// Various non-policy things from native contracts.
import (
"crypto/elliptic"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/nns"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// GetOraclePrice invokes `getPrice` method on a native Oracle contract.
//
// Deprecated: please use oracle subpackage.
func (c *Client) GetOraclePrice() (int64, error) {
oracleHash, err := c.GetNativeContractHash(nativenames.Oracle)
if err != nil {
return 0, fmt.Errorf("failed to get native Oracle hash: %w", err)
}
return c.invokeNativeGetMethod(oracleHash, "getPrice")
}
// GetNNSPrice invokes `getPrice` method on a NeoNameService contract with the specified hash.
//
// Deprecated: please use nns subpackage. This method will be removed in future versions.
func (c *Client) GetNNSPrice(nnsHash util.Uint160) (int64, error) {
return c.invokeNativeGetMethod(nnsHash, "getPrice")
}
// GetGasPerBlock invokes `getGasPerBlock` method on a native NEO contract.
//
// Deprecated: please use neo subpackage. This method will be removed in future releases.
func (c *Client) GetGasPerBlock() (int64, error) {
return c.getFromNEO("getGasPerBlock")
}
// GetCandidateRegisterPrice invokes `getRegisterPrice` method on native NEO contract.
//
// Deprecated: please use neo subpackage. This method will be removed in future releases.
func (c *Client) GetCandidateRegisterPrice() (int64, error) {
return c.getFromNEO("getRegisterPrice")
}
func (c *Client) getFromNEO(meth string) (int64, error) {
neoHash, err := c.GetNativeContractHash(nativenames.Neo)
if err != nil {
return 0, fmt.Errorf("failed to get native NEO hash: %w", err)
}
return c.invokeNativeGetMethod(neoHash, meth)
}
// GetDesignatedByRole invokes `getDesignatedByRole` method on a native RoleManagement contract.
//
// Deprecated: please use rolemgmt package.
func (c *Client) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.PublicKeys, error) {
rmHash, err := c.GetNativeContractHash(nativenames.Designation)
if err != nil {
return nil, fmt.Errorf("failed to get native RoleManagement hash: %w", err)
}
arr, err := unwrap.Array(c.reader.Call(rmHash, "getDesignatedByRole", int64(role), index))
if err != nil {
return nil, err
}
pks := make(keys.PublicKeys, len(arr))
for i, item := range arr {
val, err := item.TryBytes()
if err != nil {
return nil, fmt.Errorf("invalid array element #%d: %s", i, item.Type())
}
pks[i], err = keys.NewPublicKeyFromBytes(val, elliptic.P256())
if err != nil {
return nil, err
}
}
return pks, nil
}
// NNSResolve invokes `resolve` method on a NameService contract with the specified hash.
//
// Deprecated: please use nns subpackage. This method will be removed in future versions.
func (c *Client) NNSResolve(nnsHash util.Uint160, name string, typ nns.RecordType) (string, error) {
if typ == nns.CNAME {
return "", errors.New("can't resolve CNAME record type")
}
return unwrap.UTF8String(c.reader.Call(nnsHash, "resolve", name, int64(typ)))
}
// NNSIsAvailable invokes `isAvailable` method on a NeoNameService contract with the specified hash.
//
// Deprecated: please use nns subpackage. This method will be removed in future versions.
func (c *Client) NNSIsAvailable(nnsHash util.Uint160, name string) (bool, error) {
return unwrap.Bool(c.reader.Call(nnsHash, "isAvailable", name))
}
// NNSGetAllRecords returns iterator over records for a given name from NNS service.
// First return value is the session ID, the second one is Iterator itself, the
// third one is an error. Use TraverseIterator method to traverse iterator values or
// TerminateSession to terminate opened iterator session. See TraverseIterator and
// TerminateSession documentation for more details.
//
// Deprecated: please use nns subpackage. This method will be removed in future versions.
func (c *Client) NNSGetAllRecords(nnsHash util.Uint160, name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.reader.Call(nnsHash, "getAllRecords", name))
}
// NNSUnpackedGetAllRecords returns a set of records for a given name from NNS service
// (config.DefaultMaxIteratorResultItems at max). It differs from NNSGetAllRecords in
// that no iterator session is used to retrieve values from iterator. Instead, unpacking
// VM script is created and invoked via `invokescript` JSON-RPC call.
//
// Deprecated: please use nns subpackage. This method will be removed in future versions.
func (c *Client) NNSUnpackedGetAllRecords(nnsHash util.Uint160, name string) ([]nns.RecordState, error) {
arr, err := unwrap.Array(c.reader.CallAndExpandIterator(nnsHash, "getAllRecords", config.DefaultMaxIteratorResultItems, name))
if err != nil {
return nil, err
}
res := make([]nns.RecordState, len(arr))
for i := range arr {
rs, ok := arr[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: not a struct", i)
}
if len(rs) != 3 {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: wrong number of elements", i)
}
name, err := rs[0].TryBytes()
if err != nil {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: %w", i, err)
}
typ, err := rs[1].TryInteger()
if err != nil {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: %w", i, err)
}
data, err := rs[2].TryBytes()
if err != nil {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: %w", i, err)
}
u64Typ := typ.Uint64()
if !typ.IsUint64() || u64Typ > 255 {
return nil, fmt.Errorf("failed to decode RecordState from stackitem #%d: bad type", i)
}
res[i] = nns.RecordState{
Name: string(name),
Type: nns.RecordType(u64Typ),
Data: string(data),
}
}
return res, nil
}
// GetNotaryServiceFeePerKey returns a reward per notary request key for the designated
// notary nodes. It doesn't cache the result.
//
// Deprecated: please use the Notary contract wrapper from the notary subpackage. This
// method will be removed in future versions.
func (c *Client) GetNotaryServiceFeePerKey() (int64, error) {
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
if err != nil {
return 0, fmt.Errorf("failed to get native Notary hash: %w", err)
}
return c.invokeNativeGetMethod(notaryHash, "getNotaryServiceFeePerKey")
}

View file

@ -1,60 +0,0 @@
package rpcclient
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
// nepDecimals invokes `decimals` NEP* method on the specified contract.
func (c *Client) nepDecimals(tokenHash util.Uint160) (int64, error) {
return unwrap.Int64(c.reader.Call(tokenHash, "decimals"))
}
// nepSymbol invokes `symbol` NEP* method on the specified contract.
func (c *Client) nepSymbol(tokenHash util.Uint160) (string, error) {
return unwrap.PrintableASCIIString(c.reader.Call(tokenHash, "symbol"))
}
// nepTotalSupply invokes `totalSupply` NEP* method on the specified contract.
func (c *Client) nepTotalSupply(tokenHash util.Uint160) (int64, error) {
return unwrap.Int64(c.reader.Call(tokenHash, "totalSupply"))
}
// nepBalanceOf invokes `balanceOf` NEP* method on the specified contract.
func (c *Client) nepBalanceOf(tokenHash, acc util.Uint160, tokenID []byte) (int64, error) {
params := []any{acc}
if tokenID != nil {
params = append(params, tokenID)
}
return unwrap.Int64(c.reader.Call(tokenHash, "balanceOf", params...))
}
// nepTokenInfo returns full NEP* token info.
func (c *Client) nepTokenInfo(tokenHash util.Uint160, standard string) (*wallet.Token, error) {
cs, err := c.GetContractStateByHash(tokenHash)
if err != nil {
return nil, err
}
var isStandardOK bool
for _, st := range cs.Manifest.SupportedStandards {
if st == standard {
isStandardOK = true
break
}
}
if !isStandardOK {
return nil, fmt.Errorf("token %s does not support %s standard", tokenHash.StringLE(), standard)
}
symbol, err := c.nepSymbol(tokenHash)
if err != nil {
return nil, err
}
decimals, err := c.nepDecimals(tokenHash)
if err != nil {
return nil, err
}
return wallet.NewToken(tokenHash, cs.Manifest.Name, symbol, decimals, standard), nil
}

View file

@ -1,232 +0,0 @@
package rpcclient
import (
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config"
"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/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"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"
)
// NEP11Decimals invokes `decimals` NEP-11 method on the specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11Decimals(tokenHash util.Uint160) (int64, error) {
return c.nepDecimals(tokenHash)
}
// NEP11Symbol invokes `symbol` NEP-11 method on the specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11Symbol(tokenHash util.Uint160) (string, error) {
return c.nepSymbol(tokenHash)
}
// NEP11TotalSupply invokes `totalSupply` NEP-11 method on the specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11TotalSupply(tokenHash util.Uint160) (int64, error) {
return c.nepTotalSupply(tokenHash)
}
// NEP11BalanceOf invokes `balanceOf` NEP-11 method on the specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11BalanceOf(tokenHash, owner util.Uint160) (int64, error) {
return c.nepBalanceOf(tokenHash, owner, nil)
}
// NEP11TokenInfo returns full NEP-11 token info.
//
// Deprecated: please use Info method from the neptoken subpackage. This method
// will be removed in future versions.
func (c *Client) NEP11TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
return c.nepTokenInfo(tokenHash, manifest.NEP11StandardName)
}
// TransferNEP11 creates an invocation transaction that invokes 'transfer' method
// on the given token to move the whole NEP-11 token with the specified token ID to
// the given account and sends it to the network returning just a hash of it.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, tokenID string, data any, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, to, tokenID, data)
if err != nil {
return util.Uint256{}, err
}
return c.SignAndPushTx(tx, acc, cosigners)
}
// CreateNEP11TransferTx creates an invocation transaction for the 'transfer'
// method of the given contract (token) to move the whole (or the specified amount
// of) NEP-11 token with the specified token ID to the given account and returns it.
// The returned transaction is not signed. CreateNEP11TransferTx is also a
// helper for TransferNEP11 and TransferNEP11D.
// `args` for TransferNEP11: to util.Uint160, tokenID string, data interface{};
// `args` for TransferNEP11D: from, to util.Uint160, amount int64, tokenID string, data interface{}.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint160,
gas int64, cosigners []SignerAccount, args ...any) (*transaction.Transaction, error) {
script, err := smartcontract.CreateCallWithAssertScript(tokenHash, "transfer", args...)
if err != nil {
return nil, fmt.Errorf("failed to create NEP-11 transfer script: %w", err)
}
return c.CreateTxFromScript(script, acc, -1, gas, append([]SignerAccount{{
Signer: transaction.Signer{
Account: acc.ScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc,
}}, cosigners...))
}
// NEP11TokensOf returns iterator over token IDs for the specified owner of the
// specified NFT token. First return value is the session ID, the second one is
// Iterator itself, the third one is an error. Use TraverseIterator method to
// traverse iterator values or TerminateSession to terminate opened iterator
// session. See TraverseIterator and TerminateSession documentation for more details.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11TokensOf(tokenHash util.Uint160, owner util.Uint160) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.reader.Call(tokenHash, "tokensOf", owner))
}
// NEP11UnpackedTokensOf returns an array of token IDs for the specified owner of the specified NFT token
// (config.DefaultMaxIteratorResultItems at max). It differs from NEP11TokensOf in that no iterator session
// is used to retrieve values from iterator. Instead, unpacking VM script is created and invoked via
// `invokescript` JSON-RPC call.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11UnpackedTokensOf(tokenHash util.Uint160, owner util.Uint160) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.reader.CallAndExpandIterator(tokenHash, "tokensOf", config.DefaultMaxIteratorResultItems, owner))
}
// Non-divisible NFT methods section start.
// NEP11NDOwnerOf invokes `ownerOf` non-divisible NEP-11 method with the
// specified token ID on the specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID []byte) (util.Uint160, error) {
return unwrap.Uint160(c.reader.Call(tokenHash, "ownerOf", tokenID))
}
// Non-divisible NFT methods section end.
// Divisible NFT methods section start.
// TransferNEP11D creates an invocation transaction that invokes 'transfer'
// method on the given token to move the specified amount of divisible NEP-11 assets
// (in FixedN format using contract's number of decimals) to the given account and
// sends it to the network returning just a hash of it.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, amount int64, tokenID []byte, data any, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, acc.ScriptHash(), to, amount, tokenID, data)
if err != nil {
return util.Uint256{}, err
}
return c.SignAndPushTx(tx, acc, cosigners)
}
// NEP11DBalanceOf invokes `balanceOf` divisible NEP-11 method on a
// specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11DBalanceOf(tokenHash, owner util.Uint160, tokenID []byte) (int64, error) {
return c.nepBalanceOf(tokenHash, owner, tokenID)
}
// NEP11DOwnerOf returns iterator over the specified NEP-11 divisible token owners. First return value
// is the session ID, the second one is Iterator itself, the third one is an error. Use TraverseIterator
// method to traverse iterator values or TerminateSession to terminate opened iterator session. See
// TraverseIterator and TerminateSession documentation for more details.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11DOwnerOf(tokenHash util.Uint160, tokenID []byte) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.reader.Call(tokenHash, "ownerOf", tokenID))
}
// NEP11DUnpackedOwnerOf returns list of the specified NEP-11 divisible token owners
// (config.DefaultMaxIteratorResultItems at max). It differs from NEP11DOwnerOf in that no
// iterator session is used to retrieve values from iterator. Instead, unpacking VM
// script is created and invoked via `invokescript` JSON-RPC call.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11DUnpackedOwnerOf(tokenHash util.Uint160, tokenID []byte) ([]util.Uint160, error) {
arr, err := unwrap.ArrayOfBytes(c.reader.CallAndExpandIterator(tokenHash, "ownerOf", config.DefaultMaxIteratorResultItems, tokenID))
if err != nil {
return nil, err
}
owners := make([]util.Uint160, len(arr))
for i := range arr {
owners[i], err = util.Uint160DecodeBytesBE(arr[i])
if err != nil {
return nil, fmt.Errorf("not a Uint160 at %d: %w", i, err)
}
}
return owners, nil
}
// Divisible NFT methods section end.
// Optional NFT methods section start.
// NEP11Properties invokes `properties` optional NEP-11 method on the
// specified contract.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11Properties(tokenHash util.Uint160, tokenID []byte) (*stackitem.Map, error) {
return unwrap.Map(c.reader.Call(tokenHash, "properties", tokenID))
}
// NEP11Tokens returns iterator over the tokens minted by the contract. First return
// value is the session ID, the second one is Iterator itself, the third one is an
// error. Use TraverseIterator method to traverse iterator values or
// TerminateSession to terminate opened iterator session. See TraverseIterator and
// TerminateSession documentation for more details.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11Tokens(tokenHash util.Uint160) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.reader.Call(tokenHash, "tokens"))
}
// NEP11UnpackedTokens returns list of the tokens minted by the contract
// (config.DefaultMaxIteratorResultItems at max). It differs from NEP11Tokens in that no
// iterator session is used to retrieve values from iterator. Instead, unpacking
// VM script is created and invoked via `invokescript` JSON-RPC call.
//
// Deprecated: please use nep11 package, this method will be removed in future
// versions.
func (c *Client) NEP11UnpackedTokens(tokenHash util.Uint160) ([][]byte, error) {
return unwrap.ArrayOfBytes(c.reader.CallAndExpandIterator(tokenHash, "tokens", config.DefaultMaxIteratorResultItems))
}
// Optional NFT methods section end.

View file

@ -1,188 +0,0 @@
package rpcclient
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
// TransferTarget represents target address, token amount and data for transfer.
type TransferTarget struct {
Token util.Uint160
Address util.Uint160
Amount int64
Data any
}
// SignerAccount represents combination of the transaction.Signer and the
// corresponding wallet.Account.
type SignerAccount struct {
Signer transaction.Signer
Account *wallet.Account
}
// NEP17Decimals invokes `decimals` NEP-17 method on the specified contract.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions.
func (c *Client) NEP17Decimals(tokenHash util.Uint160) (int64, error) {
return c.nepDecimals(tokenHash)
}
// NEP17Symbol invokes `symbol` NEP-17 method on the specified contract.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions.
func (c *Client) NEP17Symbol(tokenHash util.Uint160) (string, error) {
return c.nepSymbol(tokenHash)
}
// NEP17TotalSupply invokes `totalSupply` NEP-17 method on the specified contract.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions. This method is also wrong since tokens can return values overflowing
// int64.
func (c *Client) NEP17TotalSupply(tokenHash util.Uint160) (int64, error) {
return c.nepTotalSupply(tokenHash)
}
// NEP17BalanceOf invokes `balanceOf` NEP-17 method on the specified contract.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions. This method is also wrong since tokens can return values overflowing
// int64.
func (c *Client) NEP17BalanceOf(tokenHash, acc util.Uint160) (int64, error) {
return c.nepBalanceOf(tokenHash, acc, nil)
}
// NEP17TokenInfo returns full NEP-17 token info.
//
// Deprecated: please use Info method from the neptoken subpackage. This method
// will be removed in future versions.
func (c *Client) NEP17TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
return c.nepTokenInfo(tokenHash, manifest.NEP17StandardName)
}
// CreateNEP17TransferTx creates an invocation transaction for the 'transfer'
// method of the given contract (token) to move the specified amount of NEP-17 assets
// (in FixedN format using contract's number of decimals) to the given account and
// returns it. The returned transaction is not signed.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions.
func (c *Client) CreateNEP17TransferTx(acc *wallet.Account, to util.Uint160,
token util.Uint160, amount int64, gas int64, data any, cosigners []SignerAccount) (*transaction.Transaction, error) {
return c.CreateNEP17MultiTransferTx(acc, gas, []TransferTarget{
{Token: token,
Address: to,
Amount: amount,
Data: data,
},
}, cosigners)
}
// CreateNEP17MultiTransferTx creates an invocation transaction for performing
// NEP-17 transfers from a single sender to multiple recipients with the given
// data and cosigners. The transaction sender is included with the CalledByEntry
// scope by default.
//
// Deprecated: please use nep17 package (when transferring the same token) or
// [smartcontract.Builder] (when transferring multiple tokens), this method will
// be removed in future versions.
func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64,
recipients []TransferTarget, cosigners []SignerAccount) (*transaction.Transaction, error) {
from := acc.ScriptHash()
b := smartcontract.NewBuilder()
for i := range recipients {
b.InvokeWithAssert(recipients[i].Token, "transfer",
from, recipients[i].Address, recipients[i].Amount, recipients[i].Data)
}
script, err := b.Script()
if err != nil {
return nil, fmt.Errorf("failed to create transfer script: %w", err)
}
return c.CreateTxFromScript(script, acc, -1, gas, append([]SignerAccount{{
Signer: transaction.Signer{
Account: from,
Scopes: transaction.CalledByEntry,
},
Account: acc,
}}, cosigners...))
}
// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee.
// If sysFee <= 0, it is determined via result of `invokescript` RPC. You should
// initialize network magic with Init before calling CreateTxFromScript.
//
// Deprecated: please use actor.Actor API, this method will be removed in future
// versions.
func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64,
cosigners []SignerAccount) (*transaction.Transaction, error) {
signers, accounts, err := getSigners(acc, cosigners)
if err != nil {
return nil, fmt.Errorf("failed to construct tx signers: %w", err)
}
if sysFee < 0 {
result, err := c.InvokeScript(script, signers)
if err != nil {
return nil, fmt.Errorf("can't add system fee to transaction: %w", err)
}
if result.State != "HALT" {
return nil, fmt.Errorf("can't add system fee to transaction: bad vm state: %s due to an error: %s", result.State, result.FaultException)
}
sysFee = result.GasConsumed
}
tx := transaction.New(script, sysFee)
tx.Signers = signers
tx.ValidUntilBlock, err = c.CalculateValidUntilBlock()
if err != nil {
return nil, fmt.Errorf("failed to add validUntilBlock to transaction: %w", err)
}
err = c.AddNetworkFee(tx, netFee, accounts...)
if err != nil {
return nil, fmt.Errorf("failed to add network fee: %w", err)
}
return tx, nil
}
// TransferNEP17 creates an invocation transaction that invokes 'transfer' method
// on the given token to move the specified amount of NEP-17 assets (in FixedN format
// using contract's number of decimals) to the given account with the data specified and
// sends it to the network returning just a hash of it. Cosigners argument
// specifies a set of the transaction cosigners (may be nil or may include sender)
// with a proper scope and the accounts to cosign the transaction. If cosigning is
// impossible (e.g. due to locked cosigner's account) an error is returned.
//
// Deprecated: please use nep17 package, this method will be removed in future
// versions.
func (c *Client) TransferNEP17(acc *wallet.Account, to util.Uint160, token util.Uint160,
amount int64, gas int64, data any, cosigners []SignerAccount) (util.Uint256, error) {
tx, err := c.CreateNEP17TransferTx(acc, to, token, amount, gas, data, cosigners)
if err != nil {
return util.Uint256{}, err
}
return c.SignAndPushTx(tx, acc, cosigners)
}
// MultiTransferNEP17 is similar to TransferNEP17, buf allows to have multiple recipients.
//
// Deprecated: please use nep17 package (when transferring the same token) or
// [smartcontract.Builder] (when transferring multiple tokens), this method will
// be removed in future versions.
func (c *Client) MultiTransferNEP17(acc *wallet.Account, gas int64, recipients []TransferTarget, cosigners []SignerAccount) (util.Uint256, error) {
tx, err := c.CreateNEP17MultiTransferTx(acc, gas, recipients, cosigners)
if err != nil {
return util.Uint256{}, err
}
return c.SignAndPushTx(tx, acc, cosigners)
}

View file

@ -1,66 +0,0 @@
package rpcclient
import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// GetFeePerByte invokes `getFeePerByte` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetFeePerByte() (int64, error) {
return c.invokeNativePolicyMethod("getFeePerByte")
}
// GetExecFeeFactor invokes `getExecFeeFactor` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetExecFeeFactor() (int64, error) {
return c.invokeNativePolicyMethod("getExecFeeFactor")
}
// GetStoragePrice invokes `getStoragePrice` method on a native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) GetStoragePrice() (int64, error) {
return c.invokeNativePolicyMethod("getStoragePrice")
}
// GetMaxNotValidBeforeDelta invokes `getMaxNotValidBeforeDelta` method on a native Notary contract.
//
// Deprecated: please use notary subpackage. This method will be removed
// in future versions.
func (c *Client) GetMaxNotValidBeforeDelta() (int64, error) {
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
if err != nil {
return 0, fmt.Errorf("failed to get native Notary hash: %w", err)
}
return c.invokeNativeGetMethod(notaryHash, "getMaxNotValidBeforeDelta")
}
// invokeNativePolicy method invokes Get* method on a native Policy contract.
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
policyHash, err := c.GetNativeContractHash(nativenames.Policy)
if err != nil {
return 0, fmt.Errorf("failed to get native Policy hash: %w", err)
}
return c.invokeNativeGetMethod(policyHash, operation)
}
func (c *Client) invokeNativeGetMethod(hash util.Uint160, operation string) (int64, error) {
return unwrap.Int64(c.reader.Call(hash, operation))
}
// IsBlocked invokes `isBlocked` method on native Policy contract.
//
// Deprecated: please use policy subpackage.
func (c *Client) IsBlocked(hash util.Uint160) (bool, error) {
policyHash, err := c.GetNativeContractHash(nativenames.Policy)
if err != nil {
return false, fmt.Errorf("failed to get native Policy hash: %w", err)
}
return unwrap.Bool(c.reader.Call(policyHash, "isBlocked", hash))
}

View file

@ -9,27 +9,19 @@ import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neorpc"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
var errNetworkNotInitialized = errors.New("RPC client network is not initialized")
@ -103,7 +95,7 @@ func (c *Client) getBlock(param any) (*block.Block, error) {
return nil, err
}
r := io.NewBinReaderFromBuf(resp)
sr, err := c.StateRootInHeader()
sr, err := c.stateRootInHeader()
if err != nil {
return nil, err
}
@ -136,7 +128,7 @@ func (c *Client) getBlockVerbose(param any) (*result.Block, error) {
resp = &result.Block{}
err error
)
sr, err := c.StateRootInHeader()
sr, err := c.stateRootInHeader()
if err != nil {
return nil, err
}
@ -171,7 +163,7 @@ func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) {
if err := c.performRequest("getblockheader", params, &resp); err != nil {
return nil, err
}
sr, err := c.StateRootInHeader()
sr, err := c.stateRootInHeader()
if err != nil {
return nil, err
}
@ -844,234 +836,6 @@ func (c *Client) SubmitRawOracleResponse(ps []any) error {
return c.performRequest("submitoracleresponse", ps, new(result.RelayResult))
}
// SignAndPushInvocationTx signs and pushes the given script as an invocation
// transaction using the given wif to sign it and the given cosigners to cosign it if
// possible. It spends the amount of gas specified. It returns a hash of the
// invocation transaction and an error. If one of the cosigners accounts is
// neither contract-based nor unlocked, an error is returned.
//
// Deprecated: please use actor.Actor API, this method will be removed in future
// versions.
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee fixedn.Fixed8, cosigners []SignerAccount) (util.Uint256, error) {
tx, err := c.CreateTxFromScript(script, acc, sysfee, int64(netfee), cosigners)
if err != nil {
return util.Uint256{}, fmt.Errorf("failed to create tx: %w", err)
}
return c.SignAndPushTx(tx, acc, cosigners)
}
// SignAndPushTx signs the given transaction using the given wif and cosigners and pushes
// it to the chain. It returns a hash of the transaction and an error. If one of
// the cosigners accounts is neither contract-based nor unlocked, an error is
// returned.
//
// Deprecated: please use actor.Actor API, this method will be removed in future
// versions.
func (c *Client) SignAndPushTx(tx *transaction.Transaction, acc *wallet.Account, cosigners []SignerAccount) (util.Uint256, error) {
var (
txHash util.Uint256
err error
)
m, err := c.GetNetwork()
if err != nil {
return txHash, fmt.Errorf("failed to sign tx: %w", err)
}
if err = acc.SignTx(m, tx); err != nil {
return txHash, fmt.Errorf("failed to sign tx: %w", err)
}
// try to add witnesses for the rest of the signers
for i, signer := range tx.Signers[1:] {
var isOk bool
for _, cosigner := range cosigners {
if signer.Account == cosigner.Signer.Account {
err = cosigner.Account.SignTx(m, tx)
if err != nil { // then account is non-contract-based and locked, but let's provide more detailed error
if paramNum := len(cosigner.Account.Contract.Parameters); paramNum != 0 && cosigner.Account.Contract.Deployed {
return txHash, fmt.Errorf("failed to add contract-based witness for signer #%d (%s): "+
"%d parameters must be provided to construct invocation script", i, address.Uint160ToString(signer.Account), paramNum)
}
return txHash, fmt.Errorf("failed to add witness for signer #%d (%s): account should be unlocked to add the signature. "+
"Store partially-signed transaction and then use 'wallet sign' command to cosign it", i, address.Uint160ToString(signer.Account))
}
isOk = true
break
}
}
if !isOk {
return txHash, fmt.Errorf("failed to add witness for signer #%d (%s): account wasn't provided", i, address.Uint160ToString(signer.Account))
}
}
txHash = tx.Hash()
actualHash, err := c.SendRawTransaction(tx)
if err != nil {
return txHash, fmt.Errorf("failed to send tx: %w", err)
}
if !actualHash.Equals(txHash) {
return actualHash, fmt.Errorf("sent and actual tx hashes mismatch:\n\tsent: %v\n\tactual: %v", txHash.StringLE(), actualHash.StringLE())
}
return txHash, nil
}
// getSigners returns an array of transaction signers and corresponding accounts from
// given sender and cosigners. If cosigners list already contains sender, the sender
// will be placed at the start of the list.
func getSigners(sender *wallet.Account, cosigners []SignerAccount) ([]transaction.Signer, []*wallet.Account, error) {
var (
signers []transaction.Signer
accounts []*wallet.Account
)
from := sender.ScriptHash()
s := transaction.Signer{
Account: from,
Scopes: transaction.None,
}
for _, c := range cosigners {
if c.Signer.Account == from {
s = c.Signer
continue
}
signers = append(signers, c.Signer)
accounts = append(accounts, c.Account)
}
signers = append([]transaction.Signer{s}, signers...)
accounts = append([]*wallet.Account{sender}, accounts...)
return signers, accounts, nil
}
// SignAndPushP2PNotaryRequest creates and pushes a P2PNotary request constructed from the main
// and fallback transactions using the given wif to sign it. It returns the request and an error.
// Fallback transaction is constructed from the given script using the amount of gas specified.
// For successful fallback transaction validation at least 2*transaction.NotaryServiceFeePerKey
// GAS should be deposited to the Notary contract.
// Main transaction should be constructed by the user. Several rules should be met for
// successful main transaction acceptance:
// 1. Native Notary contract should be a signer of the main transaction.
// 2. Notary signer should have None scope.
// 3. Main transaction should have dummy contract witness for Notary signer.
// 4. Main transaction should have NotaryAssisted attribute with NKeys specified.
// 5. NotaryAssisted attribute and dummy Notary witness (as long as the other incomplete witnesses)
// should be paid for. Use CalculateNotaryWitness to calculate the amount of network fee to pay
// for the attribute and Notary witness.
// 6. Main transaction either shouldn't have all witnesses attached (in this case none of them
// can be multisignature), or it only should have a partial multisignature.
//
// Note: client should be initialized before SignAndPushP2PNotaryRequest call.
//
// Deprecated: please use Actor from the notary subpackage. This method will be
// deleted in future versions.
func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fallbackScript []byte, fallbackSysFee int64, fallbackNetFee int64, fallbackValidFor uint32, acc *wallet.Account) (*payload.P2PNotaryRequest, error) {
var err error
notaryHash, err := c.GetNativeContractHash(nativenames.Notary)
if err != nil {
return nil, fmt.Errorf("failed to get native Notary hash: %w", err)
}
from := acc.ScriptHash()
signers := []transaction.Signer{{Account: notaryHash}, {Account: from}}
if fallbackSysFee < 0 {
result, err := c.InvokeScript(fallbackScript, signers)
if err != nil {
return nil, fmt.Errorf("can't add system fee to fallback transaction: %w", err)
}
if result.State != "HALT" {
return nil, fmt.Errorf("can't add system fee to fallback transaction: bad vm state %s due to an error: %s", result.State, result.FaultException)
}
fallbackSysFee = result.GasConsumed
}
maxNVBDelta, err := c.GetMaxNotValidBeforeDelta()
if err != nil {
return nil, fmt.Errorf("failed to get MaxNotValidBeforeDelta")
}
if int64(fallbackValidFor) > maxNVBDelta {
return nil, fmt.Errorf("fallback transaction should be valid for not more than %d blocks", maxNVBDelta)
}
fallbackTx := transaction.New(fallbackScript, fallbackSysFee)
fallbackTx.Signers = signers
fallbackTx.ValidUntilBlock = mainTx.ValidUntilBlock
fallbackTx.Attributes = []transaction.Attribute{
{
Type: transaction.NotaryAssistedT,
Value: &transaction.NotaryAssisted{NKeys: 0},
},
{
Type: transaction.NotValidBeforeT,
Value: &transaction.NotValidBefore{Height: fallbackTx.ValidUntilBlock - fallbackValidFor + 1},
},
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{Hash: mainTx.Hash()},
},
}
fallbackTx.Scripts = []transaction.Witness{
{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...),
VerificationScript: []byte{},
},
{
InvocationScript: []byte{},
VerificationScript: acc.GetVerificationScript(),
},
}
fallbackTx.NetworkFee, err = c.CalculateNetworkFee(fallbackTx)
if err != nil {
return nil, fmt.Errorf("failed to add network fee: %w", err)
}
fallbackTx.NetworkFee += fallbackNetFee
m, err := c.GetNetwork()
if err != nil {
return nil, fmt.Errorf("failed to sign fallback tx: %w", err)
}
if err = acc.SignTx(m, fallbackTx); err != nil {
return nil, fmt.Errorf("failed to sign fallback tx: %w", err)
}
fallbackHash := fallbackTx.Hash()
req := &payload.P2PNotaryRequest{
MainTransaction: mainTx,
FallbackTransaction: fallbackTx,
}
req.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.SignHashable(m, req)...),
VerificationScript: acc.GetVerificationScript(),
}
actualHash, err := c.SubmitP2PNotaryRequest(req)
if err != nil {
return req, fmt.Errorf("failed to submit notary request: %w", err)
}
if !actualHash.Equals(fallbackHash) {
return req, fmt.Errorf("sent and actual fallback tx hashes mismatch:\n\tsent: %v\n\tactual: %v", fallbackHash.StringLE(), actualHash.StringLE())
}
return req, nil
}
// CalculateNotaryFee calculates network fee for one dummy Notary witness and NotaryAssisted attribute with NKeys specified.
// The result should be added to the transaction's net fee for successful verification.
//
// Deprecated: NeoGo calculatenetworkfee method handles notary fees as well since 0.99.3, so
// this method is just no longer needed and will be removed in future versions.
func (c *Client) CalculateNotaryFee(nKeys uint8) (int64, error) {
baseExecFee, err := c.GetExecFeeFactor()
if err != nil {
return 0, fmt.Errorf("failed to get BaseExecFeeFactor: %w", err)
}
feePerByte, err := c.GetFeePerByte()
if err != nil {
return 0, fmt.Errorf("failed to get FeePerByte: %w", err)
}
feePerKey, err := c.GetNotaryServiceFeePerKey()
if err != nil {
return 0, fmt.Errorf("failed to get NotaryServiceFeePerKey: %w", err)
}
return int64((nKeys+1))*feePerKey + // fee for NotaryAssisted attribute
fee.Opcode(baseExecFee, // Notary node witness
opcode.PUSHDATA1, opcode.RET, // invocation script
opcode.PUSH0, opcode.SYSCALL, opcode.RET) + // System.Contract.CallNative
nativeprices.NotaryVerificationPrice*baseExecFee + // Notary witness verification price
feePerByte*int64(io.GetVarSize(make([]byte, 66))) + // invocation script per-byte fee
feePerByte*int64(io.GetVarSize([]byte{})), // verification script per-byte fee
nil
}
// SubmitP2PNotaryRequest submits given P2PNotaryRequest payload to the RPC node.
func (c *Client) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error) {
var resp = new(result.RelayResult)
@ -1103,113 +867,9 @@ func (c *Client) ValidateAddress(address string) error {
return nil
}
// CalculateValidUntilBlock calculates ValidUntilBlock field for tx as
// current blockchain height + number of validators. Number of validators
// is the length of blockchain validators list got from GetNextBlockValidators()
// method. Validators count is being cached and updated every 100 blocks.
//
// Deprecated: please use (*Actor).CalculateValidUntilBlock. This method will be
// removed in future versions.
func (c *Client) CalculateValidUntilBlock() (uint32, error) {
var (
result uint32
validatorsCount uint32
)
blockCount, err := c.GetBlockCount()
if err != nil {
return result, fmt.Errorf("can't get block count: %w", err)
}
c.cacheLock.RLock()
if c.cache.calculateValidUntilBlock.expiresAt > blockCount {
validatorsCount = c.cache.calculateValidUntilBlock.validatorsCount
c.cacheLock.RUnlock()
} else {
c.cacheLock.RUnlock()
validators, err := c.GetNextBlockValidators()
if err != nil {
return result, fmt.Errorf("can't get validators: %w", err)
}
validatorsCount = uint32(len(validators))
c.cacheLock.Lock()
c.cache.calculateValidUntilBlock = calculateValidUntilBlockCache{
validatorsCount: validatorsCount,
expiresAt: blockCount + cacheTimeout,
}
c.cacheLock.Unlock()
}
return blockCount + validatorsCount + 1, nil
}
// AddNetworkFee adds network fee for each witness script and optional extra
// network fee to transaction. `accs` is an array signer's accounts.
//
// Deprecated: please use CalculateNetworkFee or actor.Actor. This method will
// be removed in future versions.
func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs ...*wallet.Account) error {
if len(tx.Signers) != len(accs) {
return errors.New("number of signers must match number of scripts")
}
size := io.GetVarSize(tx)
var ef int64
for i, cosigner := range tx.Signers {
if accs[i].Contract.Deployed {
res, err := c.InvokeContractVerify(cosigner.Account, []smartcontract.Parameter{}, tx.Signers)
if err != nil {
return fmt.Errorf("failed to invoke verify: %w", err)
}
r, err := unwrap.Bool(res, err)
if err != nil {
return fmt.Errorf("signer #%d: %w", i, err)
}
if !r {
return fmt.Errorf("signer #%d: `verify` returned `false`", i)
}
tx.NetworkFee += res.GasConsumed
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty
continue
}
if ef == 0 {
var err error
ef, err = c.GetExecFeeFactor()
if err != nil {
return fmt.Errorf("can't get `ExecFeeFactor`: %w", err)
}
}
netFee, sizeDelta := fee.Calculate(ef, accs[i].Contract.Script)
tx.NetworkFee += netFee
size += sizeDelta
}
fee, err := c.GetFeePerByte()
if err != nil {
return err
}
tx.NetworkFee += int64(size)*fee + extraFee
return nil
}
// GetNetwork returns the network magic of the RPC node the client connected to. It
// requires Init to be done first, otherwise an error is returned.
//
// Deprecated: please use GetVersion (it has the same data in the Protocol section)
// or actor subpackage. This method will be removed in future versions.
func (c *Client) GetNetwork() (netmode.Magic, error) {
c.cacheLock.RLock()
defer c.cacheLock.RUnlock()
if !c.cache.initDone {
return 0, errNetworkNotInitialized
}
return c.cache.network, nil
}
// StateRootInHeader returns true if the state root is contained in the block header.
// You should initialize Client cache with Init() before calling StateRootInHeader.
//
// Deprecated: please use GetVersion (it has the same data in the Protocol section).
// This method will be removed in future versions.
func (c *Client) StateRootInHeader() (bool, error) {
// stateRootInHeader returns true if the state root is contained in the block header.
// Requires Init() before use.
func (c *Client) stateRootInHeader() (bool, error) {
c.cacheLock.RLock()
defer c.cacheLock.RUnlock()
@ -1219,29 +879,6 @@ func (c *Client) StateRootInHeader() (bool, error) {
return c.cache.stateRootInHeader, nil
}
// GetNativeContractHash returns native contract hash by its name.
//
// Deprecated: please use native contract subpackages that have hashes directly
// (gas, management, neo, notary, oracle, policy, rolemgmt) or
// GetContractStateByAddressOrName method that will return hash along with other
// data.
func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
c.cacheLock.RLock()
hash, ok := c.cache.nativeHashes[name]
c.cacheLock.RUnlock()
if ok {
return hash, nil
}
cs, err := c.GetContractStateByAddressOrName(name)
if err != nil {
return util.Uint160{}, err
}
c.cacheLock.Lock()
c.cache.nativeHashes[name] = cs.Hash
c.cacheLock.Unlock()
return cs.Hash, nil
}
// TraverseIterator returns a set of iterator values (maxItemsCount at max) for
// the specified iterator and session. If result contains no elements, then either
// Iterator has no elements or session was expired and terminated by the server.

View file

@ -2,7 +2,6 @@ package rpcclient
import (
"context"
"crypto/elliptic"
"encoding/base64"
"encoding/hex"
"encoding/json"
@ -18,7 +17,6 @@ import (
"github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/hash"
@ -403,136 +401,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
},
},
},
"getFeePerByte": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetFeePerByte()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(1000)
},
},
},
"getExecFeeFactor": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetExecFeeFactor()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(1000)
},
},
},
"getStoragePrice": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetStoragePrice()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"100000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(100000)
},
},
},
"getOraclePrice": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetOraclePrice()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"10000000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(10000000)
},
},
},
"getNNSPrice": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetNNSPrice(util.Uint160{1, 2, 3})
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"1000000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(1000000)
},
},
},
"getGasPerBlock": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetGasPerBlock()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"500000000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(500000000)
},
},
},
"getCandidateRegisterPrice": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetCandidateRegisterPrice()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMDWdldEZlZVBlckJ5dGUMFJphpG7sl7iTBtfOgfFbRiCR0AkyQWJ9W1I=","stack":[{"type":"Integer","value":"100000000000"}],"tx":null}}`,
result: func(c *Client) any {
return int64(100000000000)
},
},
},
"getDesignatedByRole": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetDesignatedByRole(noderoles.P2PNotary, 10)
},
serverResponse: `{"id" : 1,"result" : {"stack" : [{"value" : [{"type":"ByteString","value":"Aw0WkQoDc8WqpG18xPMTEgfHO6gRTVtMN0Mw6zw06fzl"},{"type":"ByteString","value":"A+bmJ9wIaj96Ygr+uQQvQ0AaUrQmj2b3AGnztAOkU3/L"}],"type" : "Array"}],"exception" : null,"script" : "ERQSwB8ME2dldERlc2lnbmF0ZWRCeVJvbGUMFOKV45FUTBeK2U8D7E3N/3hTTs9JQWJ9W1I=","gasconsumed" : "2028150","state" : "HALT"}, "jsonrpc" : "2.0"}`,
result: func(c *Client) any {
pk1Bytes, _ := base64.StdEncoding.DecodeString("Aw0WkQoDc8WqpG18xPMTEgfHO6gRTVtMN0Mw6zw06fzl")
pk1, err := keys.NewPublicKeyFromBytes(pk1Bytes, elliptic.P256())
if err != nil {
panic("invalid pub key #1 bytes")
}
pk2Bytes, _ := base64.StdEncoding.DecodeString("A+bmJ9wIaj96Ygr+uQQvQ0AaUrQmj2b3AGnztAOkU3/L")
pk2, err := keys.NewPublicKeyFromBytes(pk2Bytes, elliptic.P256())
if err != nil {
panic("invalid pub key #2 bytes")
}
return keys.PublicKeys{pk1, pk2}
},
},
},
"getMaxNotValidBeforeDelta": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.GetMaxNotValidBeforeDelta()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMD2dldE1heEJsb2NrU2l6ZQwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Integer","value":"262144"}],"tx":null}}`,
result: func(c *Client) any {
return int64(262144)
},
},
},
"isBlocked": {
{
name: "positive",
invoke: func(c *Client) (any, error) {
return c.IsBlocked(util.Uint160{1, 2, 3})
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"EMAMEmdldEJsb2NrZWRBY2NvdW50cwwUmmGkbuyXuJMG186B8VtGIJHQCTJBYn1bUg==","stack":[{"type":"Boolean","value":false}],"tx":null}}`,
result: func(c *Client) any {
return false
},
},
},
"getnep11balances": {
{
name: "positive",
@ -2138,92 +2006,6 @@ func wrapInitResponse(r *params.In, resp string) string {
return response
}
func TestCalculateValidUntilBlock(t *testing.T) {
var (
getBlockCountCalled int
getValidatorsCalled int
)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := params.NewRequest()
err := r.DecodeData(req.Body)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
var response string
switch r.In.Method {
case "getblockcount":
getBlockCountCalled++
response = `{"jsonrpc":"2.0","id":1,"result":50}`
case "getnextblockvalidators":
getValidatorsCalled++
response = `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`
}
requestHandler(t, r.In, w, response)
}))
t.Cleanup(srv.Close)
endpoint := srv.URL
opts := Options{}
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
c.getNextRequestID = getTestRequestID
require.NoError(t, c.Init())
validUntilBlock, err := c.CalculateValidUntilBlock()
assert.NoError(t, err)
assert.Equal(t, uint32(55), validUntilBlock)
assert.Equal(t, 1, getBlockCountCalled)
assert.Equal(t, 1, getValidatorsCalled)
// check, whether caching is working
validUntilBlock, err = c.CalculateValidUntilBlock()
assert.NoError(t, err)
assert.Equal(t, uint32(55), validUntilBlock)
assert.Equal(t, 2, getBlockCountCalled)
assert.Equal(t, 1, getValidatorsCalled)
}
func TestGetNetwork(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := params.NewRequest()
err := r.DecodeData(req.Body)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
// request handler already have `getversion` response wrapper
requestHandler(t, r.In, w, "")
}))
t.Cleanup(srv.Close)
endpoint := srv.URL
opts := Options{}
t.Run("bad", func(t *testing.T) {
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
c.getNextRequestID = getTestRequestID
// network was not initialised
_, err = c.GetNetwork()
require.ErrorIs(t, err, errNetworkNotInitialized)
require.Equal(t, false, c.cache.initDone)
})
t.Run("good", func(t *testing.T) {
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
c.getNextRequestID = getTestRequestID
require.NoError(t, c.Init())
m, err := c.GetNetwork()
require.NoError(t, err)
require.Equal(t, netmode.UnitTestNet, m)
})
}
func TestUninitedClient(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := params.NewRequest()
@ -2250,10 +2032,6 @@ func TestUninitedClient(t *testing.T) {
require.Error(t, err)
_, err = c.GetRawTransactionVerbose(util.Uint256{})
require.Error(t, err)
_, err = c.IsBlocked(util.Uint160{})
require.Error(t, err)
_, err = c.GetFeePerByte()
require.Error(t, err)
}
func newTestNEF(script []byte) nef.File {

View file

@ -566,7 +566,7 @@ readloop:
ntf := Notification{Type: event}
switch event {
case neorpc.BlockEventID:
sr, err := c.StateRootInHeader()
sr, err := c.stateRootInHeader()
if err != nil {
// Client is not initialized.
connCloseErr = fmt.Errorf("failed to fetch StateRootInHeader: %w", err)

View file

@ -31,7 +31,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neorpc"
@ -544,7 +543,7 @@ func TestClientNotary(t *testing.T) {
require.Error(t, err) // Can't be withdrawn until 1111.
}
func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
func TestCalculateNetworkFee_Base(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
@ -555,27 +554,8 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
require.NoError(t, err)
require.NoError(t, c.Init())
getAccounts := func(t *testing.T, n int) []*wallet.Account {
accs := make([]*wallet.Account, n)
var err error
for i := range accs {
accs[i], err = wallet.NewAccount()
require.NoError(t, err)
}
return accs
}
feePerByte := chain.FeePerByte()
t.Run("Invalid", func(t *testing.T) {
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
accs := getAccounts(t, 2)
tx.Signers = []transaction.Signer{{
Account: accs[0].PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
}}
require.Error(t, c.AddNetworkFee(tx, extraFee, accs[0], accs[1])) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
})
t.Run("Simple", func(t *testing.T) {
acc0 := wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0))
check := func(t *testing.T, extraFee int64) {
@ -593,16 +573,12 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
}
actualCalculatedNetFee, err := c.CalculateNetworkFee(tx)
require.NoError(t, err)
tx.Scripts = nil
require.NoError(t, c.AddNetworkFee(tx, extraFee, acc0)) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
actual := tx.NetworkFee
tx.NetworkFee = actualCalculatedNetFee + extraFee
require.NoError(t, acc0.SignTx(testchain.Network(), tx))
cFee, _ := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script)
expected := int64(io.GetVarSize(tx))*feePerByte + cFee + extraFee
require.Equal(t, expected, actual)
require.Equal(t, expected, actualCalculatedNetFee+extraFee)
err = chain.VerifyTx(tx)
if extraFee < 0 {
@ -658,12 +634,9 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
}
actualCalculatedNetFee, err := c.CalculateNetworkFee(tx)
require.NoError(t, err)
tx.NetworkFee = actualCalculatedNetFee + extraFee
tx.Scripts = nil
require.NoError(t, c.AddNetworkFee(tx, extraFee, acc0, acc1)) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
actual := tx.NetworkFee
require.NoError(t, acc0.SignTx(testchain.Network(), tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{
InvocationScript: testchain.Sign(tx),
@ -673,7 +646,6 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
cFeeM, _ := fee.Calculate(chain.GetBaseExecFee(), acc1.Contract.Script)
expected := int64(io.GetVarSize(tx))*feePerByte + cFee + cFeeM + extraFee
require.Equal(t, expected, actual)
require.Equal(t, expected, actualCalculatedNetFee+extraFee)
err = chain.VerifyTx(tx)
if extraFee < 0 {
@ -732,12 +704,11 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
}
actual, err := c.CalculateNetworkFee(tx)
require.NoError(t, err)
tx.Scripts = nil
tx.NetworkFee = actual + extraFee
require.NoError(t, c.AddNetworkFee(tx, extraFee, acc0, acc1)) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
tx.Scripts = nil
require.NoError(t, acc0.SignTx(testchain.Network(), tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{})
require.Equal(t, tx.NetworkFee, actual+extraFee)
err = chain.VerifyTx(tx)
if extraFee < 0 {
require.Error(t, err)
@ -759,37 +730,6 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
check(t, -1)
})
})
t.Run("Invalid", func(t *testing.T) {
tx := newTx(t)
acc0, err := wallet.NewAccount()
require.NoError(t, err)
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: h,
Scopes: transaction.Global,
},
}
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
})
t.Run("InvalidContract", func(t *testing.T) {
tx := newTx(t)
acc0 := wallet.NewAccountFromPrivateKey(priv)
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: util.Uint160{},
Scopes: transaction.Global,
},
}
require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) //nolint:staticcheck // SA1019: c.AddNetworkFee is deprecated
})
})
}
@ -863,160 +803,6 @@ func TestCalculateNetworkFee(t *testing.T) {
})
})
}
func TestSignAndPushInvocationTx(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
priv0 := testchain.PrivateKeyByID(0)
acc0 := wallet.NewAccountFromPrivateKey(priv0)
verifyWithoutParamsCtr, err := util.Uint160DecodeStringLE(verifyContractHash)
require.NoError(t, err)
acc1 := &wallet.Account{
Address: address.Uint160ToString(verifyWithoutParamsCtr),
Contract: &wallet.Contract{
Parameters: []wallet.ContractParam{},
Deployed: true,
},
Default: false,
}
verifyWithParamsCtr, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
require.NoError(t, err)
acc2 := &wallet.Account{
Address: address.Uint160ToString(verifyWithParamsCtr),
Contract: &wallet.Contract{
Parameters: []wallet.ContractParam{
{Name: "argString", Type: smartcontract.StringType},
{Name: "argInt", Type: smartcontract.IntegerType},
{Name: "argBool", Type: smartcontract.BoolType},
},
Deployed: true,
},
Default: false,
}
priv3 := testchain.PrivateKeyByID(3)
acc3 := wallet.NewAccountFromPrivateKey(priv3)
check := func(t *testing.T, h util.Uint256) {
mp := chain.GetMemPool()
tx, ok := mp.TryGetValue(h)
require.True(t, ok)
require.Equal(t, h, tx.Hash())
require.EqualValues(t, 30, tx.SystemFee)
}
t.Run("good", func(t *testing.T) {
t.Run("signer0: sig", func(t *testing.T) {
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc0, 30, 0, []rpcclient.SignerAccount{ //nolint:staticcheck // SA1019: c.SignAndPushInvocationTx is deprecated
{
Signer: transaction.Signer{
Account: priv0.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc0,
},
})
require.NoError(t, err)
check(t, h)
})
t.Run("signer0: sig; signer1: sig", func(t *testing.T) {
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc0, 30, 0, []rpcclient.SignerAccount{ //nolint:staticcheck // SA1019: c.SignAndPushInvocationTx is deprecated
{
Signer: transaction.Signer{
Account: priv0.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc0,
},
{
Signer: transaction.Signer{
Account: priv3.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc3,
},
})
require.NoError(t, err)
check(t, h)
})
t.Run("signer0: sig; signer1: contract-based paramless", func(t *testing.T) {
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc0, 30, 0, []rpcclient.SignerAccount{ //nolint:staticcheck // SA1019: c.SignAndPushInvocationTx is deprecated
{
Signer: transaction.Signer{
Account: priv0.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc0,
},
{
Signer: transaction.Signer{
Account: verifyWithoutParamsCtr,
Scopes: transaction.CalledByEntry,
},
Account: acc1,
},
})
require.NoError(t, err)
check(t, h)
})
})
t.Run("error", func(t *testing.T) {
t.Run("signer0: sig; signer1: contract-based with params", func(t *testing.T) {
_, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc0, 30, 0, []rpcclient.SignerAccount{ //nolint:staticcheck // SA1019: c.SignAndPushInvocationTx is deprecated
{
Signer: transaction.Signer{
Account: priv0.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc0,
},
{
Signer: transaction.Signer{
Account: verifyWithParamsCtr,
Scopes: transaction.CalledByEntry,
},
Account: acc2,
},
})
require.Error(t, err)
})
t.Run("signer0: sig; signer1: locked sig", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
acc4 := &wallet.Account{
Address: address.Uint160ToString(pk.GetScriptHash()),
Contract: &wallet.Contract{
Script: pk.PublicKey().GetVerificationScript(),
Parameters: []wallet.ContractParam{{Name: "parameter0", Type: smartcontract.SignatureType}},
},
}
_, err = c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc0, 30, 0, []rpcclient.SignerAccount{ //nolint:staticcheck // SA1019: c.SignAndPushInvocationTx is deprecated
{
Signer: transaction.Signer{
Account: priv0.GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc0,
},
{
Signer: transaction.Signer{
Account: util.Uint160{1, 2, 3},
Scopes: transaction.CalledByEntry,
},
Account: acc4,
},
})
require.Error(t, err)
})
})
}
func TestNotaryActor(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
@ -1049,92 +835,6 @@ func TestNotaryActor(t *testing.T) {
require.NoError(t, err)
}
func TestSignAndPushP2PNotaryRequest(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
acc, err := wallet.NewAccount()
require.NoError(t, err)
t.Run("client wasn't initialized", func(t *testing.T) {
_, err := c.SignAndPushP2PNotaryRequest(transaction.New([]byte{byte(opcode.RET)}, 123), []byte{byte(opcode.RET)}, -1, 0, 100, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NotNil(t, err)
})
require.NoError(t, c.Init())
t.Run("bad fallback script", func(t *testing.T) {
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NotNil(t, err)
})
t.Run("too large fallbackValidFor", func(t *testing.T) {
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.RET)}, -1, 0, 141, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NotNil(t, err)
})
t.Run("good", func(t *testing.T) {
sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
acc := wallet.NewAccountFromPrivateKey(sender)
expected := transaction.Transaction{
Attributes: []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
Script: []byte{byte(opcode.RET)},
ValidUntilBlock: chain.BlockHeight() + 5,
Signers: []transaction.Signer{{Account: util.Uint160{1, 5, 9}}},
Scripts: []transaction.Witness{{
InvocationScript: []byte{1, 4, 7},
VerificationScript: []byte{3, 6, 9},
}},
}
mainTx := expected
_ = expected.Hash()
req, err := c.SignAndPushP2PNotaryRequest(&mainTx, []byte{byte(opcode.RET)}, -1, 0, 6, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NoError(t, err)
// check that request was correctly completed
require.Equal(t, expected, *req.MainTransaction) // main tx should be the same
require.ElementsMatch(t, []transaction.Attribute{
{
Type: transaction.NotaryAssistedT,
Value: &transaction.NotaryAssisted{NKeys: 0},
},
{
Type: transaction.NotValidBeforeT,
Value: &transaction.NotValidBefore{Height: chain.BlockHeight()},
},
{
Type: transaction.ConflictsT,
Value: &transaction.Conflicts{Hash: mainTx.Hash()},
},
}, req.FallbackTransaction.Attributes)
require.Equal(t, []transaction.Signer{
{Account: chain.GetNotaryContractScriptHash()},
{Account: acc.PrivateKey().GetScriptHash()},
}, req.FallbackTransaction.Signers)
// it shouldn't be an error to add completed fallback to the chain
w, err := wallet.NewWalletFromFile(notaryPath)
require.NoError(t, err)
ntr := w.Accounts[0]
err = ntr.Decrypt(notaryPass, w.Scrypt)
require.NoError(t, err)
req.FallbackTransaction.Scripts[0] = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, ntr.PrivateKey().SignHashable(uint32(testchain.Network()), req.FallbackTransaction)...),
VerificationScript: []byte{},
}
b := testchain.NewBlock(t, chain, 1, 0, req.FallbackTransaction)
require.NoError(t, chain.AddBlock(b))
appLogs, err := chain.GetAppExecResults(req.FallbackTransaction.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(appLogs))
appLog := appLogs[0]
require.Equal(t, vmstate.Halt, appLog.VMState)
require.Equal(t, appLog.GasConsumed, req.FallbackTransaction.SystemFee)
})
}
func TestGetRawNotaryPoolAndTransaction(t *testing.T) {
var (
mainHash1, fallbackHash1, mainHash2, fallbackHash2 util.Uint256
@ -1266,20 +966,6 @@ func TestGetRawNotaryPoolAndTransaction(t *testing.T) {
})
}
func TestCalculateNotaryFee(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
t.Run("client not initialized", func(t *testing.T) {
_, err := c.CalculateNotaryFee(0) //nolint:staticcheck // SA1019: c.CalculateNotaryFee is deprecated
require.NoError(t, err) // Do not require client initialisation for this.
})
}
func TestPing(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
@ -1294,35 +980,6 @@ func TestPing(t *testing.T) {
require.Error(t, c.Ping())
}
func TestCreateTxFromScript(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
priv := testchain.PrivateKey(0)
acc := wallet.NewAccountFromPrivateKey(priv)
t.Run("NoSystemFee", func(t *testing.T) {
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10, nil) //nolint:staticcheck // SA1019: c.CreateTxFromScript is deprecated
require.NoError(t, err)
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
require.EqualValues(t, 30, tx.SystemFee) // PUSH1
require.True(t, len(tx.Signers) == 1)
require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account)
})
t.Run("ProvideSystemFee", func(t *testing.T) {
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, 123, 10, nil) //nolint:staticcheck // SA1019: c.CreateTxFromScript is deprecated
require.NoError(t, err)
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
require.EqualValues(t, 123, tx.SystemFee)
require.True(t, len(tx.Signers) == 1)
require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account)
})
}
func TestCreateNEP17TransferTx(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
@ -1885,21 +1542,6 @@ func TestClient_IteratorSessions(t *testing.T) {
})
}
func TestClient_GetNotaryServiceFeePerKey(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
var defaultNotaryServiceFeePerKey int64 = 1000_0000
actual, err := c.GetNotaryServiceFeePerKey() //nolint:staticcheck // SA1019: c.GetNotaryServiceFeePerKey is deprecated
require.NoError(t, err)
require.Equal(t, defaultNotaryServiceFeePerKey, actual)
}
func TestClient_States(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
@ -1973,63 +1615,6 @@ func TestClientOracle(t *testing.T) {
require.Equal(t, newPrice, actual)
}
func TestClient_InvokeAndPackIteratorResults(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
// storageItemsCount is the amount of storage items stored in Storage contract, it's hard-coded in the contract code.
const storageItemsCount = 255
expected := make([][]byte, storageItemsCount)
for i := 0; i < storageItemsCount; i++ {
expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes()
}
sort.Slice(expected, func(i, j int) bool {
if len(expected[i]) != len(expected[j]) {
return len(expected[i]) < len(expected[j])
}
return bytes.Compare(expected[i], expected[j]) < 0
})
storageHash, err := util.Uint160DecodeStringLE(storageContractHash)
require.NoError(t, err)
t.Run("default max items constraint", func(t *testing.T) {
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil) //nolint:staticcheck // SA1019: c.InvokeAndPackIteratorResults is deprecated
require.NoError(t, err)
require.Equal(t, vmstate.Halt.String(), res.State)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
arr, ok := res.Stack[0].Value().([]stackitem.Item)
require.True(t, ok)
require.Equal(t, config.DefaultMaxIteratorResultItems, len(arr))
for i := range arr {
require.Equal(t, stackitem.ByteArrayT, arr[i].Type())
require.Equal(t, expected[i], arr[i].Value().([]byte))
}
})
t.Run("custom max items constraint", func(t *testing.T) {
max := 123
res, err := c.InvokeAndPackIteratorResults(storageHash, "iterateOverValues", []smartcontract.Parameter{}, nil, max) //nolint:staticcheck // SA1019: c.InvokeAndPackIteratorResults is deprecated
require.NoError(t, err)
require.Equal(t, vmstate.Halt.String(), res.State)
require.Equal(t, 1, len(res.Stack))
require.Equal(t, stackitem.ArrayT, res.Stack[0].Type())
arr, ok := res.Stack[0].Value().([]stackitem.Item)
require.True(t, ok)
require.Equal(t, max, len(arr))
for i := range arr {
require.Equal(t, stackitem.ByteArrayT, arr[i].Type())
require.Equal(t, expected[i], arr[i].Value().([]byte))
}
})
}
func TestClient_Iterator_SessionConfigVariations(t *testing.T) {
var expected [][]byte
storageHash, err := util.Uint160DecodeStringLE(storageContractHash)