rpc: refactor calculatenetworkfee handler

Use (Blockchainer).VerifyWitness() to calculate network fee for
contract-based witnesses.
This commit is contained in:
AnnaShaleva 2021-10-25 17:42:20 +03:00
parent 807fa4a720
commit 2d196b3f35
12 changed files with 31 additions and 52 deletions

View file

@ -37,7 +37,7 @@ type FakeChain struct {
blocks map[util.Uint256]*block.Block blocks map[util.Uint256]*block.Block
hdrHashes map[uint32]util.Uint256 hdrHashes map[uint32]util.Uint256
txs map[util.Uint256]*transaction.Transaction txs map[util.Uint256]*transaction.Transaction
VerifyWitnessF func() error VerifyWitnessF func() (int64, error)
MaxVerificationGAS int64 MaxVerificationGAS int64
NotaryContractScriptHash util.Uint160 NotaryContractScriptHash util.Uint160
NotaryDepositExpiration uint32 NotaryDepositExpiration uint32
@ -430,7 +430,7 @@ func (chain *FakeChain) VerifyTx(*transaction.Transaction) error {
} }
// VerifyWitness implements Blockchainer interface. // VerifyWitness implements Blockchainer interface.
func (chain *FakeChain) VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) error { func (chain *FakeChain) VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error) {
if chain.VerifyWitnessF != nil { if chain.VerifyWitnessF != nil {
return chain.VerifyWitnessF() return chain.VerifyWitnessF()
} }

View file

@ -262,9 +262,11 @@ func TestPayload_Sign(t *testing.T) {
p := randomPayload(t, prepareRequestType) p := randomPayload(t, prepareRequestType)
h := priv.PublicKey().GetScriptHash() h := priv.PublicKey().GetScriptHash()
bc := newTestChain(t, false) bc := newTestChain(t, false)
require.Error(t, bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit)) _, err = bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit)
require.Error(t, err)
require.NoError(t, p.Sign(priv)) require.NoError(t, p.Sign(priv))
require.NoError(t, bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit)) _, err = bc.VerifyWitness(h, p, &p.Witness, payloadGasLimit)
require.NoError(t, err)
} }
func TestMessageType_String(t *testing.T) { func TestMessageType_String(t *testing.T) {

View file

@ -19,6 +19,6 @@ func BenchmarkVerifyWitness(t *testing.B) {
t.ResetTimer() t.ResetTimer()
for n := 0; n < t.N; n++ { for n := 0; n < t.N; n++ {
_ = bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000) _, _ = bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
} }
} }

View file

@ -2116,12 +2116,12 @@ func (bc *Blockchain) InitVerificationVM(v *vm.VM, getContract func(util.Uint160
return nil return nil
} }
// VerifyWitness checks that w is a correct witness for c signed by h. // VerifyWitness checks that w is a correct witness for c signed by h. It returns
func (bc *Blockchain) VerifyWitness(h util.Uint160, c hash.Hashable, w *transaction.Witness, gas int64) error { // the amount of GAS consumed during verification and an error.
func (bc *Blockchain) VerifyWitness(h util.Uint160, c hash.Hashable, w *transaction.Witness, gas int64) (int64, error) {
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil) ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
ic.Container = c ic.Container = c
_, err := bc.verifyHashAgainstScript(h, w, ic, gas) return bc.verifyHashAgainstScript(h, w, ic, gas)
return err
} }
// verifyHashAgainstScript verifies given hash against the given witness and returns the amount of GAS consumed. // verifyHashAgainstScript verifies given hash against the given witness and returns the amount of GAS consumed.
@ -2197,7 +2197,8 @@ func (bc *Blockchain) verifyHeaderWitnesses(currHeader, prevHeader *block.Header
} else { } else {
hash = prevHeader.NextConsensus hash = prevHeader.NextConsensus
} }
return bc.VerifyWitness(hash, currHeader, &currHeader.Script, HeaderVerificationGasLimit) _, err := bc.VerifyWitness(hash, currHeader, &currHeader.Script, HeaderVerificationGasLimit)
return err
} }
// GoverningTokenHash returns the governing token (NEO) native contract hash. // GoverningTokenHash returns the governing token (NEO) native contract hash.

View file

