From aee02ff2e3af2536c704c8de71b359d6677af9ec Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 9 Oct 2020 13:32:54 +0300 Subject: [PATCH] core: allow to invoke `verify` of native contracts --- pkg/core/blockchain.go | 22 +++++++++++++++++++--- pkg/core/blockchain_test.go | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 84ddf03fa..34f9fb453 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -29,6 +29,8 @@ import ( "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" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "go.uber.org/zap" ) @@ -1478,20 +1480,25 @@ func (bc *Blockchain) GetTestVM(tx *transaction.Transaction) *vm.VM { // Various witness verification errors. var ( ErrWitnessHashMismatch = errors.New("witness hash mismatch") + ErrNativeContractWitness = errors.New("native contract witness must have empty verification script") ErrVerificationFailed = errors.New("signature check failed") ErrUnknownVerificationContract = errors.New("unknown verification contract") ErrInvalidVerificationContract = errors.New("verification contract is missing `verify` method") ) // initVerificationVM initializes VM for witness check. -func initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error { +func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error { var offset int + var isNative bool var initMD *manifest.Method verification := witness.VerificationScript if len(verification) != 0 { if witness.ScriptHash() != hash { return ErrWitnessHashMismatch } + if bc.contracts.ByHash(hash) != nil { + return ErrNativeContractWitness + } } else { cs, err := ic.DAO.GetContractState(hash) if err != nil { @@ -1504,12 +1511,21 @@ func initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transac verification = cs.Script offset = md.Offset initMD = cs.Manifest.ABI.GetMethod(manifest.MethodInit) + isNative = cs.ID < 0 } v := ic.VM v.LoadScriptWithFlags(verification, smartcontract.NoneFlag) v.Jump(v.Context(), offset) - if initMD != nil { + if isNative { + w := io.NewBufBinWriter() + emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK) + emit.String(w.BinWriter, manifest.MethodVerify) + if w.Err != nil { + return w.Err + } + v.LoadScript(w.Bytes()) + } else if initMD != nil { v.Call(v.Context(), initMD.Offset) } v.LoadScript(witness.InvocationScript) @@ -1534,7 +1550,7 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa vm := interopCtx.SpawnVM() vm.SetPriceGetter(getPrice) vm.GasLimit = gas - if err := initVerificationVM(interopCtx, hash, witness); err != nil { + if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil { return 0, err } err := vm.Run() diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 09b22018f..ba7c7797e 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -510,6 +510,23 @@ func TestVerifyTx(t *testing.T) { tx := getOracleTx(t) require.NoError(t, oracleAcc.SignTx(tx)) require.NoError(t, bc.VerifyTx(tx)) + + t.Run("NativeVerify", func(t *testing.T) { + tx.Signers = append(tx.Signers, transaction.Signer{ + Account: bc.contracts.Oracle.Hash, + Scopes: transaction.None, + }) + tx.Scripts = append(tx.Scripts, transaction.Witness{}) + t.Run("NonZeroVerification", func(t *testing.T) { + tx.Scripts[len(tx.Scripts)-1].VerificationScript = bc.contracts.Oracle.Script + err := bc.VerifyTx(tx) + require.True(t, errors.Is(err, ErrNativeContractWitness), "got: %v", err) + }) + t.Run("Good", func(t *testing.T) { + tx.Scripts[len(tx.Scripts)-1].VerificationScript = nil + require.NoError(t, bc.VerifyTx(tx)) + }) + }) }) t.Run("InvalidRequestID", func(t *testing.T) { tx := getOracleTx(t)