From a0ff5b1bf87b6b76f4ee1ae00034aba0b68f814b Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Tue, 16 Nov 2021 18:51:15 +0300 Subject: [PATCH] [#975] morph/client: Use 4 witnesses in notary request Add invoker witness as the third witness. Required for netmap methods checks. Signed-off-by: Alex Vanin --- pkg/morph/client/notary.go | 46 ++++++++++++++++++++++----- pkg/morph/event/notary_preparator.go | 47 +++++++++++++++++++--------- 2 files changed, 71 insertions(+), 22 deletions(-) diff --git a/pkg/morph/client/notary.go b/pkg/morph/client/notary.go index 663c58540..c07842437 100644 --- a/pkg/morph/client/notary.go +++ b/pkg/morph/client/notary.go @@ -441,7 +441,11 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint _, n := mn(alphabetList, committee) u8n := uint8(n) - cosigners, err := c.notaryCosigners(alphabetList, committee) + if !invokedByAlpha { + u8n++ + } + + cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee) if err != nil { return err } @@ -510,7 +514,7 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint err = c.client.AddNetworkFee( mainTx, notaryFee, - c.notaryAccounts(multiaddrAccount)..., + c.notaryAccounts(invokedByAlpha, multiaddrAccount)..., ) if err != nil { return err @@ -538,8 +542,8 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint return nil } -func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) { - s := make([]transaction.Signer, 0, 3) +func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) { + s := make([]transaction.Signer, 0, 4) // first we have proxy contract signature, as it will pay for the execution s = append(s, transaction.Signer{ @@ -563,6 +567,16 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa AllowedGroups: c.signer.AllowedGroups, }) + if !invokedByAlpha { + // then we have invoker signature + s = append(s, transaction.Signer{ + Account: hash.Hash160(c.acc.GetVerificationScript()), + Scopes: c.signer.Scopes, + AllowedContracts: c.signer.AllowedContracts, + AllowedGroups: c.signer.AllowedGroups, + }) + } + // last one is a placeholder for notary contract signature s = append(s, transaction.Signer{ Account: c.notary.notary, @@ -572,12 +586,12 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa return s, nil } -func (c *Client) notaryAccounts(multiaddr *wallet.Account) []*wallet.Account { +func (c *Client) notaryAccounts(invokedByAlpha bool, multiaddr *wallet.Account) []*wallet.Account { if multiaddr == nil { return nil } - a := make([]*wallet.Account, 0, 3) + a := make([]*wallet.Account, 0, 4) // first we have proxy account, as it will pay for the execution a = append(a, &wallet.Account{ @@ -589,6 +603,11 @@ func (c *Client) notaryAccounts(multiaddr *wallet.Account) []*wallet.Account { // then we have inner ring multiaddress account a = append(a, multiaddr) + if !invokedByAlpha { + // then we have invoker account + a = append(a, c.acc) + } + // last one is a placeholder for notary contract account a = append(a, &wallet.Account{ Contract: &wallet.Contract{}, @@ -602,7 +621,7 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account, return nil } - w := make([]transaction.Witness, 0, 3) + w := make([]transaction.Witness, 0, 4) // first we have empty proxy witness, because notary will execute `Verify` // method on the proxy contract to check witness @@ -639,6 +658,19 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account, VerificationScript: multiaddr.GetVerificationScript(), }) + if !invokedByAlpha { + // then we have invoker witness + invokeScript = append( + []byte{byte(opcode.PUSHDATA1), 64}, + c.acc.PrivateKey().SignHashable(uint32(c.client.GetNetwork()), tx)..., + ) + + w = append(w, transaction.Witness{ + InvocationScript: invokeScript, + VerificationScript: c.acc.GetVerificationScript(), + }) + } + // last one is a placeholder for notary contract witness w = append(w, transaction.Witness{ InvocationScript: append( diff --git a/pkg/morph/event/notary_preparator.go b/pkg/morph/event/notary_preparator.go index f6f532d8f..facb679f1 100644 --- a/pkg/morph/event/notary_preparator.go +++ b/pkg/morph/event/notary_preparator.go @@ -25,6 +25,7 @@ var ( errUnexpectedCosignersAmount = errors.New("received main tx has unexpected amount of cosigners") errIncorrectAlphabetSigner = errors.New("received main tx has incorrect Alphabet signer") errIncorrectProxyWitnesses = errors.New("received main tx has non-empty Proxy witnesses") + errIncorrectInvokerWitnesses = errors.New("received main tx has empty Invoker witness") errIncorrectAlphabet = errors.New("received main tx has incorrect Alphabet verification") errIncorrectNotaryPlaceholder = errors.New("received main tx has incorrect Notary contract placeholder") errIncorrectAttributesAmount = errors.New("received main tx has incorrect attributes amount") @@ -104,12 +105,16 @@ func notaryPreparator(prm PreparatorPrm) NotaryPreparator { // since every notary call is a new notary request in fact. func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) { // notary request's main tx is expected to have - // exactly three witnesses: one for proxy contract, - // one for notary's invoker and one is for notary - // contract - if len(nr.MainTransaction.Scripts) != 3 { + // three or four witnesses: one for proxy contract, + // one for alphabet multisignature, one optional for + // notary's invoker and one is for notary contract + ln := len(nr.MainTransaction.Scripts) + switch ln { + case 3, 4: + default: return nil, errUnexpectedWitnessAmount } + invokerWitness := ln == 4 // alphabet node should handle only notary requests // that have been sent unsigned(by storage nodes) => @@ -126,19 +131,19 @@ func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) { return nil, fmt.Errorf("could not fetch Alphabet public keys: %w", err) } - err = p.validateCosigners(nr.MainTransaction.Signers, currentAlphabet) + err = p.validateCosigners(ln, nr.MainTransaction.Signers, currentAlphabet) if err != nil { return nil, err } // validate main TX's notary attribute - err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet) + err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet, invokerWitness) if err != nil { return nil, err } // validate main TX's witnesses - err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet) + err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet, invokerWitness) if err != nil { return nil, err } @@ -303,8 +308,8 @@ func (p Preparator) validateExpiration(fbTX *transaction.Transaction) error { return nil } -func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.PublicKeys) error { - if len(s) != 3 { +func (p Preparator) validateCosigners(expected int, s []transaction.Signer, alphaKeys keys.PublicKeys) error { + if len(s) != expected { return errUnexpectedCosignersAmount } @@ -320,7 +325,7 @@ func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.Pub return nil } -func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.PublicKeys) error { +func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.PublicKeys, invokerWitness bool) error { // the first one(proxy contract) must have empty // witnesses if len(w[0].VerificationScript)+len(w[0].InvocationScript) != 0 { @@ -338,23 +343,35 @@ func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.Pu return errIncorrectAlphabet } - // the third one must be a placeholder for notary - // contract witness - if !bytes.Equal(w[2].InvocationScript, p.dummyInvocationScript) || len(w[2].VerificationScript) != 0 { + if invokerWitness { + // the optional third one must be an invoker witness + if len(w[2].VerificationScript)+len(w[2].InvocationScript) == 0 { + return errIncorrectInvokerWitnesses + } + } + + // the last one must be a placeholder for notary contract witness + last := len(w) - 1 + if !bytes.Equal(w[last].InvocationScript, p.dummyInvocationScript) || len(w[last].VerificationScript) != 0 { return errIncorrectNotaryPlaceholder } return nil } -func (p Preparator) validateAttributes(aa []transaction.Attribute, alphaKeys keys.PublicKeys) error { +func (p Preparator) validateAttributes(aa []transaction.Attribute, alphaKeys keys.PublicKeys, invokerWitness bool) error { // main tx must have exactly one attribute if len(aa) != 1 { return errIncorrectAttributesAmount } + expectedN := uint8(len(alphaKeys)) + if invokerWitness { + expectedN++ + } + val, ok := aa[0].Value.(*transaction.NotaryAssisted) - if !ok || val.NKeys != uint8(len(alphaKeys)) { + if !ok || val.NKeys != expectedN { return errIncorrectAttribute }