@ -73,7 +73,7 @@ type Blockchainer interface {
SubscribeForNotifications(ch chan<- *subscriptions.NotificationEvent) SubscribeForNotifications(ch chan<- *subscriptions.NotificationEvent)
SubscribeForTransactions(ch chan<- *transaction.Transaction) SubscribeForTransactions(ch chan<- *transaction.Transaction)
VerifyTx(*transaction.Transaction) error VerifyTx(*transaction.Transaction) error
VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) error VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error)
GetMemPool() *mempool.Pool GetMemPool() *mempool.Pool
UnsubscribeFromBlocks(ch chan<- *block.Block) UnsubscribeFromBlocks(ch chan<- *block.Block)
UnsubscribeFromExecutions(ch chan<- *state.AppExecResult) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)

View file

@ -238,5 +238,6 @@ func (s *Module) verifyWitness(r *state.MPTRoot) error {
s.mtx.Lock() s.mtx.Lock()
h := s.getKeyCacheForHeight(r.Index).validatorsHash h := s.getKeyCacheForHeight(r.Index).validatorsHash
s.mtx.Unlock() s.mtx.Unlock()
return s.bc.VerifyWitness(h, r, &r.Witness[0], maxVerificationGAS) _, err := s.bc.VerifyWitness(h, r, &r.Witness[0], maxVerificationGAS)
return err
} }

View file

