Skip to content

Commit

Permalink
actor: add a new WaitSuccess API
Browse files Browse the repository at this point in the history
Most of the time people are interested in successful executions. Unfortunately,
unwrap package can't help here because of a different result structure (some
interface abstract can help, but it's still mostly stack-oriented and sessions
can be a problem), so this additional interface is needed.

Signed-off-by: Roman Khimov <[email protected]>
  • Loading branch information
roman-khimov committed Jun 20, 2024
1 parent 4ff2063 commit b713e70
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 0 deletions.
16 changes: 16 additions & 0 deletions pkg/rpcclient/actor/actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (
"fmt"

"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"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/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/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)

Expand Down Expand Up @@ -285,3 +287,17 @@ func (a *Actor) SendUncheckedRun(script []byte, sysfee int64, attrs []transactio
func (a *Actor) Sender() util.Uint160 {
return a.txSigners[0].Account
}

// WaitSuccess is similar to [waiter.Wait], but also checks for the VM state
// to be HALT (successful execution). Execution result is still returned in
// case you need to examine events or stack.
func (a *Actor) WaitSuccess(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
aer, err := a.Wait(h, vub, err)
if err != nil {
return nil, err
}
if aer.VMState != vmstate.Halt {
return nil, fmt.Errorf("vm failed: %s", aer.FaultException)
}
return aer, nil
}
37 changes: 37 additions & 0 deletions pkg/rpcclient/actor/actor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import (

"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"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/result"
"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/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -295,3 +297,38 @@ func TestSender(t *testing.T) {
require.NoError(t, err)
require.Equal(t, acc.ScriptHash(), a.Sender())
}

func TestWaitSuccess(t *testing.T) {
client, acc := testRPCAndAccount(t)
a, err := NewSimple(client, acc)
require.NoError(t, err)

someErr := errors.New("someErr")
_, err = a.WaitSuccess(util.Uint256{}, 0, someErr)
require.ErrorIs(t, err, someErr)

cont := util.Uint256{1, 2, 3}
ex := state.Execution{
Trigger: trigger.Application,
VMState: vmstate.Halt,
GasConsumed: 123,
Stack: []stackitem.Item{stackitem.Null{}},
}
applog := &result.ApplicationLog{
Container: cont,
IsTransaction: true,
Executions: []state.Execution{ex},
}
client.appLog = applog
client.appLog.Executions[0].VMState = vmstate.Fault
_, err = a.WaitSuccess(util.Uint256{}, 0, nil)
require.Error(t, err)

client.appLog.Executions[0].VMState = vmstate.Halt
res, err := a.WaitSuccess(util.Uint256{}, 0, nil)
require.NoError(t, err)
require.Equal(t, &state.AppExecResult{
Container: cont,
Execution: ex,
}, res)
}
19 changes: 19 additions & 0 deletions pkg/rpcclient/notary/actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)

Expand Down Expand Up @@ -332,3 +333,21 @@ func (a *Actor) Wait(mainHash, fbHash util.Uint256, vub uint32, err error) (*sta
}
return a.WaitAny(context.TODO(), vub, mainHash, fbHash)
}

// WaitSuccess works similar to [Actor.Wait], but checks that the main
// transaction was accepted and it has a HALT VM state (executed successfully).
// [state.AppExecResult] is still returned in case you need some additional
// event or stack checks.
func (a *Actor) WaitSuccess(mainHash, fbHash util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
aer, err := a.Wait(mainHash, fbHash, vub, err)
if err != nil {
return nil, err
}
if aer.Container != mainHash {
return nil, errors.New("fallback transaction accepted")
}
if aer.VMState != vmstate.Halt {
return nil, fmt.Errorf("vm failed: %s", aer.FaultException)
}
return aer, nil
}
21 changes: 21 additions & 0 deletions pkg/rpcclient/notary/actor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,9 @@ func TestWait(t *testing.T) {
_, err = act.Wait(util.Uint256{}, util.Uint256{}, 0, someErr)
require.ErrorIs(t, err, someErr)

_, err = act.WaitSuccess(util.Uint256{}, util.Uint256{}, 0, someErr)
require.ErrorIs(t, err, someErr)

cont := util.Uint256{1, 2, 3}
ex := state.Execution{
Trigger: trigger.Application,
Expand All @@ -584,4 +587,22 @@ func TestWait(t *testing.T) {
Container: cont,
Execution: ex,
}, res)

// Not successful since result has a different hash.
_, err = act.WaitSuccess(util.Uint256{}, util.Uint256{}, 0, nil)
require.Error(t, err)
_, err = act.WaitSuccess(util.Uint256{}, util.Uint256{1, 2, 3}, 0, nil)
require.Error(t, err)

rc.applog.Executions[0].VMState = vmstate.Fault
_, err = act.WaitSuccess(util.Uint256{1, 2, 3}, util.Uint256{}, 0, nil)
require.Error(t, err)

rc.applog.Executions[0].VMState = vmstate.Halt
res, err = act.WaitSuccess(util.Uint256{1, 2, 3}, util.Uint256{}, 0, nil)
require.NoError(t, err)
require.Equal(t, &state.AppExecResult{
Container: cont,
Execution: ex,
}, res)
}

0 comments on commit b713e70

Please sign in to comment.