forked from TrueCloudLab/neoneo-go
rpcclient: move Waiter to a separate package
There are use-cases when not only Actor, but also Invoker and even simple RPC client must wait (e.g. sendtx or dumptx CLI commands). Actor requires optional signers in constructor, and it's not always appropriate to create Actor only to be able to use Waiter, sometimes it's needed to use only Waiter without Actor. Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
parent
28f1beff64
commit
4c6dca876c
5 changed files with 91 additions and 27 deletions
|
@ -17,6 +17,7 @@ import (
|
|||
"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/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
@ -72,7 +73,7 @@ type SignerAccount struct {
|
|||
// and ErrTxNotAccepted is returned if transaction wasn't accepted by this moment.
|
||||
type Actor struct {
|
||||
invoker.Invoker
|
||||
Waiter
|
||||
waiter.Waiter
|
||||
|
||||
client RPCActor
|
||||
opts Options
|
||||
|
@ -126,7 +127,7 @@ func New(ra RPCActor, signers []SignerAccount) (*Actor, error) {
|
|||
}
|
||||
return &Actor{
|
||||
Invoker: *inv,
|
||||
Waiter: NewWaiter(ra, version),
|
||||
Waiter: waiter.New(ra, version),
|
||||
client: ra,
|
||||
opts: NewDefaultOptions(),
|
||||
signers: signers,
|
||||
|
|
|
@ -11,9 +11,3 @@ func TestRPCActorRPCClientCompat(t *testing.T) {
|
|||
_ = actor.RPCActor(&rpcclient.WSClient{})
|
||||
_ = actor.RPCActor(&rpcclient.Client{})
|
||||
}
|
||||
|
||||
func TestRPCWaiterRPCClientCompat(t *testing.T) {
|
||||
_ = actor.RPCPollingWaiter(&rpcclient.Client{})
|
||||
_ = actor.RPCPollingWaiter(&rpcclient.WSClient{})
|
||||
_ = actor.RPCEventWaiter(&rpcclient.WSClient{})
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
|
||||
"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"
|
||||
|
@ -78,7 +79,7 @@ func (r *RPCClient) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*r
|
|||
return r.applog, nil
|
||||
}
|
||||
|
||||
var _ = actor.RPCPollingWaiter(&RPCClient{})
|
||||
var _ = waiter.RPCPollingWaiter(&RPCClient{})
|
||||
|
||||
func TestNewActor(t *testing.T) {
|
||||
rc := &RPCClient{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package actor
|
||||
package waiter
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -100,12 +100,12 @@ func errIsAlreadyExists(err error) bool {
|
|||
return strings.Contains(strings.ToLower(err.Error()), "already exists")
|
||||
}
|
||||
|
||||
// NewWaiter creates Waiter instance. It can be either websocket-based or
|
||||
// New creates Waiter instance. It can be either websocket-based or
|
||||
// polling-base, otherwise Waiter stub is returned. As a first argument
|
||||
// it accepts RPCEventWaiter implementation, RPCPollingWaiter implementation
|
||||
// or not an implementation of these two interfaces. It returns websocket-based
|
||||
// waiter, polling-based waiter or a stub correspondingly.
|
||||
func NewWaiter(base any, v *result.Version) Waiter {
|
||||
func New(base any, v *result.Version) Waiter {
|
||||
if eventW, ok := base.(RPCEventWaiter); ok {
|
||||
return &EventWaiter{
|
||||
ws: eventW,
|
|
@ -1,20 +1,82 @@
|
|||
package actor
|
||||
package waiter_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"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/neorpc"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
|
||||
"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/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type RPCClient struct {
|
||||
err error
|
||||
invRes *result.Invoke
|
||||
netFee int64
|
||||
bCount atomic.Uint32
|
||||
version *result.Version
|
||||
hash util.Uint256
|
||||
appLog *result.ApplicationLog
|
||||
context context.Context
|
||||
}
|
||||
|
||||
func (r *RPCClient) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
|
||||
return r.invRes, r.err
|
||||
}
|
||||
func (r *RPCClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) {
|
||||
return r.netFee, r.err
|
||||
}
|
||||
func (r *RPCClient) GetBlockCount() (uint32, error) {
|
||||
return r.bCount.Load(), r.err
|
||||
}
|
||||
func (r *RPCClient) GetVersion() (*result.Version, error) {
|
||||
verCopy := *r.version
|
||||
return &verCopy, r.err
|
||||
}
|
||||
func (r *RPCClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) {
|
||||
return r.hash, r.err
|
||||
}
|
||||
func (r *RPCClient) TerminateSession(sessionID uuid.UUID) (bool, error) {
|
||||
return false, nil // Just a stub, unused by actor.
|
||||
}
|
||||
func (r *RPCClient) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount int) ([]stackitem.Item, error) {
|
||||
return nil, nil // Just a stub, unused by actor.
|
||||
}
|
||||
func (r *RPCClient) Context() context.Context {
|
||||
if r.context == nil {
|
||||
return context.Background()
|
||||
}
|
||||
return r.context
|
||||
}
|
||||
|
||||
func (r *RPCClient) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) {
|
||||
if r.appLog != nil {
|
||||
return r.appLog, nil
|
||||
}
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
type AwaitableRPCClient struct {
|
||||
RPCClient
|
||||
|
||||
|
@ -38,16 +100,16 @@ func (c *AwaitableRPCClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr
|
|||
func (c *AwaitableRPCClient) Unsubscribe(id string) error { return nil }
|
||||
|
||||
func TestNewWaiter(t *testing.T) {
|
||||
w := NewWaiter((RPCActor)(nil), nil)
|
||||
_, ok := w.(NullWaiter)
|
||||
w := waiter.New((actor.RPCActor)(nil), nil)
|
||||
_, ok := w.(waiter.NullWaiter)
|
||||
require.True(t, ok)
|
||||
|
||||
w = NewWaiter(&RPCClient{}, &result.Version{})
|
||||
_, ok = w.(*PollingWaiter)
|
||||
w = waiter.New(&RPCClient{}, &result.Version{})
|
||||
_, ok = w.(*waiter.PollingWaiter)
|
||||
require.True(t, ok)
|
||||
|
||||
w = NewWaiter(&AwaitableRPCClient{RPCClient: RPCClient{}}, &result.Version{})
|
||||
_, ok = w.(*EventWaiter)
|
||||
w = waiter.New(&AwaitableRPCClient{RPCClient: RPCClient{}}, &result.Version{})
|
||||
_, ok = w.(*waiter.EventWaiter)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
|
@ -58,8 +120,8 @@ func TestPollingWaiter_Wait(t *testing.T) {
|
|||
expected := &state.AppExecResult{Container: h, Execution: state.Execution{}}
|
||||
c := &RPCClient{appLog: appLog}
|
||||
c.bCount.Store(bCount)
|
||||
w := NewWaiter(c, &result.Version{Protocol: result.Protocol{MillisecondsPerBlock: 1}}) // reduce testing time.
|
||||
_, ok := w.(*PollingWaiter)
|
||||
w := waiter.New(c, &result.Version{Protocol: result.Protocol{MillisecondsPerBlock: 1}}) // reduce testing time.
|
||||
_, ok := w.(*waiter.PollingWaiter)
|
||||
require.True(t, ok)
|
||||
|
||||
// Wait with error.
|
||||
|
@ -75,7 +137,7 @@ func TestPollingWaiter_Wait(t *testing.T) {
|
|||
// Missing AER after VUB.
|
||||
c.appLog = nil
|
||||
_, err = w.Wait(h, bCount-2, nil)
|
||||
require.ErrorIs(t, ErrTxNotAccepted, err)
|
||||
require.ErrorIs(t, waiter.ErrTxNotAccepted, err)
|
||||
|
||||
checkErr := func(t *testing.T, trigger func(), target error) {
|
||||
errCh := make(chan error)
|
||||
|
@ -106,14 +168,14 @@ func TestPollingWaiter_Wait(t *testing.T) {
|
|||
// Tx is accepted before VUB.
|
||||
c.appLog = nil
|
||||
c.bCount.Store(bCount)
|
||||
checkErr(t, func() { c.bCount.Store(bCount + 1) }, ErrTxNotAccepted)
|
||||
checkErr(t, func() { c.bCount.Store(bCount + 1) }, waiter.ErrTxNotAccepted)
|
||||
|
||||
// Context is cancelled.
|
||||
c.appLog = nil
|
||||
c.bCount.Store(bCount)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
c.context = ctx
|
||||
checkErr(t, cancel, ErrContextDone)
|
||||
checkErr(t, cancel, waiter.ErrContextDone)
|
||||
}
|
||||
|
||||
func TestWSWaiter_Wait(t *testing.T) {
|
||||
|
@ -123,8 +185,8 @@ func TestWSWaiter_Wait(t *testing.T) {
|
|||
expected := &state.AppExecResult{Container: h, Execution: state.Execution{}}
|
||||
c := &AwaitableRPCClient{RPCClient: RPCClient{appLog: appLog}}
|
||||
c.bCount.Store(bCount)
|
||||
w := NewWaiter(c, &result.Version{Protocol: result.Protocol{MillisecondsPerBlock: 1}}) // reduce testing time.
|
||||
_, ok := w.(*EventWaiter)
|
||||
w := waiter.New(c, &result.Version{Protocol: result.Protocol{MillisecondsPerBlock: 1}}) // reduce testing time.
|
||||
_, ok := w.(*waiter.EventWaiter)
|
||||
require.True(t, ok)
|
||||
|
||||
// Wait with error.
|
||||
|
@ -176,7 +238,7 @@ func TestWSWaiter_Wait(t *testing.T) {
|
|||
// Missing AER after VUB.
|
||||
go func() {
|
||||
_, err = w.Wait(h, bCount-2, nil)
|
||||
require.ErrorIs(t, err, ErrTxNotAccepted)
|
||||
require.ErrorIs(t, err, waiter.ErrTxNotAccepted)
|
||||
doneCh <- struct{}{}
|
||||
}()
|
||||
check(t, func() {
|
||||
|
@ -185,3 +247,9 @@ func TestWSWaiter_Wait(t *testing.T) {
|
|||
c.subBlockCh <- &block.Block{}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRPCWaiterRPCClientCompat(t *testing.T) {
|
||||
_ = waiter.RPCPollingWaiter(&rpcclient.Client{})
|
||||
_ = waiter.RPCPollingWaiter(&rpcclient.WSClient{})
|
||||
_ = waiter.RPCEventWaiter(&rpcclient.WSClient{})
|
||||
}
|
Loading…
Reference in a new issue