Merge pull request #3141 from nspcc-dev/limit-calculatenetworkfee-gas

Limit calculatenetworkfee GAS
This commit is contained in:
Roman Khimov 2023-09-28 09:13:27 +03:00 committed by GitHub
commit 4598f3d3c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 3 deletions

View file

@ -233,7 +233,9 @@ where:
proxy can be used to have proper app-specific CORS settings), but it's an
easy way to make RPC interface accessible from the browser.
- `MaxGasInvoke` is the maximum GAS allowed to spend during `invokefunction` and
`invokescript` RPC-calls.
`invokescript` RPC-calls. `calculatenetworkfee` also can't exceed this GAS amount
(normally the limit for it is MaxVerificationGAS from Policy, but if MaxGasInvoke
is lower than that then this limit is respected).
- `MaxIteratorResultItems` - maximum number of elements extracted from iterator
returned by `invoke*` call. When the `MaxIteratorResultItems` value is set to
`n`, only `n` iterations are returned and truncated is true, indicating that

View file

@ -917,7 +917,15 @@ func (s *Server) calculateNetworkFee(reqParams params.Params) (any, *neorpc.Erro
return 0, neorpc.WrapErrorWithData(neorpc.ErrInvalidParams, fmt.Sprintf("failed to compute tx size: %s", err))
}
size := len(hashablePart) + io.GetVarSize(len(tx.Signers))
var netFee int64
var (
netFee int64
// Verification GAS cost can't exceed this policy.
gasLimit = s.chain.GetMaxVerificationGAS()
)
if gasLimit > int64(s.config.MaxGasInvoke) {
// But we honor instance configuration as well.
gasLimit = int64(s.config.MaxGasInvoke)
}
for i, signer := range tx.Signers {
w := tx.Scripts[i]
if len(w.InvocationScript) == 0 { // No invocation provided, try to infer one.
@ -951,7 +959,11 @@ func (s *Server) calculateNetworkFee(reqParams params.Params) (any, *neorpc.Erro
}
w.InvocationScript = inv.Bytes()
}
gasConsumed, _ := s.chain.VerifyWitness(signer.Account, tx, &w, int64(s.config.MaxGasInvoke))
gasConsumed, err := s.chain.VerifyWitness(signer.Account, tx, &w, gasLimit)
if err != nil && !errors.Is(err, core.ErrInvalidSignature) {
return nil, neorpc.WrapErrorWithData(neorpc.ErrInvalidSignature, err.Error())
}
gasLimit -= gasConsumed
netFee += gasConsumed
size += io.GetVarSize(w.VerificationScript) + io.GetVarSize(w.InvocationScript)
}

View file

@ -3,6 +3,7 @@ package rpcsrv
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
gio "io"
@ -26,6 +27,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
@ -3184,6 +3186,21 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
body := calcReq(t, tx)
_ = checkErrGetResult(t, body, true, neorpc.ErrInvalidVerificationFunctionCode, "signer 0 has no verify method in deployed contract")
})
t.Run("execution limit, fail", func(t *testing.T) {
// 1_6000_0000 GAS with the default 1.5 allowed by Policy
verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x58, 0x89, 0x09, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)}
binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas)))
tx := &transaction.Transaction{
Script: []byte{byte(opcode.RET)},
Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}},
Scripts: []transaction.Witness{{
InvocationScript: []byte{byte(opcode.NOP)},
VerificationScript: verifScript,
}},
}
body := calcReq(t, tx)
_ = checkErrGetResult(t, body, true, neorpc.ErrInvalidSignatureCode, "GAS limit exceeded")
})
checkCalc := func(t *testing.T, tx *transaction.Transaction, fee int64) {
resp := checkErrGetResult(t, calcReq(t, tx), false, 0)
res := new(result.NetworkFee)
@ -3258,6 +3275,20 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
invocScript := invocWriter.Bytes()
checkContract(t, verAcc, invocScript, 146960) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side.
})
t.Run("execution limit, ok", func(t *testing.T) {
// 1_4000_0000 GAS with the default 1.5 allowed by Policy
verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x3b, 0x58, 0x08, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)}
binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas)))
tx := &transaction.Transaction{
Script: []byte{byte(opcode.RET)},
Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}},
Scripts: []transaction.Witness{{
InvocationScript: []byte{byte(opcode.NOP)},
VerificationScript: verifScript,
}},
}
checkCalc(t, tx, 140065570)
})
})
t.Run("sendrawtransaction", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["%s"]}`