rpc: allow to calculatenetworkfee for verify with args

We can load invocation script for those contract-based witnesses which
requires arguments for `verify` methods.
This commit is contained in:
Anna Shaleva 2021-03-26 18:52:51 +03:00
parent 252e03bc34
commit bcc5c055de
2 changed files with 74 additions and 2 deletions

View file

@ -18,6 +18,7 @@ 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/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -314,6 +315,76 @@ func TestAddNetworkFeeCalculateNetworkFee(t *testing.T) {
}) })
} }
func TestCalculateNetworkFee(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close()
defer rpcSrv.Shutdown()
const extraFee = 10
c, err := client.New(context.Background(), httpSrv.URL, client.Options{})
require.NoError(t, err)
require.NoError(t, c.Init())
t.Run("ContractWithArgs", func(t *testing.T) {
check := func(t *testing.T, extraFee int64) {
h, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
require.NoError(t, err)
priv := testchain.PrivateKeyByID(0)
acc0 := wallet.NewAccountFromPrivateKey(priv)
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
require.NoError(t, err)
tx.ValidUntilBlock = chain.BlockHeight() + 10
tx.Signers = []transaction.Signer{
{
Account: acc0.PrivateKey().GetScriptHash(),
Scopes: transaction.CalledByEntry,
},
{
Account: h,
Scopes: transaction.Global,
},
}
bw := io.NewBufBinWriter()
emit.Bool(bw.BinWriter, false)
emit.Int(bw.BinWriter, int64(4))
emit.String(bw.BinWriter, "good_string") // contract's `verify` return `true` with this string
require.NoError(t, bw.Err)
contractInv := bw.Bytes()
// we need to fill standard verification scripts to use CalculateNetworkFee.
tx.Scripts = []transaction.Witness{
{VerificationScript: acc0.GetVerificationScript()},
{InvocationScript: contractInv},
}
tx.NetworkFee, err = c.CalculateNetworkFee(tx)
require.NoError(t, err)
tx.NetworkFee += extraFee
tx.Scripts = nil
require.NoError(t, acc0.SignTx(testchain.Network(), tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{InvocationScript: contractInv})
err = chain.VerifyTx(tx)
if extraFee < 0 {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
t.Run("with extra fee", func(t *testing.T) {
// check that calculated network fee with extra value is enough
check(t, extraFee)
})
t.Run("without extra fee", func(t *testing.T) {
// check that calculated network fee without extra value is enough
check(t, 0)
})
t.Run("exactFee-1", func(t *testing.T) {
// check that we don't add unexpected extra GAS
check(t, -1)
})
})
}
func TestSignAndPushInvocationTx(t *testing.T) { func TestSignAndPushInvocationTx(t *testing.T) {
chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
defer chain.Close() defer chain.Close()

View file

@ -584,7 +584,7 @@ func (s *Server) calculateNetworkFee(reqParams request.Params) (interface{}, *re
} }
if verificationScript == nil { // then it still might be a contract-based verification if verificationScript == nil { // then it still might be a contract-based verification
verificationErr := fmt.Sprintf("contract verification for signer #%d failed", i) verificationErr := fmt.Sprintf("contract verification for signer #%d failed", i)
res, respErr := s.runScriptInVM(trigger.Verification, []byte{}, signer.Account, tx) res, respErr := s.runScriptInVM(trigger.Verification, tx.Scripts[i].InvocationScript, signer.Account, tx)
if respErr != nil && errors.Is(respErr.Cause, core.ErrUnknownVerificationContract) { if respErr != nil && errors.Is(respErr.Cause, core.ErrUnknownVerificationContract) {
// it's neither a contract-based verification script nor a standard witness attached to // it's neither a contract-based verification script nor a standard witness attached to
// the tx, so the user did not provide enough data to calculate fee for that witness => // the tx, so the user did not provide enough data to calculate fee for that witness =>
@ -612,7 +612,8 @@ func (s *Server) calculateNetworkFee(reqParams request.Params) (interface{}, *re
return 0, response.NewRPCError(verificationErr, cause.Error(), cause) return 0, response.NewRPCError(verificationErr, cause.Error(), cause)
} }
netFee += res.GasConsumed netFee += res.GasConsumed
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty size += io.GetVarSize([]byte{}) + // verification script is empty (contract-based witness)
io.GetVarSize(tx.Scripts[i].InvocationScript) // invocation script might not be empty (args for `verify`)
continue continue
} }