rpc: refactor calculatenetworkfee
handler
Use (Blockchainer).VerifyWitness() to calculate network fee for contract-based witnesses.
This commit is contained in:
parent
807fa4a720
commit
2d196b3f35
12 changed files with 31 additions and 52 deletions
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue