core: check all transaction scripts

Refs. #1699.
This commit is contained in:
Roman Khimov 2021-02-09 22:28:36 +03:00
parent 158f0d9d9c
commit dd05cae506
2 changed files with 56 additions and 2 deletions

View file

@ -1380,6 +1380,7 @@ var (
ErrTxSmallNetworkFee = errors.New("too small network fee") ErrTxSmallNetworkFee = errors.New("too small network fee")
ErrTxTooBig = errors.New("too big transaction") ErrTxTooBig = errors.New("too big transaction")
ErrMemPoolConflict = errors.New("invalid transaction due to conflicts with the memory pool") ErrMemPoolConflict = errors.New("invalid transaction due to conflicts with the memory pool")
ErrInvalidScript = errors.New("invalid script")
ErrTxInvalidWitnessNum = errors.New("number of signers doesn't match witnesses") ErrTxInvalidWitnessNum = errors.New("number of signers doesn't match witnesses")
ErrInvalidAttribute = errors.New("invalid attribute") ErrInvalidAttribute = errors.New("invalid attribute")
) )
@ -1387,6 +1388,13 @@ var (
// verifyAndPoolTx verifies whether a transaction is bonafide or not and tries // verifyAndPoolTx verifies whether a transaction is bonafide or not and tries
// to add it to the mempool given. // to add it to the mempool given.
func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.Pool, feer mempool.Feer, data ...interface{}) error { func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.Pool, feer mempool.Feer, data ...interface{}) error {
// This code can technically be moved out of here, because it doesn't
// really require a chain lock.
err := vm.IsScriptCorrect(t.Script, nil)
if err != nil {
return fmt.Errorf("%w: %v", ErrInvalidScript, err)
}
height := bc.BlockHeight() height := bc.BlockHeight()
isPartialTx := data != nil isPartialTx := data != nil
if t.ValidUntilBlock <= height || !isPartialTx && t.ValidUntilBlock > height+transaction.MaxValidUntilBlockIncrement { if t.ValidUntilBlock <= height || !isPartialTx && t.ValidUntilBlock > height+transaction.MaxValidUntilBlockIncrement {
@ -1424,7 +1432,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
return err return err
} }
} }
err := bc.verifyTxWitnesses(t, nil, isPartialTx) err = bc.verifyTxWitnesses(t, nil, isPartialTx)
if err != nil { if err != nil {
return err return err
} }
@ -1728,7 +1736,9 @@ var (
ErrWitnessHashMismatch = errors.New("witness hash mismatch") ErrWitnessHashMismatch = errors.New("witness hash mismatch")
ErrNativeContractWitness = errors.New("native contract witness must have empty verification script") ErrNativeContractWitness = errors.New("native contract witness must have empty verification script")
ErrVerificationFailed = errors.New("signature check failed") ErrVerificationFailed = errors.New("signature check failed")
ErrInvalidInvocation = errors.New("invalid invocation script")
ErrInvalidSignature = fmt.Errorf("%w: invalid signature", ErrVerificationFailed) ErrInvalidSignature = fmt.Errorf("%w: invalid signature", ErrVerificationFailed)
ErrInvalidVerification = errors.New("invalid verification script")
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")
) )
@ -1744,6 +1754,10 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
if bc.contracts.ByHash(hash) != nil { if bc.contracts.ByHash(hash) != nil {
return ErrNativeContractWitness return ErrNativeContractWitness
} }
err := vm.IsScriptCorrect(witness.VerificationScript, nil)
if err != nil {
return fmt.Errorf("%w: %v", ErrInvalidVerification, err)
}
v.LoadScriptWithFlags(witness.VerificationScript, callflag.ReadStates) v.LoadScriptWithFlags(witness.VerificationScript, callflag.ReadStates)
} else { } else {
cs, err := ic.GetContract(hash) cs, err := ic.GetContract(hash)
@ -1765,6 +1779,10 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
} }
} }
if len(witness.InvocationScript) != 0 { if len(witness.InvocationScript) != 0 {
err := vm.IsScriptCorrect(witness.InvocationScript, nil)
if err != nil {
return fmt.Errorf("%w: %v", ErrInvalidInvocation, err)
}
v.LoadScript(witness.InvocationScript) v.LoadScript(witness.InvocationScript)
if isNative { if isNative {
if err := v.StepOut(); err != nil { if err := v.StepOut(); err != nil {

View file

@ -390,6 +390,42 @@ func TestVerifyTx(t *testing.T) {
require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed) require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed)
}) })
}) })
t.Run("InvalidTxScript", func(t *testing.T) {
tx := bc.newTestTx(h, testScript)
tx.Script = append(tx.Script, 0xff)
require.NoError(t, accs[0].SignTx(tx))
checkErr(t, ErrInvalidScript, tx)
})
t.Run("InvalidVerificationScript", func(t *testing.T) {
tx := bc.newTestTx(h, testScript)
verif := []byte{byte(opcode.JMP), 3, 0xff, byte(opcode.PUSHT)}
tx.Signers = append(tx.Signers, transaction.Signer{
Account: hash.Hash160(verif),
Scopes: transaction.Global,
})
tx.NetworkFee += 1000000
require.NoError(t, accs[0].SignTx(tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{
InvocationScript: []byte{},
VerificationScript: verif,
})
checkErr(t, ErrInvalidVerification, tx)
})
t.Run("InvalidInvocationScript", func(t *testing.T) {
tx := bc.newTestTx(h, testScript)
verif := []byte{byte(opcode.PUSHT)}
tx.Signers = append(tx.Signers, transaction.Signer{
Account: hash.Hash160(verif),
Scopes: transaction.Global,
})
tx.NetworkFee += 1000000
require.NoError(t, accs[0].SignTx(tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{
InvocationScript: []byte{byte(opcode.JMP), 3, 0xff},
VerificationScript: verif,
})
checkErr(t, ErrInvalidInvocation, tx)
})
t.Run("Conflict", func(t *testing.T) { t.Run("Conflict", func(t *testing.T) {
balance := bc.GetUtilityTokenBalance(h).Int64() balance := bc.GetUtilityTokenBalance(h).Int64()
tx := bc.newTestTx(h, testScript) tx := bc.newTestTx(h, testScript)
@ -583,7 +619,7 @@ func TestVerifyTx(t *testing.T) {
}) })
t.Run("InvalidScript", func(t *testing.T) { t.Run("InvalidScript", func(t *testing.T) {
tx := getOracleTx(t) tx := getOracleTx(t)
tx.Script[0] = ^tx.Script[0] tx.Script = append(tx.Script, byte(opcode.NOP))
require.NoError(t, oracleAcc.SignTx(tx)) require.NoError(t, oracleAcc.SignTx(tx))
checkErr(t, ErrInvalidAttribute, tx) checkErr(t, ErrInvalidAttribute, tx)
}) })