@ -69,7 +69,7 @@ func (p *Pool) Add(e *payload.Extensible) (bool, error) {
} }
func (p *Pool) verify(e *payload.Extensible) (bool, error) { func (p *Pool) verify(e *payload.Extensible) (bool, error) {
if err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil { if _, err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil {
return false, err return false, err
} }
h := p.chain.BlockHeight() h := p.chain.BlockHeight()
@ -118,7 +118,7 @@ func (p *Pool) RemoveStale(index uint32) {
lst.Remove(old) lst.Remove(old)
continue continue
} }
if err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil { if _, err := p.chain.VerifyWitness(e.Sender, e, &e.Witness, extensibleVerifyMaxGAS); err != nil {
delete(p.verified, h) delete(p.verified, h)
lst.Remove(old) lst.Remove(old)
continue continue

View file

@ -134,11 +134,11 @@ func newTestChain() *testChain {
}, },
} }
} }
func (c *testChain) VerifyWitness(u util.Uint160, _ hash.Hashable, _ *transaction.Witness, _ int64) error { func (c *testChain) VerifyWitness(u util.Uint160, _ hash.Hashable, _ *transaction.Witness, _ int64) (int64, error) {
if !c.verifyWitness(u) { if !c.verifyWitness(u) {
return errVerification return 0, errVerification
} }
return nil return 0, nil
} }
func (c *testChain) IsExtensibleAllowed(u util.Uint160) bool { func (c *testChain) IsExtensibleAllowed(u util.Uint160) bool {
return c.isAllowed(u) return c.isAllowed(u)

View file

@ -1048,7 +1048,7 @@ func (s *Server) verifyAndPoolNotaryRequest(r *payload.P2PNotaryRequest) error {
func verifyNotaryRequest(bc blockchainer.Blockchainer, _ *transaction.Transaction, data interface{}) error { func verifyNotaryRequest(bc blockchainer.Blockchainer, _ *transaction.Transaction, data interface{}) error {
r := data.(*payload.P2PNotaryRequest) r := data.(*payload.P2PNotaryRequest)
payer := r.FallbackTransaction.Signers[1].Account payer := r.FallbackTransaction.Signers[1].Account
if err := bc.VerifyWitness(payer, r, &r.Witness, bc.GetPolicer().GetMaxVerificationGAS()); err != nil { if _, err := bc.VerifyWitness(payer, r, &r.Witness, bc.GetPolicer().GetMaxVerificationGAS()); err != nil {
return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err) return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err)
} }
notaryHash := bc.GetNotaryContractScriptHash() notaryHash := bc.GetNotaryContractScriptHash()

View file

@ -432,11 +432,11 @@ func TestConsensus(t *testing.T) {
return NewMessage(CMDExtensible, pl) return NewMessage(CMDExtensible, pl)
} }
s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() error { return errors.New("invalid") } s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, errors.New("invalid") }
msg := newConsensusMessage(0, s.chain.BlockHeight()+1) msg := newConsensusMessage(0, s.chain.BlockHeight()+1)
require.Error(t, s.handleMessage(p, msg)) require.Error(t, s.handleMessage(p, msg))
s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() error { return nil } s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, nil }
require.NoError(t, s.handleMessage(p, msg)) require.NoError(t, s.handleMessage(p, msg))
require.Contains(t, s.consensus.(*fakeConsensus).payloads, msg.Payload.(*payload.Extensible)) require.Contains(t, s.consensus.(*fakeConsensus).payloads, msg.Payload.(*payload.Extensible))
@ -726,7 +726,7 @@ func TestInv(t *testing.T) {
}) })
t.Run("extensible", func(t *testing.T) { t.Run("extensible", func(t *testing.T) {
ep := payload.NewExtensible() ep := payload.NewExtensible()
s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() error { return nil } s.chain.(*fakechain.FakeChain).VerifyWitnessF = func() (int64, error) { return 0, nil }
ep.ValidBlockEnd = s.chain.(*fakechain.FakeChain).BlockHeight() + 1 ep.ValidBlockEnd = s.chain.(*fakechain.FakeChain).BlockHeight() + 1
ok, err := s.extensiblePool.Add(ep) ok, err := s.extensiblePool.Add(ep)
require.NoError(t, err) require.NoError(t, err)
@ -1033,12 +1033,12 @@ func TestVerifyNotaryRequest(t *testing.T) {
} }
t.Run("bad payload witness", func(t *testing.T) { t.Run("bad payload witness", func(t *testing.T) {
bc.VerifyWitnessF = func() error { return errors.New("bad witness") } bc.VerifyWitnessF = func() (int64, error) { return 0, errors.New("bad witness") }
require.Error(t, verifyNotaryRequest(bc, nil, newNotaryRequest())) require.Error(t, verifyNotaryRequest(bc, nil, newNotaryRequest()))
}) })
t.Run("bad fallback sender", func(t *testing.T) { t.Run("bad fallback sender", func(t *testing.T) {
bc.VerifyWitnessF = func() error { return nil } bc.VerifyWitnessF = func() (int64, error) { return 0, nil }
r := newNotaryRequest() r := newNotaryRequest()
r.FallbackTransaction.Signers[0] = transaction.Signer{Account: util.Uint160{7, 8, 9}} r.FallbackTransaction.Signers[0] = transaction.Signer{Account: util.Uint160{7, 8, 9}}
require.Error(t, verifyNotaryRequest(bc, nil, r)) require.Error(t, verifyNotaryRequest(bc, nil, r))

View file

@ -613,36 +613,11 @@ 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) gasConsumed, err := s.chain.VerifyWitness(signer.Account, tx, &tx.Scripts[i], int64(s.config.MaxGasInvoke))
res, respErr := s.runScriptInVM(trigger.Verification, tx.Scripts[i].InvocationScript, signer.Account, tx)
if respErr != nil && errors.Is(respErr.Cause, core.ErrUnknownVerificationContract) {
// 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 =>
// it's a user error
return 0, response.NewRPCError(verificationErr, respErr.Cause.Error(), respErr.Cause)
}
if respErr != nil {
return 0, respErr
}
res.Finalize()
if res.State != "HALT" {
cause := fmt.Errorf("invalid VM state %s due to an error: %s", res.State, res.FaultException)
return 0, response.NewRPCError(verificationErr, cause.Error(), cause)
}
if l := len(res.Stack); l != 1 {
cause := fmt.Errorf("result stack length should be equal to 1, got %d", l)
return 0, response.NewRPCError(verificationErr, cause.Error(), cause)
}
isOK, err := res.Stack[0].TryBool()
if err != nil { if err != nil {
cause := fmt.Errorf("resulting stackitem cannot be converted to Boolean: %w", err) return 0, response.NewRPCError(fmt.Sprintf("contract verification for signer #%d failed", i), err.Error(), err)
return 0, response.NewRPCError(verificationErr, cause.Error(), cause)
} }
if !isOK { netFee += gasConsumed
cause := errors.New("`verify` method returned `false` on stack")
return 0, response.NewRPCError(verificationErr, cause.Error(), cause)
}
netFee += res.GasConsumed
size += io.GetVarSize([]byte{}) + // verification script is empty (contract-based witness) 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`) io.GetVarSize(tx.Scripts[i].InvocationScript) // invocation script might not be empty (args for `verify`)
continue continue

View file

@ -227,7 +227,7 @@ func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
switch r.witnessInfo[i].typ { switch r.witnessInfo[i].typ {
case Contract: case Contract:
// Need to check even if r.main.Scripts[i].InvocationScript is already filled in. // Need to check even if r.main.Scripts[i].InvocationScript is already filled in.
err := n.Config.Chain.VerifyWitness(r.main.Signers[i].Account, r.main, &w, n.Config.Chain.GetPolicer().GetMaxVerificationGAS()) _, err := n.Config.Chain.VerifyWitness(r.main.Signers[i].Account, r.main, &w, n.Config.Chain.GetPolicer().GetMaxVerificationGAS())
if err != nil { if err != nil {
continue continue
} }