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:
parent
252e03bc34
commit
bcc5c055de
2 changed files with 74 additions and 2 deletions
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue