From b431e47d2a38c3e8442b373198f9f6a62eae390c Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 4 Apr 2022 13:22:20 +0300 Subject: [PATCH] core: add GetTransactionVMState to native Ledger contract Close #2343. --- pkg/core/native/ledger.go | 19 +++++++++++++ pkg/core/native/native_test/ledger_test.go | 31 ++++++++++++++++++++-- pkg/interop/native/ledger/ledger.go | 20 ++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go index d82342373..e00923a71 100644 --- a/pkg/core/native/ledger.go +++ b/pkg/core/native/ledger.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -62,6 +63,11 @@ func newLedger() *Ledger { md = newMethodAndPrice(l.getTransactionFromBlock, 1<<16, callflag.ReadStates) l.AddMethod(md, desc) + desc = newDescriptor("getTransactionVMState", smartcontract.IntegerType, + manifest.NewParameter("hash", smartcontract.Hash256Type)) + md = newMethodAndPrice(l.getTransactionVMState, 1<<15, callflag.ReadStates) + l.AddMethod(md, desc) + return l } @@ -142,6 +148,19 @@ func (l *Ledger) getTransactionFromBlock(ic *interop.Context, params []stackitem return TransactionToStackItem(block.Transactions[index]) } +// getTransactionVMState returns VM state got after transaction invocation. +func (l *Ledger) getTransactionVMState(ic *interop.Context, params []stackitem.Item) stackitem.Item { + hash, err := getUint256FromItem(params[0]) + if err != nil { + panic(err) + } + h, _, aer, err := ic.DAO.GetTxExecResult(hash) + if err != nil || !isTraceableBlock(ic.Chain, h) { + return stackitem.Make(vm.NoneState) + } + return stackitem.Make(aer.VMState) +} + // isTraceableBlock defines whether we're able to give information about // the block with index specified. func isTraceableBlock(bc interop.Ledger, index uint32) bool { diff --git a/pkg/core/native/native_test/ledger_test.go b/pkg/core/native/native_test/ledger_test.go index ab71dbee0..9306f3008 100644 --- a/pkg/core/native/native_test/ledger_test.go +++ b/pkg/core/native/native_test/ledger_test.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/config" - "github.com/nspcc-dev/neo-go/pkg/neotest/chain" - "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/neotest/chain" "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/stackitem" "github.com/stretchr/testify/require" @@ -44,6 +44,33 @@ func TestLedger_GetTransactionHeight(t *testing.T) { }) } +func TestLedger_GetTransactionState(t *testing.T) { + c := newLedgerClient(t) + e := c.Executor + ledgerInvoker := c.WithSigners(c.Committee) + + hash := e.InvokeScript(t, []byte{byte(opcode.RET)}, []neotest.Signer{c.Committee}) + + t.Run("unknown transaction", func(t *testing.T) { + ledgerInvoker.Invoke(t, vm.NoneState, "getTransactionVMState", util.Uint256{1, 2, 3}) + }) + t.Run("not a hash", func(t *testing.T) { + ledgerInvoker.InvokeFail(t, "expected []byte of size 32", "getTransactionVMState", []byte{1, 2, 3}) + }) + t.Run("good: HALT", func(t *testing.T) { + ledgerInvoker.Invoke(t, vm.HaltState, "getTransactionVMState", hash) + }) + t.Run("isn't traceable", func(t *testing.T) { + // Add more blocks so that tx becomes untraceable. + e.GenerateNewBlocks(t, int(e.Chain.GetConfig().MaxTraceableBlocks)) + ledgerInvoker.Invoke(t, vm.NoneState, "getTransactionVMState", hash) + }) + t.Run("good: FAULT", func(t *testing.T) { + faultedH := e.InvokeScript(t, []byte{byte(opcode.ABORT)}, []neotest.Signer{c.Committee}) + ledgerInvoker.Invoke(t, vm.FaultState, "getTransactionVMState", faultedH) + }) +} + func TestLedger_GetTransaction(t *testing.T) { c := newLedgerClient(t) e := c.Executor diff --git a/pkg/interop/native/ledger/ledger.go b/pkg/interop/native/ledger/ledger.go index 8de5deb4b..4b3c0ef2e 100644 --- a/pkg/interop/native/ledger/ledger.go +++ b/pkg/interop/native/ledger/ledger.go @@ -13,6 +13,21 @@ import ( // Hash represents Ledger contract hash. const Hash = "\xbe\xf2\x04\x31\x40\x36\x2a\x77\xc1\x50\x99\xc7\xe6\x4c\x12\xf7\x00\xb6\x65\xda" +// VMState represents VM execution state. +type VMState uint8 + +// Various VM execution states. +const ( + // NoneState represents NONE VM state. + NoneState VMState = 0 + // HaltState represents HALT VM state. + HaltState VMState = 1 + // FaultState represents FAULT VM state. + FaultState VMState = 2 + // BreakState represents BREAK VM state. + BreakState VMState = 4 +) + // CurrentHash represents `currentHash` method of Ledger native contract. func CurrentHash() interop.Hash256 { return neogointernal.CallWithToken(Hash, "currentHash", int(contract.ReadStates)).(interop.Hash256) @@ -43,3 +58,8 @@ func GetTransactionFromBlock(indexOrHash interface{}, txIndex int) *Transaction return neogointernal.CallWithToken(Hash, "getTransactionFromBlock", int(contract.ReadStates), indexOrHash, txIndex).(*Transaction) } + +// GetTransactionVMState represents `getTransactionVMState` method of Ledger native contract. +func GetTransactionVMState(hash interop.Hash256) VMState { + return neogointernal.CallWithToken(Hash, "getTransactionVMState", int(contract.ReadStates), hash).(VMState) +}