forked from TrueCloudLab/neoneo-go
actor: don't abort waiter on "already exists" error
It can happen in many cases of distributed tx generation/submission, we can just wait normally in this case and there will be some proper result.
This commit is contained in:
parent
cd6bb68246
commit
fd04b2befd
2 changed files with 21 additions and 5 deletions
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
@ -41,7 +42,11 @@ type (
|
||||||
// Wait allows to wait until transaction will be accepted to the chain. It can be
|
// Wait allows to wait until transaction will be accepted to the chain. It can be
|
||||||
// used as a wrapper for Send or SignAndSend and accepts transaction hash,
|
// used as a wrapper for Send or SignAndSend and accepts transaction hash,
|
||||||
// ValidUntilBlock value and an error. It returns transaction execution result
|
// ValidUntilBlock value and an error. It returns transaction execution result
|
||||||
// or an error if transaction wasn't accepted to the chain.
|
// or an error if transaction wasn't accepted to the chain. Notice that "already
|
||||||
|
// exists" err value is not treated as an error by this routine because it
|
||||||
|
// means that the transactions given might be already accepted or soon going
|
||||||
|
// to be accepted. Such transaction can be waited for in a usual way, potentially
|
||||||
|
// with positive result, so that's what will happen.
|
||||||
Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error)
|
Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error)
|
||||||
// WaitAny waits until at least one of the specified transactions will be accepted
|
// WaitAny waits until at least one of the specified transactions will be accepted
|
||||||
// to the chain until vub (including). It returns execution result of this
|
// to the chain until vub (including). It returns execution result of this
|
||||||
|
@ -89,6 +94,12 @@ type EventWaiter struct {
|
||||||
polling Waiter
|
polling Waiter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errIsAlreadyExists is a temporary helper until we have #2248 solved. Both C#
|
||||||
|
// and Go nodes return this string (possibly among other data).
|
||||||
|
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
|
// newWaiter creates Waiter instance. It can be either websocket-based or
|
||||||
// polling-base, otherwise Waiter stub is returned.
|
// polling-base, otherwise Waiter stub is returned.
|
||||||
func newWaiter(ra RPCActor, v *result.Version) Waiter {
|
func newWaiter(ra RPCActor, v *result.Version) Waiter {
|
||||||
|
@ -139,7 +150,7 @@ func NewPollingWaiter(waiter RPCPollingWaiter) (*PollingWaiter, error) {
|
||||||
|
|
||||||
// Wait implements Waiter interface.
|
// Wait implements Waiter interface.
|
||||||
func (w *PollingWaiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
func (w *PollingWaiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||||
if err != nil {
|
if err != nil && !errIsAlreadyExists(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return w.WaitAny(context.TODO(), vub, h)
|
return w.WaitAny(context.TODO(), vub, h)
|
||||||
|
@ -209,7 +220,7 @@ func NewEventWaiter(waiter RPCEventWaiter) (*EventWaiter, error) {
|
||||||
|
|
||||||
// Wait implements Waiter interface.
|
// Wait implements Waiter interface.
|
||||||
func (w *EventWaiter) Wait(h util.Uint256, vub uint32, err error) (res *state.AppExecResult, waitErr error) {
|
func (w *EventWaiter) Wait(h util.Uint256, vub uint32, err error) (res *state.AppExecResult, waitErr error) {
|
||||||
if err != nil {
|
if err != nil && !errIsAlreadyExists(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return w.WaitAny(context.TODO(), vub, h)
|
return w.WaitAny(context.TODO(), vub, h)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"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/core/transaction"
|
||||||
|
@ -320,9 +321,13 @@ func (a *Actor) SendRequestExactly(mainTx *transaction.Transaction, fbTx *transa
|
||||||
// the resulting application execution result or actor.ErrTxNotAccepted if both transactions
|
// the resulting application execution result or actor.ErrTxNotAccepted if both transactions
|
||||||
// failed to persist. Wait can be used if underlying Actor supports transaction awaiting,
|
// failed to persist. Wait can be used if underlying Actor supports transaction awaiting,
|
||||||
// see actor.Actor and actor.Waiter documentation for details. Wait may be used as a wrapper
|
// see actor.Actor and actor.Waiter documentation for details. Wait may be used as a wrapper
|
||||||
// for Notarize, SendRequest or SendRequestExactly.
|
// for Notarize, SendRequest or SendRequestExactly. Notice that "already exists" or "already
|
||||||
|
// on chain" answers are not treated as errors by this routine because they mean that some
|
||||||
|
// of the transactions given might be already accepted or soon going to be accepted. These
|
||||||
|
// transactions can be waited for in a usual way potentially with positive result.
|
||||||
func (a *Actor) Wait(mainHash, fbHash util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
func (a *Actor) Wait(mainHash, fbHash util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||||
if err != nil {
|
// #2248 will eventually remove this garbage from the code.
|
||||||
|
if err != nil && !(strings.Contains(strings.ToLower(err.Error()), "already exists") || strings.Contains(strings.ToLower(err.Error()), "already on chain")) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.WaitAny(context.TODO(), vub, mainHash, fbHash)
|
return a.WaitAny(context.TODO(), vub, mainHash, fbHash)
|
||||||
|
|
Loading…
Reference in a new issue