[#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 <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2021-11-16 18:51:15 +03:00 committed by Alex Vanin
parent 6f23dbfefe
commit a0ff5b1bf8
2 changed files with 71 additions and 22 deletions

View file

@ -441,7 +441,11 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
_, n := mn(alphabetList, committee) _, n := mn(alphabetList, committee)
u8n := uint8(n) u8n := uint8(n)
cosigners, err := c.notaryCosigners(alphabetList, committee) if !invokedByAlpha {
u8n++
}
cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee)
if err != nil { if err != nil {
return err return err
} }
@ -510,7 +514,7 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
err = c.client.AddNetworkFee( err = c.client.AddNetworkFee(
mainTx, mainTx,
notaryFee, notaryFee,
c.notaryAccounts(multiaddrAccount)..., c.notaryAccounts(invokedByAlpha, multiaddrAccount)...,
) )
if err != nil { if err != nil {
return err return err
@ -538,8 +542,8 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
return nil return nil
} }
func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) { func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) {
s := make([]transaction.Signer, 0, 3) s := make([]transaction.Signer, 0, 4)
// first we have proxy contract signature, as it will pay for the execution // first we have proxy contract signature, as it will pay for the execution
s = append(s, transaction.Signer{ s = append(s, transaction.Signer{
@ -563,6 +567,16 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa
AllowedGroups: c.signer.AllowedGroups, 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 // last one is a placeholder for notary contract signature
s = append(s, transaction.Signer{ s = append(s, transaction.Signer{
Account: c.notary.notary, Account: c.notary.notary,
@ -572,12 +586,12 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa
return s, nil 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 { if multiaddr == nil {
return 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 // first we have proxy account, as it will pay for the execution
a = append(a, &wallet.Account{ 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 // then we have inner ring multiaddress account
a = append(a, multiaddr) 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 // last one is a placeholder for notary contract account
a = append(a, &wallet.Account{ a = append(a, &wallet.Account{
Contract: &wallet.Contract{}, Contract: &wallet.Contract{},
@ -602,7 +621,7 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account,
return nil 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` // first we have empty proxy witness, because notary will execute `Verify`
// method on the proxy contract to check witness // method on the proxy contract to check witness
@ -639,6 +658,19 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account,
VerificationScript: multiaddr.GetVerificationScript(), 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 // last one is a placeholder for notary contract witness
w = append(w, transaction.Witness{ w = append(w, transaction.Witness{
InvocationScript: append( InvocationScript: append(

View file

@ -25,6 +25,7 @@ var (
errUnexpectedCosignersAmount = errors.New("received main tx has unexpected amount of cosigners") errUnexpectedCosignersAmount = errors.New("received main tx has unexpected amount of cosigners")
errIncorrectAlphabetSigner = errors.New("received main tx has incorrect Alphabet signer") errIncorrectAlphabetSigner = errors.New("received main tx has incorrect Alphabet signer")
errIncorrectProxyWitnesses = errors.New("received main tx has non-empty Proxy witnesses") 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") errIncorrectAlphabet = errors.New("received main tx has incorrect Alphabet verification")
errIncorrectNotaryPlaceholder = errors.New("received main tx has incorrect Notary contract placeholder") errIncorrectNotaryPlaceholder = errors.New("received main tx has incorrect Notary contract placeholder")
errIncorrectAttributesAmount = errors.New("received main tx has incorrect attributes amount") 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. // since every notary call is a new notary request in fact.
func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) { func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) {
// notary request's main tx is expected to have // notary request's main tx is expected to have
// exactly three witnesses: one for proxy contract, // three or four witnesses: one for proxy contract,
// one for notary's invoker and one is for notary // one for alphabet multisignature, one optional for
// contract // notary's invoker and one is for notary contract
if len(nr.MainTransaction.Scripts) != 3 { ln := len(nr.MainTransaction.Scripts)
switch ln {
case 3, 4:
default:
return nil, errUnexpectedWitnessAmount return nil, errUnexpectedWitnessAmount
} }
invokerWitness := ln == 4
// alphabet node should handle only notary requests // alphabet node should handle only notary requests
// that have been sent unsigned(by storage nodes) => // 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) 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 { if err != nil {
return nil, err return nil, err
} }
// validate main TX's notary attribute // validate main TX's notary attribute
err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet) err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet, invokerWitness)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// validate main TX's witnesses // validate main TX's witnesses
err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet) err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet, invokerWitness)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -303,8 +308,8 @@ func (p Preparator) validateExpiration(fbTX *transaction.Transaction) error {
return nil return nil
} }
func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.PublicKeys) error { func (p Preparator) validateCosigners(expected int, s []transaction.Signer, alphaKeys keys.PublicKeys) error {
if len(s) != 3 { if len(s) != expected {
return errUnexpectedCosignersAmount return errUnexpectedCosignersAmount
} }
@ -320,7 +325,7 @@ func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.Pub
return nil 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 // the first one(proxy contract) must have empty
// witnesses // witnesses
if len(w[0].VerificationScript)+len(w[0].InvocationScript) != 0 { 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 return errIncorrectAlphabet
} }
// the third one must be a placeholder for notary if invokerWitness {
// contract witness // the optional third one must be an invoker witness
if !bytes.Equal(w[2].InvocationScript, p.dummyInvocationScript) || len(w[2].VerificationScript) != 0 { 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 errIncorrectNotaryPlaceholder
} }
return nil 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 // main tx must have exactly one attribute
if len(aa) != 1 { if len(aa) != 1 {
return errIncorrectAttributesAmount return errIncorrectAttributesAmount
} }
expectedN := uint8(len(alphaKeys))
if invokerWitness {
expectedN++
}
val, ok := aa[0].Value.(*transaction.NotaryAssisted) val, ok := aa[0].Value.(*transaction.NotaryAssisted)
if !ok || val.NKeys != uint8(len(alphaKeys)) { if !ok || val.NKeys != expectedN {
return errIncorrectAttribute return errIncorrectAttribute
} }