actor: fix event-based tx awaiting
If VUB-th block is received, we still can't guaranty that transaction wasn't accepted to chain. Back this situation by rolling back to a poll-based waiter.
This commit is contained in:
parent
6dbae7edc4
commit
95e23c8e46
3 changed files with 62 additions and 2 deletions
|
@ -304,13 +304,17 @@ func (w *EventWaiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Ui
|
|||
}
|
||||
|
||||
select {
|
||||
case _, ok := <-bRcvr:
|
||||
case b, ok := <-bRcvr:
|
||||
if !ok {
|
||||
// We're toast, retry with non-ws client.
|
||||
wsWaitErr = ErrMissedEvent
|
||||
return
|
||||
}
|
||||
waitErr = ErrTxNotAccepted
|
||||
// We can easily end up in a situation when subscription was performed too late and
|
||||
// the desired transaction and VUB-th block have already got accepted before the
|
||||
// subscription happened. Thus, always retry with non-ws client, it will perform
|
||||
// AER requests and make sure.
|
||||
wsWaitErr = fmt.Errorf("block #%d was received by EventWaiter", b.Index)
|
||||
case aer, ok := <-aerRcvr:
|
||||
if !ok {
|
||||
// We're toast, retry with non-ws client.
|
||||
|
|
|
@ -166,6 +166,7 @@ func TestWSWaiter_Wait(t *testing.T) {
|
|||
})
|
||||
|
||||
// Missing AER after VUB.
|
||||
c.RPCClient.appLog = nil
|
||||
go func() {
|
||||
_, err = w.Wait(h, bCount-2, nil)
|
||||
require.ErrorIs(t, err, ErrTxNotAccepted)
|
||||
|
|
|
@ -2073,3 +2073,58 @@ func TestWSClient_Wait(t *testing.T) {
|
|||
}
|
||||
require.True(t, faultedChecked, "FAULTed transaction wasn't checked")
|
||||
}
|
||||
|
||||
func TestWSClient_WaitWithLateSubscription(t *testing.T) {
|
||||
chain, rpcSrv, httpSrv := initClearServerWithServices(t, false, false, true)
|
||||
defer chain.Close()
|
||||
defer rpcSrv.Shutdown()
|
||||
|
||||
url := "ws" + strings.TrimPrefix(httpSrv.URL, "http") + "/ws"
|
||||
c, err := rpcclient.NewWS(context.Background(), url, rpcclient.Options{})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.Init())
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
act, err := actor.New(c, []actor.SignerAccount{
|
||||
{
|
||||
Signer: transaction.Signer{
|
||||
Account: acc.ScriptHash(),
|
||||
},
|
||||
Account: acc,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Firstly, accept the block.
|
||||
blocks := getTestBlocks(t)
|
||||
b1 := blocks[0]
|
||||
b2 := blocks[1]
|
||||
tx := b1.Transactions[0]
|
||||
require.NoError(t, chain.AddBlock(b1))
|
||||
|
||||
// After that, subscribe for AERs/blocks and wait.
|
||||
rcvr := make(chan *state.AppExecResult)
|
||||
go func() {
|
||||
aer, err := act.Wait(tx.Hash(), tx.ValidUntilBlock, nil)
|
||||
require.NoError(t, err)
|
||||
rcvr <- aer
|
||||
}()
|
||||
|
||||
// Accept the next block to trigger event-based waiter loop exit and rollback to
|
||||
// poll-based waiter.
|
||||
require.NoError(t, chain.AddBlock(b2))
|
||||
|
||||
// Wait for the result.
|
||||
waitloop:
|
||||
for {
|
||||
select {
|
||||
case aer := <-rcvr:
|
||||
require.Equal(t, tx.Hash(), aer.Container)
|
||||
require.Equal(t, trigger.Application, aer.Trigger)
|
||||
require.Equal(t, vmstate.Halt, aer.VMState)
|
||||
break waitloop
|
||||
case <-time.NewTimer(time.Duration(chain.GetConfig().SecondsPerBlock) * time.Second).C:
|
||||
t.Fatal("transaction failed to be awaited")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue