Merge pull request #1476 from nspcc-dev/fix/verify

core: allow to invoke `verify` of native contracts
This commit is contained in:
Roman Khimov 2020-10-14 12:49:01 +03:00 committed by GitHub
commit 99e0e346c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 3 deletions

View file

@ -29,6 +29,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -1478,20 +1480,25 @@ func (bc *Blockchain) GetTestVM(tx *transaction.Transaction) *vm.VM {
// Various witness verification errors. // Various witness verification errors.
var ( var (
ErrWitnessHashMismatch = errors.New("witness hash mismatch") ErrWitnessHashMismatch = errors.New("witness hash mismatch")
ErrNativeContractWitness = errors.New("native contract witness must have empty verification script")
ErrVerificationFailed = errors.New("signature check failed") ErrVerificationFailed = errors.New("signature check failed")
ErrUnknownVerificationContract = errors.New("unknown verification contract") ErrUnknownVerificationContract = errors.New("unknown verification contract")
ErrInvalidVerificationContract = errors.New("verification contract is missing `verify` method") ErrInvalidVerificationContract = errors.New("verification contract is missing `verify` method")
) )
// initVerificationVM initializes VM for witness check. // 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 offset int
var isNative bool
var initMD *manifest.Method var initMD *manifest.Method
verification := witness.VerificationScript verification := witness.VerificationScript
if len(verification) != 0 { if len(verification) != 0 {
if witness.ScriptHash() != hash { if witness.ScriptHash() != hash {
return ErrWitnessHashMismatch return ErrWitnessHashMismatch
} }
if bc.contracts.ByHash(hash) != nil {
return ErrNativeContractWitness
}
} else { } else {
cs, err := ic.DAO.GetContractState(hash) cs, err := ic.DAO.GetContractState(hash)
if err != nil { if err != nil {
@ -1504,12 +1511,21 @@ func initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transac
verification = cs.Script verification = cs.Script
offset = md.Offset offset = md.Offset
initMD = cs.Manifest.ABI.GetMethod(manifest.MethodInit) initMD = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
isNative = cs.ID < 0
} }
v := ic.VM v := ic.VM
v.LoadScriptWithFlags(verification, smartcontract.NoneFlag) v.LoadScriptWithFlags(verification, smartcontract.NoneFlag)
v.Jump(v.Context(), offset) 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.Call(v.Context(), initMD.Offset)
} }
v.LoadScript(witness.InvocationScript) v.LoadScript(witness.InvocationScript)
@ -1534,7 +1550,7 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
vm := interopCtx.SpawnVM() vm := interopCtx.SpawnVM()
vm.SetPriceGetter(getPrice) vm.SetPriceGetter(getPrice)
vm.GasLimit = gas vm.GasLimit = gas
if err := initVerificationVM(interopCtx, hash, witness); err != nil { if err := bc.initVerificationVM(interopCtx, hash, witness); err != nil {
return 0, err return 0, err
} }
err := vm.Run() err := vm.Run()

View file

@ -510,6 +510,23 @@ func TestVerifyTx(t *testing.T) {
tx := getOracleTx(t) tx := getOracleTx(t)
require.NoError(t, oracleAcc.SignTx(tx)) require.NoError(t, oracleAcc.SignTx(tx))
require.NoError(t, bc.VerifyTx(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) { t.Run("InvalidRequestID", func(t *testing.T) {
tx := getOracleTx(t) tx := getOracleTx(t)